I'm also having the same problems.

As Jeremie said, as soon as you start introducing shared methods (via 
synchronized for example), you rapidly get into trouble that can only be 
overcome by excessive casting.

It may be possible to contain the problem by refactoring multi-threaded 
code so that the shared objects are very small and simple, but even then 
the casting required is too much. This approach might be ok if you could 
define classes as being shared or immutable, and ALL instance of them 
were then implicitly shared or immutable. Also, immutable objects should 
be implicitly shareable.


On Sat, 12 Sep 2009 15:32:05 -0400, Jason House wrote:

> I'm glad to see I'm not the only one trying to use shared. I tried to
> use it with 2.031 and rapidly hit bug after bug... I submitted several
> bug reports for basic functionality, and none of it appeared in the
> changelog.
> 
> http://d.puremagic.com/issues/show_bug.cgi?id=3089
> http://d.puremagic.com/issues/show_bug.cgi?id=3090
> http://d.puremagic.com/issues/show_bug.cgi?id=3091
> 
> 
> 
> Jeremie Pelletier Wrote:
> 
>> I decided to play once again with shared and see what 2.032 is capable
>> of. Turns out a lot of the previous issues I was having last time are
>> gone, however, there are still a few things left which prevent me from
>> rewriting my code.
>> 
>> The first issue that jumped to my face straight away was how 'shared
>> const' methods are not callable from 'shared' objects.
>> 
>> shared class Foo {
>>      void bar() const;
>> }
>> auto foo = new Foo; // foo is of type shared(Foo) foo.bar; //  Error:
>> function Foo.bar () shared const is not callable using argument types
>> () shared
>> 
>> Considering how 'const' methods can be called from mutable objects,
>> this looks like either a bug or a really awkward feature to me. Sending
>> a shared(Foo) to a method expecting a shared(const(Foo)) also triggers
>> a similar error from the compiler.
>> 
>> The other issue may be an intended feature, but it doesn't sound
>> practical to me. Marking a method as shared assumes all used properties
>> in the method's scope are also shared. Here is an example to illustrate
>> my point:
>> 
>> class SimpleReader {
>>    this(LocalFile file) { _stream = new FileInputStream(file); } ...
>> private:
>>     synchronized void read(ubyte[] buf, long offset) {
>>         _stream.seek(offset);
>>         _stream.read(buf);
>>     }
>>     FileInputStream _stream;
>> }
>> 
>> The FileInputStream here is a generic blocking binary stream which is
>> not thread-safe by design. The reader is a composite class where every
>> instance has its own unique stream instance and use it to implement
>> asynchronous reads over the file format it abstracts, which in my case
>> is a specialized read-only archive using a lot of random accesses from
>> different threads.
>> 
>> This is where the issue shows its ugly head. The 'synchronized' keyword
>> tags the read method as shared, which in itself is quite neat, what is
>> annoying however is that it also changes the type of _stream in the
>> method's scope to shared(FileInputStream) and therefore triggers
>> compiler errors because _stream.seek and _stream.read are not shared:
>> 
>> Error: function FileInputStream.read (ubyte[]) is not callable using
>> argument types (ubyte[]) shared
>> 
>> While it may be an attempt to keep shared usage safe, it isn't very
>> practical. The stream object here is not shared because it is not
>> thread-safe. While it may be used by different threads, it is unique to
>> the reader's context and its accesses are synchronized by the reader,
>> the stream should therefore be completely oblivious to the fact it is
>> being used by different threads.
>> 
>> Maybe this could be the time to implement an unique qualifier; this is
>> a context where having _stream be of type unique(FileInputStream) would
>> solve the problem and allow further compiler optimizations. I don't
>> know if it can be done with templates, and without any overhead
>> whatsoever. I know I would much rather see unique(Foo) than Unique!Foo,
>> and it would allow the use of 'is(foo : unique)'.
>> 
>> Furthermore, tagging a method with shared does not make it thread-safe,
>> it may however use synchronized within its scope to protect its shared
>> or unique data. This may be confusing when calling shared methods vs
>> calling synchronized methods; one may think the shared one is not
>> thread-safe and optionally synchronize the call, resulting in another
>> monitor being used for nothing, or no monitor being used at all:
>> 
>> class Foo {
>>     shared void bar() {
>>         // Do stuff with local or immutable data synchronized(this) {
>>         /* do stuff with shared data */ }
>>     }
>>     shared void bar2() {
>>         // Do stuff on shared data
>>     }
>> }
>> 
>> Someone seeing only the prototype of Foo.bar may assume the method is
>> not thread-safe and call it as 'synchronized(foo) foo.bar()'. Just like
>> they could see the prototype of bar2 and assume it is thread-safe,
>> calling it as 'foo.bar2()'.
>> 
>> What could be a good design against this sort of misleading behavior?
>> 
>> Phew, that's about enough issues and questions for now :)

Reply via email to