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