Boost logo

Boost :

From: Christopher Kohlhoff (chris_at_[hidden])
Date: 2005-12-15 06:56:29


Wow, what a thread. Rather than replying to everything
individually, I'm going to try to address the issues in this one
email.

--- Arkadiy Vertleyb <vertleyb_at_[hidden]> wrote:
> My problem with the current approach is that synchronous
> operations seems to be viewed as second-class citizens.

On the contrary, asio attempts to elevate asynchronous
operations to the same level as their synchronous counterparts.
For virtually every blocking synchronous operation in asio,
there is an asynchronous equivalent. The intent is to improve
ease of use for asynchronous operations to be as close as
possible to synchronous.

Far from synchronous operations being given second class status,
considerable effort has gone into ensuring that the composed
operations (such as asio::[async_]read and asio::[async_]write)
have the same semantics for both synchronous and asynchronous
calls. Further levels of abstraction should also be achievable
in a similar vein.

Let me state as clearly as possible: the synchronous operations
are not implemented in terms of the asynchronous operations.
They call the underlying synchronous system call with little
overhead.

The question was raised over whether a synchronous-only sockets
API should be provided, and asio implemented in terms of that. I
rejected this approach early on in the development of asio. As
others have already noted, such an API is not sufficient for the
development of asynchronous operations, and you are forced to
step outside of it to use operating system-specific
functionality. Instead, asio is itself intended as a thin,
portable API that provides both synchronous and asynchronous
support.

So, the nub of your issue appears to be the conceptual
difficulties you perceive in having the socket constructor take
a demuxer, and for the interface to include both synchronous and
asynchronous operations.

Let's consider the alternative approach proposed: to split the
socket class into separate sync_socket and async_socket classes.
The drawbacks of this approach are increased redundancy and
complexity in the programming interface. If this approach were
already in place, I can imagine that I would be getting
questions about why there are so many socket classes, and which
one is the most appropriate to use.

There are trade-offs in every design decision. I elected to give
synchronous and asynchronous operations equal weight, and
combine them in a single interface. This has the added advantage
of allowing mixed-mode programming, where synchronous operations
can be used alongside asynchronous in a single application. As
you point out, there are situations where synchronous is more
appropriate, and these differences can occur within an
application.

I don't think that the need to pass a demuxer to the socket
constructor is conceptually difficult to explain. It's there for
the asynchronous operations, if and when you need them.

I also do not agree that it is onerous to have to pass the
demuxer to the constructor, or to pass it wherever it's needed
in the program. You can make it a global, declare a new one for
each use, or even use one associated with an existing object, as
in:

  stream_socket s(my_acceptor.demuxer());

Therefore I stand by my design choices as the best balance
between usability, functionality and flexibility.

Now, to turn to more philosophical matters, namely synchronous
versus asynchronous approaches.

As you pointed out, synchronous socket programming is easier to
debug. However, there are also disadvantages, notably increased
resource usage (with a thread required per connection) reducing
scalability, and the need for explicit synchronisation between
threads. Correct programming in the presence of threads is a
difficult art, and I see this as a major disadvantage of the
synchronous approach.

Asynchronous socket programming, on the other hand, has the
advantage of allowing concurrent operations without
synchronisation. Programs can be written as though they exist in
a single-threaded environment, and still scale to handle
numerous connections.

The main disadvantage of the asynchronous approach is program
complexity, since operation initiation is separate from
completion, and the flow of control is inverted. However I have
designed asio to mitigate these as much as possible,
particularly in providing the ability to combine asynchronous
operations to allow the implementation of higher-level
abstractions.

When people first start network programming, it is natural for
them to gravitate towards synchronous operations. They may then
progress to writing more complex applications, such as servers,
that must handle multiple connections. If all they have been
exposed to is synchronous network programming, then it is likely
they will end up with a design that requires many threads.

By making both synchronous and asynchronous operations readily
available, asio may inform programmers that there are
alternatives to multithreading. If this helps them to make a
more appropriate choice for their application, then that has got
to be a good thing.

Cheers,
Chris


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