"// FIXME: this is illegal (accessing GC memory)"

This could indeed be a reason for the observed behavior. I've pushed a fix that uses a vibe.utils.hashmap.HashMap together with a manual memory allocator to fix this.

More importantly though, I'm left with what to do about it... I'd happily use the Win32 driver but I need this ultimately to run on Linux too and would prefer to test in both places with the same driver. (Aside: Win32 driver is still missing one thing that I need as well, namely the ability to get the remote address of a connection.) I briefly looked at Libev on Linux but even once I got that compiling it spit a bunch of warnings about unimplemented stuff in vibe at me so I got scared off :) Not sure the status or plans for that implementation.

Yeah, I want to get the libev driver to a complete and stable state at some point to hopefully get better performance than with the libevent driver, but I'm simply missing the time for that (the priority is very low, as it is basically only a performance improvement).

I'll implement the Win32TCPConnection.peerAddress property later today.

Then I started thinking about how to avoid ManualEvent entirely... I can obviously do some sort of mutex and acquire/release on the TCPConnection itself for sending but that would block fibers that trigger sends to a client who is misbehaving, which is undesirable. I was trying to think whether there was perhaps a way to make the regular case work like this and then "detect" badness and kill the offending client, but it gets a bit messy.

Another possibility would now be to use vibe.core.concurrency to send and receive messages and then start two tasks for each connection, one for reading and one for writing - I've just implemented read/write separation for TCP connections after a recent discussion with Vladimir Panteleev, which makes this possible. setMaxMailboxSize can be used to determine what happens if a certain connection stacks up too many messages. I also started a generic MessageHub class today, but I can't say when exactly I'm going to get to finish it.

But to just get it working quickly, hopefully the ManualEvent fix was the right one for this issue...