First steps of splitting io_data_t

First steps towards #484.

By using shared_ptr to manage io_data_t and avoid copying io_data_t, better destruction semantics is achieved. `io_buffer_destroy` will be converted into a virtual destructor a bit later (after splitting io_buffer_t, of course).