Thread Attributes

Jonathan Marler via Digitalmars-d digitalmars-d at puremagic.com
Mon Jul 14 14:26:22 PDT 2014


On Sunday, 13 July 2014 at 10:45:29 UTC, Jacob Carlborg wrote:
> This would be implemented as a declaration macro [1], something 
> like this:
>
> macro thread (Context, Ast!(string) name, Declaration decl)
> {
>     if (decl.isVariable)
>         decl.attributes ~= Thread(name);
>
>     else if (decl.isCallable)
>     {
>         foreach (var ; decl.accessedVariables)
>         {
>             if (auto attr = var.getAttribute!(Thread))
>                 if (attr.name != name)
>                     context.compiler.error("Cannot access 
> variable with thread name " ~ attr.name ~ " from callable with 
> thread name " ~ name);
>         }
>     }
>
>     return decl;
> }
>
> Usage:
>
> @thread("main") int mainThreadGlobal;
> @thread("worker") void workerLoop ();
>

Ah I see now.  It looks like AST macros are going to open up alot 
of new paradigms, I'm excited to see how they progress and what 
they can do.  It doesn't get us all the way there in this example 
but its a very good alternative without having to add anything to 
the compiler. Your macro would help the developer ensure that 
particular variables and functions are only touched by the 
appropriate code.  If the feature only did this I would use a 
different name such as "restrict" or something.  However, in 
order for the compiler to perform thread-optimizations based on 
this information it would have to be apart of the language.

It also doesn't handle the synchronized case. For that you would 
need a knowledge of the execution paths of your functions to 
determine what parts are locked and what parts are not (Unless of 
course AST macros could support that, I would be very pleasently 
surprised if they did).

This AST macro is very intriguing though.  Its like you're 
writing code that can analyze your code as you develop it.  This 
is a really cool idea.

As I've had more time to think on this here's one of the 
potential consequence I've thought of.

*********** Deprecating usage of the __gshared hack ***********

Here's some cases that a developer may think __gshared usage is 
justified.
   1. In a single threaded application
   2. When the developer knows that its only possible for one 
thread to access that global at a time or,
   3. When the developer is using some type of locking scheme to 
access the globals is the correct design.

In a single threaded application then you could add the thread 
attribute with the same name to every single function and 
variable, but this would be very unnecessary. Instead the 
developer could use a pragme to tell the compiler to make sure it 
is single threaded, but since adding this feature would require 
the compiler to know when threads are started anyway, the 
compiler could determine that an application was single threaded 
on its own.  The pragma would just be a sanity check so that the 
developer would be notified when their code has changed to break 
the initial design.

In the second case, if you know that only one thread will ever 
access the global variable(s) then you may be willing to take the 
risk of making the variable(s) __gshared and just remember to 
make sure you don't break your own rule by using it on another 
thread.  This feature would allow the compiler to verify this at 
compile time taking pressure off the developer.
		
In the third case, the developer has designed the code to use the 
global(s) so long as they lock on the appropriate object first.  
This is a huge risk because the safety of the code is up to the 
developer remembering to check that every access of the global(s) 
has locked on the appropriate object.  Adding the sync(object) 
attribute would allow the compiler to verify this at compile time 
for the developer, and the compiler would again have no need to 
make the globals thread local.

There's one more odd corner case I thought of.  Suppose you know 
that only one thread will access the global data but this thread 
could change over the course of the program's life (impossible to 
know at compile time which thread it is).  In this case you would 
use the sync(object) design pattern and just have the appropriate 
thread lock on the object whenever it is started.  Instead of 
locking before every access you would just lock the object as 
soon as the thread is started and unlock it when the thread dies. 
  It would also be beneficial if the thread could throw an 
exception if the object is already locked on.  This is a 
different kind of way to used locked objects, instead of using 
them to synchronize small accesses to shared data, it is used to 
create a "slot" so that only one thread can be in the slot at a 
time.

***********************************************************************************


More information about the Digitalmars-d mailing list