I ended up staring at the PrimOps.cmm file today, and porting tryPutMVar to C so it can be used from the RTS.
During my staring, it occurred to me that it should be possible to implement a wait-on-multiple MVars with mostly no overhead. I am guessing that this would be desirable, but I am not sure.
-- wait for all MVars, return the value of one of them, and the corresponding index
typedef struct StgMVarTSOQueue_ {
StgHeader header;
struct StgMVarTSOQueue_ *link;
// The group_head and group_link are used when a TSO is waiting on
// multiple MVars. Multiple StgMVarTSOQueue objects
// are created, one for each MVar, and they are linked using the
// group_link pointer. group_head always points to the first
// element. The tso pointer will always be the same in this case.
// The group head acts as a synchronization element for the whole group.
// When the group head lock is acquired, the owner can invalidate the group
struct StgMVarTSOQueue *group_head;
struct StgMVarTSOQueue *group_link;
struct StgTSO_ *tso;
} StgMVarTSOQueue;
Now, whenever a TSO should be woken up by the MVar code, if
... putMVar(...) {
...
queue = mvar->queue...
loop:
if (end_of_queue(queue) {
return 0;
}
if (marked_as_invalid(queue) { // invalid = (queue->group_head == NULL) for example
queue = queue->link;
goto loop;
}
if (queue->group_head != queue) {
// Try to win race to wake up the TSO with *our* MVar.
won = TryLock(queue->group_head);
if (!won) {
// ignore this queue element for now, we can't get the lock,
// and it will be marked_as_invalid at some point in the future anyways.
queue = queue->link;
goto loop; // try next
}
if (won) {
for all in group, mark them as "invalid"
Unlock(queue->group_head);
goto wake_up_tso;
} else {
// Did not win
goto loop;
}
} else {
..what is currently done..
wake_up_tso:
... do wake up
Alexander