Boost logo

Boost :

From: Matt Vogt (mattvogt_at_[hidden])
Date: 2005-12-27 18:44:49


Christopher Kohlhoff <chris <at> kohlhoff.com> writes:

> But we've still got the problem of composed operations. Perhaps
> what you want could be achieved by wrapping the socket in an
> application class that automatically created a new handler
> function object with the appropriate error handling. E.g.:
>
> template <typename Stream>
> class stream_wrapper
> {
> public:
> stream_wrapper(..., function<void(const error&)> f);
> ...
> template <typename Const_Buffers, typename Handler>
> void async_read_some(const Const_Buffers& bufs, Handler h)
> {
> stream_.async_read_some(bufs, split_error(f_, h));
> }
> ...
> };

Yes, that's not a bad idea. Of course, you don't want to rewrite all the
forwarding functions with any regularity, so it's probably better to do
this with a Handler object, constructed with an error-handling function
and then supplied with the success-handling function for each new operation.

Is split_error documented somewhere?

> > Well, that's subtle. I'm not sure if it's a good idea or not.
>
> This approach removes the need for explicit resource management
> (i.e. closing the socket) which I think is, on balance, a good
> thing.

Yes, I guess it is, once enable_shared_from_this is in your working
vocabulary.

> Let's assume that the socket implementation contained a boolean
> data member indicating whether EOF had been reached. Firstly,
> this member would have to be updated upon completion of an
> operation. There's no guarantee that this will occur in any
> particular thread if multiple threads call demuxer::run, so some
> synchronisation (and associated cost) would be required.
> Secondly there is no guarantee that the original socket object
> still exists at the time the completion handler is delivered.

Well, that sounds like an insurmountable problem :)

> For the synchronous operations you can create your own error
> handler object to add the required context, e.g.:
>
> socket.connect(endpoint, throw_my_error("connect"));

Perhaps it's been discussed, but is there any particular reason you wouldn't
want to define an 'asio::socket_error' subclass of asio::error? Attaching
the relevant shared_ptr<socket> to the error code would make some error-
handling strategies simpler.

> > If it were a pair of error-code and socket, and could be used
> > asynchronously, it would be useful, but propagating the
> > exception outside demuxer::run seems messy, since multiple
> > threads could be calling 'run'.
>
> In respect of exceptions and demuxer::run, I hope I have defined
> the behaviour clearly. Specifically, the demuxer cannot know
> about all exception types, so exceptions are allowed to safely
> propagate to outside the run call where they can be handled.
> This only affects the thread where the exception is raised.
> After handling the exception, that thread may immediately call
> demuxer::run again to return to the pool.

Sorry if I'm missing the point, but I don't see what your saying here.
If there are some exceptions that you cannot deal with, why should that
prevent you from handling the ones you do know about? If users supply
handlers that throw, they will need to deal with the resulting exceptions
manually. I don't see how anything is simplified by requiring the user to
deal with library exceptions in the same place (the call to demuxer::run)...

> Asio has something vaguely similar in its stream layers. For
> example the ssl::stream template can be layered over a stream
> socket like so:
>
> typedef ssl::stream<stream_socket> ssl_stream_socket;
>
> Maybe this can be extended to support more complex composition
> scenarios.

Yes, that seems plausible.

Matt


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