- The library seems to mix synchronous operations (connect) and asynchronous ones (read/write). Stick to one, everything else doesn't make sense.
- It isn't documented on which thread callbacks are actually executed. It can't be a user-visible eventloop since the user never starts one. So it's probably an eventloop on a background thread. Based on the -DIO_SERVICE_NB_WORKERS=1 variable it might even be multiple background threads. From my experience the majority of users won't expect that and will inevitably run into ugly synchronization problems. Even for those who expect it proper synchronization will be hard to add if operations start and finish on different threads (and sometimes the same threads). I would recommend to stay at a pure singlethreaded eventloop model if asynchronous APIs are the design goal. Even in super-mature libraries like boost asio a mixed use of multithreaded and async operations causes headaches and is far more likely to show implementation bugs.
- Things like read don't accept a buffer as input into which the read should happen but only a size. The buffer is allocated by the library. This is convenient as far as it doesn't force the caller to guarantee that the buffer outlives the operation. But it will cause a lot of memory churn, which is in my experience of the most typical reasons of performance degradation.
- const std::list<std::shared_ptr<tacopie::tcp_client> >& tacopie::tcp_server::get_clients() const : Returning a reference to a non-synchronized list in a multithreaded environment seems inevitably broken.