Thread overview
Various shared bugs
Dec 08, 2009
Jason House
Dec 09, 2009
Graham St Jack
Dec 24, 2009
Jason House
December 08, 2009
So, after months of avoiding shared, I decided to see if I could remove all my casting away of shared.  It looks like things are still pretty buggy (or at least not particularly easy to use).

"is(T : shared)" gives a parse error
"alias shared T U" silently does the wrong thing.
   (Use "alias shared(T) U" instead)
"xxxx is not callable using argument types ()" is just awful.  Typically, it
means you're calling a non-shared function with a shared type or a shared
function with a non-shared type.  I hit into a case where I got that message
with a shared type and a shared method, but I have mad no attempt to
reproduce.
The error "Can not implicitly convert expression of type(this) of type
shared(xxx) to full.name.xxx" can pop up when defining shared this()
constructor gives no line number where the offending usage occurs.

I've backed out most of my pro-shared changes and will try again in a few months :(
December 09, 2009
On Mon, 07 Dec 2009 22:45:17 -0500, Jason House wrote:

> So, after months of avoiding shared, I decided to see if I could remove all my casting away of shared.  It looks like things are still pretty buggy (or at least not particularly easy to use).
> 
> "is(T : shared)" gives a parse error
> "alias shared T U" silently does the wrong thing.
>    (Use "alias shared(T) U" instead)
> "xxxx is not callable using argument types ()" is just awful.
> Typically, it means you're calling a non-shared function with a shared
> type or a shared function with a non-shared type.  I hit into a case
> where I got that message with a shared type and a shared method, but I
> have mad no attempt to reproduce.
> The error "Can not implicitly convert expression of type(this) of type
> shared(xxx) to full.name.xxx" can pop up when defining shared this()
> constructor gives no line number where the offending usage occurs.
> 
> I've backed out most of my pro-shared changes and will try again in a few months :(

I have also given up on shared and am also adopting a waiting strategy.

I would love to get some tips from anyone (like Walter, for example) who thinks they have a way of using shared successfully.

My recent post on the subject got no meaningful responses - just one from bearophile that was supportive, but alas not helpful.
December 24, 2009
Graham St Jack wrote:

> On Mon, 07 Dec 2009 22:45:17 -0500, Jason House wrote:
>> I've backed out most of my pro-shared changes and will try again in a few months :(
> 
> I have also given up on shared and am also adopting a waiting strategy.

Yeah, it's sad.  I have been successful in converting part of my code base to use shared, and suspect I can probably get another chunk working if I try really hard.  I partially suspect that the viral nature of shared makes any conversion of a code base tough.  It may be much easier to get something working if shared is used from the beginning while developing.


> I would love to get some tips from anyone (like Walter, for example) who thinks they have a way of using shared successfully.

Here's the one module that I was able to completely convert to use shared (and converted all uses of it to use shared).  Maybe it'd help you figure out how to get things to work?  It's a relatively brain dead message queue. It can't hold more than one message at a time (adding a second message will block until the first message has been received by all threads).  It's intended for one master thread to send a message to all recipients in a thread group.

module hb.io.ipc;

import std.cstream;
import core.thread;
import tango.core.Atomic;

template broadcastMessageQueue(target, double sleepSec=0.1){
	private enum int sleepTicks = cast(int) (sleepSec*100_000_000);
	/// Broadcasts a delegate to a bunch of identical recipients
	class sender{
		alias void delegate(target) messageType;
		private int id;
		private int max;
		private int pending;
		private messageType msg;
		this(int numberOfRecipients){ max = numberOfRecipients; }
		/// Only blocks if queue is full
		void send(shared messageType message) shared{
			waitForQueueToEmpty;
			id++;
			msg = message;
			pending = max;
		}
		/// Blocks until every recipient got the message
		void push(shared messageType message) shared{
			send(message);
			waitForQueueToEmpty;
		}
		private bool receive(int messageId, target t) shared{
			if (pending == 0 || id < messageId)
				return false;
			msg(t);
			atomicDecrement!(msync.raw)(pending);
			return true;
		}
		private void waitForQueueToEmpty() shared{
			while(pending > 0)
				Thread.sleep(sleepTicks);
		}
	}

	/// Receives delegates from the specified sender. Never blocks.
	class receiver{
		private target parent;
		private shared sender source;
		private int nextMessageId = 1;
		this(target t, shared sender s){ parent = t; source = s; }
		bool receive(){
			// Cast is hack to circumvent bugzilla issue #3089
			if (source.receive(nextMessageId, parent)){
				nextMessageId++;
				return true;
			}
			return false;
		}
	}
}

version(test)
unittest{
	derr.writefln("Testing broadcast message queue");
	class dummy{ int x; }
	auto foo = new dummy;
	auto bar = new dummy;
	// Extra parenthesis as hack to circumvent dmd bugzilla issue #3091
	auto sender = new shared(broadcastMessageQueue!(dummy).sender)(2);
	auto rx1 = new broadcastMessageQueue!(dummy).receiver(foo, sender);
	auto rx2 = new broadcastMessageQueue!(dummy).receiver(bar, sender);

	assert(rx1.receive == false);
	assert(rx2.receive == false);

	sender.send( cast(shared void delegate(dummy)) (dummy d){d.x++;});

	assert(rx1.receive);
	assert(rx2.receive);

	assert(rx1.receive == false);
	assert(rx2.receive == false);


	assert(foo.x == 1);
	assert(bar.x == 1);
}