Something needs to happen with shared, and soon.

Johannes Pfau nospam at example.com
Mon Nov 12 02:57:18 PST 2012


Am Sun, 11 Nov 2012 18:30:17 -0800
schrieb Walter Bright <newshound2 at digitalmars.com>:

> 
> To make a shared type work in an algorithm, you have to:
> 
> 1. ensure single threaded access by aquiring a mutex
> 2. cast away shared
> 3. operate on the data
> 4. cast back to shared
> 5. release the mutex
> 
> Also, all op= need to be disabled for shared types.

But there are also shared member functions and they're kind of annoying
right now:

* You can't call shared methods from non-shared methods or vice versa.
  This leads to code duplication, you basically have to implement
  everything twice:

----------
struct ABC
{
        Mutext mutex;
	void a()
	{
		aImpl();
	}
	shared void a()
	{
		synchronized(mutex)
		    aImpl();  //not allowed
	}
	private void aImpl()
	{
		
	}
}
----------
The only way to avoid this is casting away shared in the shared a
method, but that really is annoying.

* You can't have data members be included only for the shared version.
  In the above example, the mutex member will always be included, even
  if ABC instance is thread local.

So you're often better off writing a non-thread safe struct and writing
a wrapper struct. This way you don't have useless overhead in the
non-thread safe implementation. But the nice instance syntax is
lost:

shared(ABC) abc1; ABC abc2;
vs
SharedABC abc1; ABC abc2; 

even worse, shared propagation won't work this way;

struct DEF
{
    ABC abc;
}
shared(DEF) def;
def.abc.a();



and then there's also the druntime issue: core.sync doesn't work with
shared which leads to this schizophrenic situation:
struct A
{
    Mutex m;
    void a() //Doesn't compile with shared
    {
        m.lock();  //Compiles, but locks on a TLS mutex!
        m.unlock();
    }
}

struct A
{
    shared Mutex m;
    shared void a()
    {
        m.lock();  //Doesn't compile
        (cast(Mutex)m).unlock(); //Ugly
    }
}

So the only useful solution avoids using shared:
struct A
{
    __gshared Mutex m; //Good we have __gshared!
    shared void a()
    {
        m.lock();
        m.unlock();
    }
}



And then there are some open questions with advanced use cases:
* How do I make sure that a non-shared delegate is only accepted if I
  have an A, but a shared delegate should be supported
  for shared(A) and A? (calling a shared delegate from a non-shared
  function should work, right?)

struct A
{
    void a(T)(T v)
    {
        writeln("non-shared");
    }
    shared void a(T)(T v)  if (isShared!v) //isShared doesn't exist
    {
        writeln("shared");
    }
}

And having fun with this little example:
http://dpaste.dzfl.pl/7f6a4ad2

* What's the difference between: "void delegate() shared"
  and "shared(void delegate())"?

Error: cannot implicitly convert expression (&a.abc) of type void
delegate() shared to shared(void delegate())

* So let's call it void delegate() shared instead:
void incrementA(void delegate() shared del)
/home/c684/c922.d(7): Error: const/immutable/shared/inout attributes
  are only valid for non-static member functions



More information about the Digitalmars-d mailing list