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 :)