Let me see if I understand this right... let's say I have some (unshared) class that launches threads to do its real work in.

class Foo {
  this () {thread = spawn (&work);}
  shared void work () {...};
  void send_message (T) (T msg) {thread.send (msg);}
  Tid thread;
}

It has an unshared method to pass messages to the worker thread, and everything is copacetic, but what is the best practice for when I want another thread to interact with one of these instances?

class Bar {
  this () {thread = spawn (&pass);}
  shared void pass () {
    receive (
       (shared Foo F) {this.F = F;}                  // (1)
       (T msg) {(cast(Foo)F).send_message (msg);},   // (2)
    );
  void send_message (T) (T msg) {thread.send (msg);}
  Tid thread;
  Foo F;
}

void main () {
  auto F = new Foo;
  auto B = new Bar;
  B.send_message (cast(shared)F);                    // (3)
  // sleep
}

When I interact with F from at (1), this.F is automatically shared (because Bar is shared) so this compiles. Here, this.F refers to the same F I would refer to if I accessed this.F from the main method, it just gets automatically shared whenever Bar is shared. So when I spawn a worker thread, the worker thread sees a shared Bar (and shared F), while the main thread sees a Bar that it owns, but they are both looking at the same Bar (just with different access rules, like a const and non-const reference to the same object). At (2), I have to cast F to unshared in order to send it a message. But this is ok, because I am only sending messages, and am not risking a race condition. Basically I am announcing "I know that multiple threads can see this, but I am taking ownership of it and taking responsiblity for preventing races." And when I cast to shared at (3), I am announcing "I am explicitly recognizing that multiple threads may now access this, and that there is a potential for races."

Is this the right way to handle this situation?

And on a more general note, the shared keyword, as I understand it, is intended to be a sort of compiler-reinforced tag that I can use to prevent races the same way const helps prevent unintended side-effects, and to aid my reasoning when I do need to debug a race?

Reply via email to