shared adventures in the realm of thread-safety.
Robert Jacques
sandford at jhu.edu
Sun Sep 13 13:31:03 PDT 2009
On Sun, 13 Sep 2009 15:04:57 -0400, Jeremie Pelletier <jeremiep at gmail.com>
wrote:
[snip]
> Unique data could only be used for aggregate properties, const/immutable
> data would also be implicitly unique. This qualifier alone would
> simplify shared quite a lot, allowing the use of unshared objects in
> shared contexts safely.
Neither const nor immutable data can be considered unique. First, any
const data may be being mutated by another routine, so it can't be safely
accessed without synchronization. Second, unique data is mutable while
const/immutable data is not. Third, most implementations of unique allow
for deterministic memory reclamation, which isn't possible if the unique
data might actually be const/immutable.
> The compiler should make the distinction between shared code and shared
> data and allow both shared and unshared instances to use shared methods,
> just like both const and mutable instances may call const methods. An
> error should also be triggered when calling a shared method of a shared
> object without synchronization, and maybe have a __sync keyword to
> override this. If a synchronized method is called from a non-shared
> object, no synchronization takes place.
I think you have the wrong paradigm in mind. Shared and non-shared aren't
mutable and const. They're mutable and immutable. From a technical
perspective, synchronization of shared methods are handled by the callee,
so there is no way not to call them and non-shared objects don't have a
monitor that can be synchronized. Now you can have the compiler use the
same code to generate two different object types (vtables, object layouts,
etc) with have the same interface, but that doesn't sound like what you're
suggesting.
> Allow me to illustrate my point with some code:
>
> class Foo {
> int bar() shared { return a; }
> __sync bar2() { synchronized(this) return a; }
> synchronized void foo() { a = 1; }
> int a;
> }
> auto foo1 = new shared(Foo)();
> auto foo2 = new Foo;
>
> foo1.foo(); // ok, synchronized call
> synchronized(foo1) foo1.foo(); // warning: recursive synchronization
Why a warning? Monitors are designed to handle recursive synchronization.
> foo2.foo(); // ok, unsynchronized call
> synchronized(foo2) foo2.foo(); // ok synchronized call
>
> foo1.bar(); // error, unsynchronized call to bar() shared
> synchronized(foo1) foo1.bar(); // ok, synchronized call
> foo2.bar(); // ok, unsynchronized call
> synchronized(foo1) foo1.bar(); // ok, synchronized call
>
> foo1.bar2(); // ok, method handles synchronized
> synchronized(foo1) foo1.bar2(); // warning, recursive synchronization
> foo2.bar2(); // ok, method handles synchronized, even on unshared object
> synchronized(foo2) foo2.bar2(); // warning, recursive synchronization,
> even on unshared object
>
> That's about it, I believe this behavior would allow quite a number of
> multi-threaded techniques to be easily implemented and documented. It
> would only be the most natural thing since its quite similar to how
> const works.
The major benefit of const isn't method declaration, but object use: i.e.
only having to declare func(const T var) and not func(immutable T var) and
func(T var). Currently, there's no planned type to fill this role though
there have been some proposals.
P.S. Shouldn't 'a' be either private or protected?
P.S.S. Bartosz Milewski has a good series of blogs on multi-threading
(with an eye on how to do it well in D).
Bike-shed: I've always preferred the CSP/pi-calculas term 'mobile' for the
concept of 'unique'. I think mobile better expresses the concept with
regard to multi-threading, where mobile is used to cheaply transfer data
between threads (i.e. it moves around/can move between threads, but isn't
shared between them). I find 'unique' to mainly convey the memory storage
aspect of the concept, which is less important outside of C/C++.
More information about the Digitalmars-d
mailing list