Thread overview
Static constructors?
Jul 22, 2010
awishformore
Jul 22, 2010
Sean Kelly
Jul 22, 2010
awishformore
Jul 22, 2010
Dave
Jul 22, 2010
awishformore
Jul 23, 2010
Dave
Jul 23, 2010
Dave
July 22, 2010
I have a question to static constructors in D2 and with threads.

-LogManager.d-

import std.stdio;

final class LogManager
{
	private:
		static __gshared LogManager instance;
		
	public:
	
		static this()
		{
			instance = new LogManager();
			writefln("New LogManager instance created.");
		}
	
		static ~this()
		{
			instance = null;
		}

		static LogManager opCall()
		{
			return instance;
		}
}

-end-

As stated in the docs, the static constructor is supposed to be executed before main. However, over the course of my application, every time I use the LogManager in a new thread, the static constructor will be executed again. Afaik this means that I have one static instance per thread, which is not at all which I would expect, especially considering the fact that instance is declared as __gshared (fyi: shared doesn't make a difference either).

Is this a bug? If not, could someone explain why the behaviour is different?

Thanks, Max.
July 22, 2010
Make the ctors "shared static this()" -- those are only constructed once when the process starts up.  The non-shared static ctors are thread-local.
July 22, 2010
On 22/07/2010 03:36, Sean Kelly wrote:
> Make the ctors "shared static this()" -- those are only constructed once when the process starts up.  The non-shared static ctors are thread-local.

That concept is really weird, though. So this applies to anything static, not just variables?
July 22, 2010
On 7/21/2010 7:27 PM, awishformore wrote:
> On 22/07/2010 03:36, Sean Kelly wrote:
>> Make the ctors "shared static this()" -- those are only constructed
>> once when the process starts up. The non-shared static ctors are
>> thread-local.
>
> That concept is really weird, though. So this applies to anything
> static, not just variables?

I had the same issue, I believe this pretty much explains it all: http://www.digitalmars.com/d/2.0/migrate-to-shared.html

July 22, 2010
On 22/07/2010 07:54, Dave wrote:
> On 7/21/2010 7:27 PM, awishformore wrote:
>> On 22/07/2010 03:36, Sean Kelly wrote:
>>> Make the ctors "shared static this()" -- those are only constructed
>>> once when the process starts up. The non-shared static ctors are
>>> thread-local.
>>
>> That concept is really weird, though. So this applies to anything
>> static, not just variables?
>
> I had the same issue, I believe this pretty much explains it all:
> http://www.digitalmars.com/d/2.0/migrate-to-shared.html
>

I read that page about a million times already, and it pretty much explains nothing. Where does this page even mention shared functions, structs, classes or objects?

The same goes for TDPL by Andrei. It's a bit more elaborate, but it still doesn't even get close to explaining shared in detail.

/Max
July 22, 2010
On Wed, 21 Jul 2010 22:27:10 -0400, awishformore <awishformore@nospam.plz> wrote:

> On 22/07/2010 03:36, Sean Kelly wrote:
>> Make the ctors "shared static this()" -- those are only constructed once when the process starts up.  The non-shared static ctors are thread-local.
>
> That concept is really weird, though. So this applies to anything static, not just variables?

The puzzle is, let's say you have thread local static variables and shared static variables.  Which should be initialized when?

The end result we came to is that there should be a way for thread-local variables to be initialized on thread creation, and shared variables to be initialized on program instantiation.

So we need a syntax for this.  Various things were suggested, and the current behavior was chosen for its consistency with current terminology:

shared int x;
shared static this() { x = 5; }

or:

shared
{
  int x;
  static this() { x = 5; }
}

int x2;
static this() { x2 = 5; }

It looks pretty good to me.  You just have to start thinking that thread-local is the norm :)

-Steve
July 23, 2010
On 7/22/2010 7:40 AM, Steven Schveighoffer wrote:
> On Wed, 21 Jul 2010 22:27:10 -0400, awishformore
> <awishformore@nospam.plz> wrote:
>
>> On 22/07/2010 03:36, Sean Kelly wrote:
>>> Make the ctors "shared static this()" -- those are only constructed
>>> once when the process starts up. The non-shared static ctors are
>>> thread-local.
>>
>> That concept is really weird, though. So this applies to anything
>> static, not just variables?
>
> The puzzle is, let's say you have thread local static variables and
> shared static variables. Which should be initialized when?
>
> The end result we came to is that there should be a way for thread-local
> variables to be initialized on thread creation, and shared variables to
> be initialized on program instantiation.
>
> So we need a syntax for this. Various things were suggested, and the
> current behavior was chosen for its consistency with current terminology:
>
> shared int x;
> shared static this() { x = 5; }
>
> or:
>
> shared
> {
> int x;
> static this() { x = 5; }
> }
>
> int x2;
> static this() { x2 = 5; }
>
> It looks pretty good to me. You just have to start thinking that
> thread-local is the norm :)
>
> -Steve

While we're on the topic, I've been using dcollections lib for some time now and came across a problem when implementing shared classes, this being an example:

import dcollections.ArrayList;

shared:

class Resource {
}

synchronized class ResourceManager {
    void add(Resource res) {
        bool added;
        resources.add(res, added);
    }

    void remove(Resource res) {
        foreach(ref doRemove, v; &resources.purge) {
            doRemove = v is res;
        }
    }

private:
    ArrayList!(Resource) resources;
}

I'm getting the following compile errors:

threadTest.d|13|Error: function dcollections.ArrayList.ArrayList!(shared(Resource)).ArrayList.add (shared(Resource) v, out bool wasAdded) is not callable using argument types (shared(Resource),bool) shared|
threadTest.d|13|Error: cannot implicitly convert expression (res) of type shared(Resource) to shared(Resource)[]|
threadTest.d|13|Error: cast(uint)added is not an lvalue|
threadTest.d|17|Error: function dcollections.ArrayList.ArrayList!(shared(Resource)).ArrayList.purge (scope int delegate(ref bool doPurge, ref shared(Resource) v) dg) is not callable using argument types (int delegate(ref bool doPurge, ref shared(Resource) v)) shared|
||=== Build finished: 4 errors, 0 warnings ===|

I'm not quite sure I understand the errors. Is there something I'm missing?
July 23, 2010
On Fri, 23 Jul 2010 05:56:18 -0400, Dave <ddionisio@renegadeware.com> wrote:

> On 7/22/2010 7:40 AM, Steven Schveighoffer wrote:
>> On Wed, 21 Jul 2010 22:27:10 -0400, awishformore
>> <awishformore@nospam.plz> wrote:
>>
>>> On 22/07/2010 03:36, Sean Kelly wrote:
>>>> Make the ctors "shared static this()" -- those are only constructed
>>>> once when the process starts up. The non-shared static ctors are
>>>> thread-local.
>>>
>>> That concept is really weird, though. So this applies to anything
>>> static, not just variables?
>>
>> The puzzle is, let's say you have thread local static variables and
>> shared static variables. Which should be initialized when?
>>
>> The end result we came to is that there should be a way for thread-local
>> variables to be initialized on thread creation, and shared variables to
>> be initialized on program instantiation.
>>
>> So we need a syntax for this. Various things were suggested, and the
>> current behavior was chosen for its consistency with current terminology:
>>
>> shared int x;
>> shared static this() { x = 5; }
>>
>> or:
>>
>> shared
>> {
>> int x;
>> static this() { x = 5; }
>> }
>>
>> int x2;
>> static this() { x2 = 5; }
>>
>> It looks pretty good to me. You just have to start thinking that
>> thread-local is the norm :)
>>
>> -Steve
>
> While we're on the topic, I've been using dcollections lib for some time now and came across a problem when implementing shared classes, this being an example:
>
> import dcollections.ArrayList;
>
> shared:
>
> class Resource {
> }
>
> synchronized class ResourceManager {
>      void add(Resource res) {
>          bool added;
>          resources.add(res, added);
>      }
>
>      void remove(Resource res) {
>          foreach(ref doRemove, v; &resources.purge) {
>              doRemove = v is res;
>          }
>      }
>
> private:
>      ArrayList!(Resource) resources;
> }
>
> I'm getting the following compile errors:
>
> threadTest.d|13|Error: function dcollections.ArrayList.ArrayList!(shared(Resource)).ArrayList.add (shared(Resource) v, out bool wasAdded) is not callable using argument types (shared(Resource),bool) shared|
> threadTest.d|13|Error: cannot implicitly convert expression (res) of type shared(Resource) to shared(Resource)[]|
> threadTest.d|13|Error: cast(uint)added is not an lvalue|
> threadTest.d|17|Error: function dcollections.ArrayList.ArrayList!(shared(Resource)).ArrayList.purge (scope int delegate(ref bool doPurge, ref shared(Resource) v) dg) is not callable using argument types (int delegate(ref bool doPurge, ref shared(Resource) v)) shared|
> ||=== Build finished: 4 errors, 0 warnings ===|
>
> I'm not quite sure I understand the errors. Is there something I'm missing?

Yes, ArrayList is not meant to be a shared class.  There are no shared methods on it.  The clue is here:

dcollections.ArrayList.ArrayList!(shared(Resource)).ArrayList.add
(shared(Resource) v, out bool wasAdded) is not callable using argument
types (shared(Resource),bool) >>>>>shared<<<<<

Emphasis added by me.

The problem here is that the compiler says everything must be shared transitively.  So even though you are locking the ResourceManager object, you have to treat its 'resources' member as if something outside the ResourceManager object also has a reference to it.

I'm not exactly sure how to solve this, without duplicating all methods and marking them shared.  You can try casting, it should be safe in this context since the array list is private.

Sean, Walter, Andrei?  What is the correct method to say "don't mark this member as shared because it's always protected by a lock"?  Or if that's impossible, what's the right way to solve this kind of problem?

Also, resources.add(res) should work I think (you don't need the added part).  The documentation is severely out of date, I apologize for that.

-Steve
July 23, 2010
On 7/23/2010 5:14 AM, Steven Schveighoffer wrote:
> On Fri, 23 Jul 2010 05:56:18 -0400, Dave <ddionisio@renegadeware.com>
> wrote:
>
>> On 7/22/2010 7:40 AM, Steven Schveighoffer wrote:
>>> On Wed, 21 Jul 2010 22:27:10 -0400, awishformore
>>> <awishformore@nospam.plz> wrote:
>>>
>>>> On 22/07/2010 03:36, Sean Kelly wrote:
>>>>> Make the ctors "shared static this()" -- those are only constructed
>>>>> once when the process starts up. The non-shared static ctors are
>>>>> thread-local.
>>>>
>>>> That concept is really weird, though. So this applies to anything
>>>> static, not just variables?
>>>
>>> The puzzle is, let's say you have thread local static variables and
>>> shared static variables. Which should be initialized when?
>>>
>>> The end result we came to is that there should be a way for thread-local
>>> variables to be initialized on thread creation, and shared variables to
>>> be initialized on program instantiation.
>>>
>>> So we need a syntax for this. Various things were suggested, and the
>>> current behavior was chosen for its consistency with current
>>> terminology:
>>>
>>> shared int x;
>>> shared static this() { x = 5; }
>>>
>>> or:
>>>
>>> shared
>>> {
>>> int x;
>>> static this() { x = 5; }
>>> }
>>>
>>> int x2;
>>> static this() { x2 = 5; }
>>>
>>> It looks pretty good to me. You just have to start thinking that
>>> thread-local is the norm :)
>>>
>>> -Steve
>>
>> While we're on the topic, I've been using dcollections lib for some
>> time now and came across a problem when implementing shared classes,
>> this being an example:
>>
>> import dcollections.ArrayList;
>>
>> shared:
>>
>> class Resource {
>> }
>>
>> synchronized class ResourceManager {
>> void add(Resource res) {
>> bool added;
>> resources.add(res, added);
>> }
>>
>> void remove(Resource res) {
>> foreach(ref doRemove, v; &resources.purge) {
>> doRemove = v is res;
>> }
>> }
>>
>> private:
>> ArrayList!(Resource) resources;
>> }
>>
>> I'm getting the following compile errors:
>>
>> threadTest.d|13|Error: function
>> dcollections.ArrayList.ArrayList!(shared(Resource)).ArrayList.add
>> (shared(Resource) v, out bool wasAdded) is not callable using argument
>> types (shared(Resource),bool) shared|
>> threadTest.d|13|Error: cannot implicitly convert expression (res) of
>> type shared(Resource) to shared(Resource)[]|
>> threadTest.d|13|Error: cast(uint)added is not an lvalue|
>> threadTest.d|17|Error: function
>> dcollections.ArrayList.ArrayList!(shared(Resource)).ArrayList.purge
>> (scope int delegate(ref bool doPurge, ref shared(Resource) v) dg) is
>> not callable using argument types (int delegate(ref bool doPurge, ref
>> shared(Resource) v)) shared|
>> ||=== Build finished: 4 errors, 0 warnings ===|
>>
>> I'm not quite sure I understand the errors. Is there something I'm
>> missing?
>
> Yes, ArrayList is not meant to be a shared class. There are no shared
> methods on it. The clue is here:
>
> dcollections.ArrayList.ArrayList!(shared(Resource)).ArrayList.add
> (shared(Resource) v, out bool wasAdded) is not callable using argument
> types (shared(Resource),bool) >>>>>shared<<<<<
>
> Emphasis added by me.
>
> The problem here is that the compiler says everything must be shared
> transitively. So even though you are locking the ResourceManager object,
> you have to treat its 'resources' member as if something outside the
> ResourceManager object also has a reference to it.
>
> I'm not exactly sure how to solve this, without duplicating all methods
> and marking them shared. You can try casting, it should be safe in this
> context since the array list is private.
>
> Sean, Walter, Andrei? What is the correct method to say "don't mark this
> member as shared because it's always protected by a lock"? Or if that's
> impossible, what's the right way to solve this kind of problem?
>
> Also, resources.add(res) should work I think (you don't need the added
> part). The documentation is severely out of date, I apologize for that.
>
> -Steve

Ah thanks for pointing that out, I misread the error. It does read better that way as it is calling the method as 'shared'.

I would have thought that instantiating a template with a shared class implies that all its methods will also be shared?

No worries about the doc, you did indicate it in your project. The ddoc generated with the current code seem to be correct so far...if not, the code is clear enough to understand the methods.

I was mucking around with it more and came up with this:

module threadTest;

import dcollections.ArrayList;

class Resource {
}

synchronized class ResourceManager {

    void add(Resource res) {
            (cast(ArrayList!(Resource))resources).add(res);
    }

    void remove(Resource res) {
        ArrayList!(Resource) reses = cast(ArrayList!(Resource))resources;

        foreach(ref doRemove, v; &reses.purge) {
            doRemove = v is res;
        }
    }

private:
    ArrayList!(Resource) resources;
}

Seems to compile, I'd have to test it at runtime and actual application to see if it is feasible. I suppose it's safe to uncast a shared variable so long as it is localized within the methods.