
Hi Giovanni, --- "Giovanni P. Deretta" <gpderetta@gmail.com> wrote:
As i said, i see, in this case, move as a correctness issue than an optimization.
Can you explain what you mean by correctness? Today I was pondering move (and swap) as a general design idea, and I suspect that a good rule of thumb might be that they are appropriate on value-type classes that have "heavy" implementations, and particularly where copying may throw. They do not seem appropriate operations for a non-value-type class which has identity, since to me the address of the object is part of its identity. I do believe the usual motivation for move is as a performance optimisation, to construct the target object with minimum expense, e.g. see: http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1377.htm If it is not possible to implement move efficiently, then I think it should not be there at all, otherwise it could lead to unpleasant surprises.
I do not see how you can avoid locking if you are going for intrusive refcounting. For example, thread A creates a stream_socket and register it in a demuxer. The demuxer might be shared between threads so it needs to lock the list mutex to insert it in the queue. Then thread A might destroy the socket at any time, so it need to relock the queue to remove the socket. Am i missing something?
But this locking is currently only needed when the socket object is constructed or destroyed, which are not usually common operations. Moving is something that could occur quite a lot.
I have (*) one use case that would really benefit if asio moved its callback instead of copying it. As the callback owns the socket, it would need the socket to be movable, or at least swappable. And no, i do not think it is a wouldn't be just "a cool thing"... it is obvious from asio examples that support for moving (with support from bind of course :) ) an auto pointer would be obviate the need of a shared_ptr.
The callbacks are an example of why I think a move operation would have limited application. Move support can only be used if there is just one chain of asynchronous operations associated with the object. As soon as there is a second chain (e.g. running an async_wait on a timer while you do an async_read, or doing an async_read and async_write at the same time) the move support becomes useless.
I do not see how and why locking a mutex could trow? Boost::thread mutexes do not throw.
I see that it does not, but perhaps it should :) On Windows 2000 or earlier the EnterCriticalSection function can fail in low memory situations. This is not handled at all in the boost::mutex class as far as I can see.
Btw, you could use a lock free list (not that i know how to use it :)).
I would categorise this as the "difficult" bit of "difficult and inefficient" :) Seriously though, the more I think about move the more problems and limitations I see it creating for the implementation. Some more examples: - An implementation that stores state direct in the socket object may have pointers to that state in other data structures. All these would need to be tracked and updated if a move occurred. - The data structures used by epoll and kqueue can contain a pointer to user data. For efficiency, this could (and probably should) point directly to the state that is embedded in the socket object. This pointer can only be updated via a system call. This discussion has actually been quite helpful for me in confirming why the sockets should not support move and swap... Sorry :)
Sure, but the highest socket value seen by a demuxer is not very high (in the order of MAX_OPEN on sane systems).
Hmmm, I dunno. I have used asio in servers that support more than 50000 concurrent connections. That's potentially a lot of wasted space! Cheers, Chris