
I wrote my Chan around the abstraction:
data Chan a = Chan (MVar (Either [a] [MVar a]))
The Chan either has elements in it (Left), or has readers waiting for elements (Right). To get the fairness properties on Chan you might want to make these two lists Queue's, but I think the basic principle still works. By using this abstraction my Chan was a lot simpler. With this scheme implementing isEmpyChan or unGetChan would both work nicely. My Chan was not designed for performance. (In truth I replaced the Left with IntMap a, and inserted elements with a randomly chosen key, but the basic idea is the same.)
I like the idea. But what happens if one of the blocked threads gets killed by a killThread (e.g. a timeout) while it is waiting? Won't we still give it an element of the Chan sometime in the future? Perhaps this doesn't happen in your scenario, but it seems to throw a spanner in the works for using this as a general-purpose implementation.
I hadn't thought of that at all - my scenario doesn't have any threads being killed. With the thought of threads dying concurrency abstractions become significantly harder - I hadn't quite realised how hard that must make it. Thanks, Neil