
Hi Ion, Just some quick comments about the memory allocations: --- Ion GaztaƱaga <igaztanaga@gmail.com> wrote: <snip>
Good overall. I've following the "Daytime.3 - An asynchronous TCP daytime server" example with the Visual 7.1 debugger and specially looking for mechanisms that hurt embedded systems. I've found some interesting things about memory allocations. I've commented the code with the number of calls to "operator new": <snip annotated code>.
I made a few quick changes and re-ran the daytime3 server to observe the memory allocations. What I changed was: - Created a class static member to be returned by the null() function. Returning a new object each time was a bit of a pessimisation, I admit :) - Explicitly initialised the services socket_acceptor_service and stream_socket_service. Normally these are initialised on the first use of a socket_acceptor or stream_socket respectively. I made this change to the example to make it clearer what the per-object cost is. - Only initialise the reactor implementation on the first use of asynchronous connect emulation (it's not used in this program). ------------------------------ int main() { try { //--> 4 calls to new. asio::demuxer demuxer; //--> 4 calls to new. demuxer.get_service(asio::service_factory< asio::socket_acceptor_service<> >()); //--> 2 calls to new. demuxer.get_service(asio::service_factory< asio::stream_socket_service<> >()); //--> 2 calls to new associated with socket in shared_ptr. asio::socket_acceptor acceptor(demuxer, asio::ipv4::tcp::endpoint(13)); //--> 1 call to new (the explicit new shown here). asio::stream_socket* socket = new asio::stream_socket(demuxer); //--> 1 call to new for OVERLAPPED-derived object. acceptor.async_accept(*socket, boost::bind(handle_accept, &acceptor, socket, asio::placeholders::error)); //--> 2 calls to new when accepting new connection and // underlying socket in shared_ptr is created. demuxer.run(); } catch (asio::error& e) { std::cerr << e << std::endl; } return 0; } void handle_accept(asio::socket_acceptor* acceptor, asio::stream_socket* socket, const asio::error& error) { if (!error) { using namespace std; // For time_t, time, ctime, strdup and strlen. time_t now = time(0); char* write_buf = strdup(ctime(&now)); size_t write_length = strlen(write_buf); //--> 1 call to new for OVERLAPPED-derived object. asio::async_write(*socket, asio::buffer(write_buf, write_length), boost::bind(handle_write, socket, write_buf, asio::placeholders::error, asio::placeholders::bytes_transferred)); //--> 1 call to new (the explicit new shown here). socket = new asio::stream_socket(acceptor->demuxer()); //--> 1 call to new for OVERLAPPED-derived object. acceptor->async_accept(*socket, boost::bind(handle_accept, acceptor, socket, asio::placeholders::error)); } else { delete socket; } } void handle_write(asio::stream_socket* socket, char* write_buf, const asio::error& /*error*/, size_t /*bytes_transferred*/) { using namespace std; // For free. free(write_buf); //--> 0 calls to new. delete socket; } ------------------------------ The shared_ptr wrapping the socket is a workaround for Windows not behaving as MSDN says it should with respect to operation cancellation. I'd be *very* happy to find another efficient way to correctly indicate operation cancellation without requiring the allocation. The intention was that the Allocator template parameter be used for custom memory allocation, much as it is in the standard library. However the current implementation doesn't use it. Christopher Baus has said that he is looking at another way to allow customisable allocation, and I look forward to seeing his proposal. Cheers, Chris