The readIO function is similar to read except that it
signals parse failure to the IO monad instead of terminating
the program.
This operation may fail with:
Read the value of an IORef.
Beware that the CPU executing a thread can reorder reads or writes to
independent locations. See Data.IORef#memmodel for more
details.
Atomically read the contents of the IOPort. If the
IOPort is currently empty, readIOPort will wait until it
is full. After a readIOPort, the IOPort is left empty.
There is one important property of readIOPort:
Only a single threads can be blocked on an IOPort.