Refactor to use dispatch_io for tasks, and wait for EOF

Hopefully resolves #139, #135, #161.

/cc @joshaber @keithduncan
I’m trying to trace though the code paths to find where we might duplicate `close()` the same FD, this can cause issues as the same integer slot can reference a new stream in the kernel which a duplicate `close` would operate on.