module Tanker

Because Ruby only has synchronous streams, we can't read them on Tanker's thread (in the read callback). To work around that, we start a new Thread for each read operation. This is not that bad because Ruby uses a thread pool behind the scenes and creating a Thread is usually just a work pushed on a queue.

Also, there doesn't seem to easily create an IO object. The simplest way is to create a pipe. A pipe is a stream where you read everything you write into it. Tanker streams however are pull streams, so there must be something that pulls from the Tanker stream and pushes onto the IO stream. We start a long-running thread for that which just loops.

Because so much is happening in parallel, synchronization is not trivial. There's one mutex per stream, and we lock that same mutex for every critical section. Here are some important constraints that must be held:

called

been called

stream. Though it is ok to close the stream after the call, but before the future is resolved.

Constants

ASSERT_UTF8