
Christopher Kohlhoff wrote:
Hi Giovanni,
--- "Giovanni P. Deretta" <gpderetta@gmail.com> wrote: <snip>
A nice solution would be to make socket movable...
Yes, this would be nice, but unfortunately I see problems reconciling this with some other changes arising from the review that I want to make :(
Basically, I want the public interface to allow an implementation that stores state directly in the socket object. Some of the advantages of doing this include:
- The ability to eliminate per-socket memory allocations. Coupled with the custom handler allocation support, you could write programs with no ongoing memory allocations occurring inside asio.
I think this is very important and I'm all in favor of it. But I do not see how making the socket movable excludes this point. A movable socket would have an extra bit saying if the state is valid or not. When it is "moved" the receving socket gets a copy of the state. The valid bit on the source is turned off while the same bit is turned on in the receiving socket (*). I expect that the internal state of the socket is not big enough that copying is a bottle-neck. Moving is thus not a performance optimization, but a correctess operation. The source object is no longer valid (can only be destoryed) and the destination socket becomes the true handle for the device
- Improved performance, e.g. by eliminating many of the hash table lookups to find the list of operations associated with a descriptor.
Some of this "state" might be that the socket object is part of an intrusive linked list or hash map. This would make implementing movability, at best, difficult and inefficient.
I do not see how this would be inefficient. Suppose that a socket is : struct socket { socket* prev; socket* next; bool valid; /* more state here */ void swap(socket& from) { prev = to.prev; next = to.next; prev->next = this; next->prev = this; /* swap state */ } } ; void move(socket& from, socket& to) { socket().swap(to); from.swap(to) } This requires the socket to be default constructible (or else do a socket(to.demuxer()).swap(to)) and a swap function in socket (i thought that asio::socket already had it, but looking at the docs i see i was wrong). Yes this is thread unsafe. To make it thread safe, you need to lock the list (or at least the objects involved) to protect the swap. But you need a mutex anyway because the object in the intrusive list could be destroyed at any time. Ah, btw, at least on posix systems, file descriptors are guaranteed to be allocated contiguously. So you could create a vector as big as the hightest fd, store the list of operations in it and use the socket_impl as a key for an O(1) lookup in the vector. (*) Btw, you do not really need an extra bit of course. A socket_impl equal to null_socket would identify an invalid socket (as is the case right now). -- Giovanni P. Deretta