
[asio] demuxer and services One thing the documentation does not really make clear is the relationship between the demuxer and the services. I think the documentation should probably try to clarify their general relationship. Possibly most of this is implementation detail, but a rough grasp of it might help use the library effectively in large systems, and would probably be required when thinking about customizing the library. Some of the structural objections raised in the review might be more manageable if it were clearer how customization might work. One particular point of confusion, I suspect, is the demuxer's demuxer_service. I think the library (or maybe just the naming and the doc) hasn't quite made up it's mind as to whether the demuxer is just a collection of services or actually provides a demultiplexing service, or both: On the one hand, TCP, timer and other services are "pay as you go" - they are added at run-time as they are used, and from the demuxer's point of view they are just members in a collection of services. Additionally, the demuxer itself performs almost no work (right?), but instead forwards member function calls to services. On the other hand, demuxer always creates a "demuxer_service" that exists in the service collection as a regular service, but also as a "distinguished" service in that the demuxer has a direct pointer to it and the demuxer forwards its member functions to that pointer. This demuxer_service has real responsibility: it implements asynchronous callbacks. So demuxer has 2 responsibilities: 1) collect and provide access to services. 2) create and provide access to the asynchronous callback service. ...correct, more or less? Chris, I can spend some hours on doc in the next couple weeks if that's helpful. I'm not sure I know enough about asio or networking to actually write the doc but I'm happy to try or to review or edit. Just let me know. Here's an attempt to describe the relationship between the demuxer and the services. --------------------------------------- demuxer: demuxer is the entry point of the asio library. A demuxer object is required to use asio. A reference to a demuxer object is passed to all asio functions that perform io. The demuxer class itself does very little. Instead demuxer "forwards" requests to its collection of "services". The demuxer always creates a "demuxer_service" service to implement asynchronous callbacks. Other services are created at run-time when they are first needed. Service collection + creation 1) demuxer's collection of services is a linked list, with one entry of each requested service type. 2) A service can be requested through demuxer::get_service<ServiceType>, which will find an existing service by type or add one if it is not found. 3) Asio code that needs a service automatically calls demuxer::get_service<TheServiceTypeINeed>, finding or adding the service to the demuxer. The user does not need to explicitly call get_service. 3.1) Incidentally, this sounds sort of like a list of boost::any, right?. The list and the "any" feature are implemented by hand for efficiency? demuxer's demuxer_service 4) demuxer constructor creates a demuxer_service service. 5) demuxer_service creates a platform-implementation demuxer_service (right?) 6) The role of the demuxer_service is to implement asynchronous callbacks (right?) 7) demuxer forwards its member functions to demuxer_service. 8) Although a loop is required in which to do the callbacks, the demuxer_service does not start a thread for that loop. Instead, user is given full control over this loop and must call demuxer::run() to execute it. 9) The demuxer_service is a "distinguished" service: it exists in the list of services as a service that can be retrieved with demuxer::get_service, but demuxer also has a direct pointer to it and uses it in a special way. 10) Any demuxer has two services after construction: the demuxer_service and the platform-implementation of the demuxer_service. 11) On Windows XP, the demuxer_service creates a win_iocp_demuxer_service, which allocates a windows IoCompletionPort (but does not start any threads). --------------------------------------- Services: Services factor functionality provided by demuxer into more manageable pieces. (right?) They isolate code and allow resources to be loaded at run-time when they are needed. 1) High-level services "forward" functionality to lower-level services, selected by platform. (right?) 2) Low-level and high-level services exist together in demuxer's linked list, and can all be retrieved with get_service<ServiceType>. 3) Services can use each other. Multiple high-level services can use the same low-level service, and services can use "cousin" services. 4) Actual user-level objects such as timers and sockets use a service, and it is probably through the instantiation of these objects that the service is created in the first place. 5) In general, services maintain a pointer to their "implementation" service, the demuxer maintains a pointer to the demuxer_service, and user-level objects (e.g. deadline_time and stream_socket) maintain a pointer to their service. Some services also have a pointer "up" to the demuxer, or "across" to another service. (Dunno if I happened to see any service pointing up to another service, but don't see any reason it couldn't happen). --------------------------------------- Services example: 4) Example of a Windows program using: *asynchronous timer callbacks, *synchronous socket i/o, *one demuxer shared between timer and socket code. In this example, the demuxer is created first (it has to be), then a stream_socket is created, then an async_timer is created, finally demuxer::run is called. 4.1) demuxer::demuxer() creates: demuxer_service creates: win_iocp_demuxer_service The demuxer now has 2 services: demuxer_service win_iocp_demuxer_service No additional threads are running. The win_iocp_demuxer_service has allocated a Windows IoCompletionPort. 4.2) stream_socket::stream_socket(demuxer) creates: stream_socket_service creates: win_iocp_socket_service creates: select_reactor<true> (a service) The demuxer now has 5 services: demuxer_service win_iocp_demuxer_service stream_socket_service win_iocp_socket_service select_reactor<true> The select_reactor service has started a thread. The behavior of this thread depends on whether any async winsock calls are made (right?) If none are made the thread will call Sleep(till next timer expires); otherwise it will call select(with timeout when next timer expires). Synchronous socket calls can now be made. They do no use the select_reactor thread (right?). 4.3) deadline_timer::deadline_timer(demuxer) creates: deadline_timer_service creates: reactive_deadline_timer_service finds already existing: select_reactor<true> The demuxer now has 7 services: demuxer_service win_iocp_demuxer_service stream_socket_service win_iocp_socket_service select_reactor<true> deadline_timer_service reactive_deadline_timer_service No additional threads are running. However, in order to use asynchronous timer callbacks, demuxer::run must be called in some thread. 4.4) demuxer::run() is called in WinMain. There are now two threads running: select_reactor thread executes select_reactor which loops on Sleep(till next timer expires) main thread executes demuxer::run which loops on GetQueuedCompletionStatus and executes timer completion handlers.