
Hi Arkadiy, --- Arkadiy Vertleyb <vertleyb@hotmail.com> wrote:
Could you provide some (high-level) justification of why it is beneficial to have a socket dependency upon a demuxer?
Maybe I am missing something, but IMO such a dependency is unnatural even for asynchronous IO.
This has not come up in the discussion thus far, but the dependency on the demuxer is required to enable an efficient and portable implementation of asynchronous I/O. This is one of the primary goals of asio. The asio interface has been designed to reflect efficient asynchronous I/O mechanisms across major operating systems. Let's take the specific case of Windows, where asynchronous I/O is implemented using I/O completion ports. Once a socket is associated with an I/O completion port it cannot be disassociated. In asio, the demuxer represents the I/O completion port. The asio interface enforces this association by constructing the socket with its associated demuxer/IO-completion-port.
From the point of view of the library user, any asynchronous operations started on the socket will have the completion handler delivered through the associated demuxer (as it is with I/O completion ports).
Separating the asynchronous I/O operations from the socket class would no longer enforce this requirement at compile time. (That's even leaving aside questions about where the operation logically belongs.)
Hiding it behind default parameter, singleton, etc., just masks what I believe is a design problem.
If you absolutely believe this dependency is necessary for async IO, having two separate classes, such as sync_socket and async_socket would be much cleaner. But again, I can't see what justifies such a dependency even for async IO.
So the question is really whether there should be separate sync and async socket classes, versus a combined class as there is now. I believe that, on balance, separate classes would be harmful. Let's reiterate some of the points: - Synchronous and asynchronous operations are essentially the same, in that they perform the same operation and aim to fulfil the same contract. How they differ is simply that one blocks the current thread, whereas the other executes in a background logical thread and tells you when it is complete. I want there to be a clear mapping from one to the other. - Separate classes reinforces the notion that synchronous operations are somehow more useful for the ordinary programmer, and that asynchronous is hard and best left in the realm of the networking guru. I do not believe that asynchronicity is difficult to use or understand, it is simply that programmers often approach sockets with preconceived ideas of how an API should behave. But I do believe that separating the classes will continue the lack of awareness of asynchronicity as an option, leading to the needless use of inferior designs. - The greater number of classes and options will increase the size of the library's interface. - I have demonstrated that there is a use case for mixed-mode applications. Predominantly synchronous designs can benefit from localised asynchronicity. Asynchronous designs can be simplified by selective use of synchronous calls. - A line of reasoning has been presented which says that, because files already have a synchronous-only interface and could be given an asynchronous interface, sockets should also have a synchronous-only interface. However, sockets are inherently different to files due to the long timescales involved in many operations. Developers will look for some form of concurrency to address this, and I believe asynchronicity *should* be promoted over threads as a solution. It is worth noting that even .NET combines the sync and async socket operations on a single interface. I do find that there is a compelling case for a synchronous-only interface to sockets at the iostreams level. But that is because of the established use and wide applicability of iostreams, not because it is synchronous. So let me turn the question around. Why must there be a low-level synchronous-only interface at all? What benefits will it bring? What use cases do you have that cannot be met by a combined interface or an iostream interface? Cheers, Chris