Boost logo

Boost :

From: Andrew Schweitzer (a.schweitzer.grps_at_[hidden])
Date: 2005-12-27 14:29:53


[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.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk