
I have reviewed ASIO by browsing some of the documentation and working through the tutorial. What follows are my comments on the library. Regarding the Tutorial: Some of the tutorial material seems too low-level with respect to the facilities available from the C++ Standard Library. Why the use of char arrays, strdup, and strlen? Furthermore, is strdup a standard C++ function? I do not think it is standard C. I would like to see an interface for reading/writing that supports iterators if that makes sense. It would also be nice to be able to read from a socket into an std::string. If that is possible, then it is not obvious from the tutorial. Based on the daytime tutorial, I am concerned that not enough high-level functionality has been added to the library. The daytime examples still have the flavor of using unix sockets. I think that this low-level functionality is critical to flexibility (and perhaps performance), but I would like to be able to quickly construct simple network applications as well. Just for some perspective, here's a tcp daytime server written in the TCL scripting language: -----<snip>----- #!/bin/sh # Standard Trick \ exec tclsh $0 ${1+"$@"} proc serveMe {handle addr port} { puts $handle [clock format [clock seconds]] close $handle } set sk [socket -server serveMe 13] vwait forever -----<snip>----- and here's a tcp datetime client: -----<snip>----- #!/bin/sh # Standard Trick \ exec tclsh $0 ${1+"$@"} if { $argc != 1} { puts stderr "client <host>" exit 1 } set sk [socket [lindex $argv 0] 13] puts -nonewline [read $sk] -----<snip>----- I would like to see an interface at this coarse-grain a level of functionality as well (using the analogous Modern C++ idioms of course). I did not find the Timer 5 example (Using boost threads) to be very compelling. Granted the examples are meant to give a flavor of the library interface, but it may be more compelling to defer multi-threading to the daytime server, perhaps forking a new thread on each connection. This would probably more closely resemble a real use of the facility. I suspect that something more would need to be done to demonstrate synchronization. I am also curious if the library could provide synchronous networking (which asio supports contrary to the library's name) through an iostreams-like interface as well. Demuxer: It appears that any application that uses asio must have one and only one demuxer object floating around. If that is the case, then perhaps it would be wise to model demuxer after std::cout: a global object that is implicitly constructed and destructed and available to any translation unit that includes the relevant header. Demuxer appears to have a more general use than merely i/o. In fact it appears that demuxer could be generally applicable to other asynchronous event handling that might occur in a C++ application (Graphical User Interface come immediately to mind). Existing libraries for GUI's have their own event loop systems. Two issues to consider are: 1) Might future libraries that require asynchrony piggyback off of ASIO's demultiplexing facilitites (For example a C++ GUI library, etc). 2) How can one interleave asio's demultiplexing facilities with other existing event-based libraries. I'm afraid I have not had time to investigate whether and how this can be done in asio currently. But some applications will require mixing asio into some other master event loop. Something of like demuxer::run_one_event() would be a good start. Documentation: The reference documentation seems to be quite good. I do not see documentation for the default class types (such as demuxer) written without reference to the underlying policies involved. It would be very helpful to basic users of the library to have higher-level documentation available in addition to the detailed lower-level documentation. There appear to be some gaps in the documentation on first view (No documentation of the Demuxer_Service concept (modeled by demuxer_service). Code Comments: It was necessary for me to make the following changes to the library in order to prevent warnings and to compile under OS X (Panther/10.3): OS X 10.3 does not define EV_OOBAND. To compile the programs, I had to supply a definition to kqueue_reactor.hpp: #define EV_OOBAND EV_FLAG1 It is not clear to me that this is the proper fix. Furthermore, it was necessary for me to supply some casts in socket_ops.hpp to prevent warnings: recv_bufs[i].iov_len = static_cast<int>(b[i].size); recv_bufs[i].iov_base = static_cast<caddr_t>(b[i].data); ... send_bufs[i].iov_len = static_cast<int>(b[i].size); send_bufs[i].iov_base = static_cast<caddr_t>(b[i].data); Cheers, ron

Hi Ron, --- Ronald Garcia <garcia@cs.indiana.edu> wrote:
Some of the tutorial material seems too low-level with respect to the facilities available from the C++ Standard Library. Why the use of char arrays, strdup, and strlen? Furthermore, is strdup a standard C++ function? I do not think it is standard C.
Yep, good point. I'll change the tutorials to use things like std::vector or std::string.
I would like to see an interface for reading/writing that supports iterators if that makes sense.
I'm not sure if it does make sense, as the socket operations must operate on contiguous sequences of bytes, which is not something that iterators enforce. However, it might be feasible to support iterators on containers of POD types. For example, an overload of buffer: template <typename Iterator> unspecified buffer(Iterator begin, Iterator end); which could be used: std::list<pod_type> data; ... sock.write(asio::buffer(data.begin(), data.end())); The main problem with this approach is that it might mask inefficiencies: - The number of elements in the returned list of buffers cannot be determined until runtime, and so the return type of the buffer() overload would have to be something like std::vector or std::list. - The number of elements might be very large, and so would require multiple underlying read/write system calls to transfer. - The size of pod_type might be small, so it might be more costly to copy the list of buffers than to simply copy the POD values into contiguous storage (e.g. a vector) first. - For implementations where std::vector<>::iterator is not a pointer it probably wouldn't be possible to optimise to use the already contiguous data in a single buffer. I'm interested to know if you or others still see direct iterator support as a useful thing to have, in light of the above. If it is widely useful I'd be happy to add it.
It would also be nice to be able to read from a socket into an std::string. If that is possible, then it is not obvious from the tutorial.
The problem with std::string is that it doesn't give you write access to an underlying buffer (as say std::vector does). This means that a read into a string is always going to involve a separate read into some other buffer first, and then a copy of the data into the string. That's not to say read support for std::string shouldn't be added, but it would have to be as a new function (e.g. read_string/async_read_string) rather than hooking into the existing read functions.
Based on the daytime tutorial, I am concerned that not enough high-level functionality has been added to the library. The daytime examples still have the flavor of using unix sockets. I think that this low-level functionality is critical to flexibility (and perhaps performance), but I would like to be able to quickly construct simple network applications as well.
I'd like to see something like this layered on top of asio as a separate framework. There are many different styles and approaches to take, so it may actually be multiple frameworks :)
I did not find the Timer 5 example (Using boost threads) to be very compelling. Granted the examples are meant to give a flavor of the library interface, but it may be more compelling to defer multi-threading to the daytime server, perhaps forking a new thread on each connection. This would probably more closely resemble a real use of the facility. I suspect that something more would need to be done to demonstrate synchronization.
Fair enough, I'll have a think about a better example to demonstrate multithreading.
I am also curious if the library could provide synchronous networking (which asio supports contrary to the library's name) through an iostreams-like interface as well.
Yep, see the included iostreams example. I plan to tidy the interface up a bit and make this part of the library itself.
Demuxer: It appears that any application that uses asio must have one and only one demuxer object floating around. If that is the case, then perhaps it would be wise to model demuxer after std::cout: a global object that is implicitly constructed and destructed and available to any translation unit that includes the relevant header.
Actually no, you can have multiple demuxers in a single program. This can be used, for example, to partition a program's I/O functionality into multiple single-threaded components. <snip>
2) How can one interleave asio's demultiplexing facilities with other existing event-based libraries. I'm afraid I have not had time to investigate whether and how this can be done in asio currently. But some applications will require mixing asio into some other master event loop. Something of like demuxer::run_one_event() would be a good start.
Based on other feedback, I already plan to add demuxer::run() overloads that take a timeout indicating the maximum time that they are allowed to execute. <snip>
Code Comments:
It was necessary for me to make the following changes to the library in order to prevent warnings and to compile under OS X (Panther/10.3): <snip>
Unfortunately there are other issues with support for 10.3, such as the lack of thread-safe DNS functions. Cheers, Chris

Christopher Kohlhoff wrote:
The problem with std::string is that it doesn't give you write access to an underlying buffer (as say std::vector does). This means that a read into a string is always going to involve a separate read into some other buffer first, and then a copy of the data into the string.
The other problem is that reading characters from sockets directly into a std::string (which uses the platform character set and encoding) is inherently unportable. Even if the incoming encoding is ASCII, the platform might still use EBCDIC. Far more likely is that incoming is UTF-8 and local ISO-8859-1 or Windows-1252 (typical Linux and Win32 platforms), which would still cause problems on German umlauts and similar characters. read_string would therefore have to supply a recoding function to safely import characters. Sebastian Redl

To be clear, I vote YES to accept asio into Boost. On Dec 27, 2005, at 10:30 PM, Christopher Kohlhoff wrote:
Based on the daytime tutorial, I am concerned that not enough high-level functionality has been added to the library. The daytime examples still have the flavor of using unix sockets. I think that this low-level functionality is critical to flexibility (and perhaps performance), but I would like to be able to quickly construct simple network applications as well.
I'd like to see something like this layered on top of asio as a separate framework. There are many different styles and approaches to take, so it may actually be multiple frameworks :)
I think that the above reply relates to many of my concerns regarding the library. A strong emphasis has been placed upon efficiency and functionality for this library. Perhaps some of the functionality that I mentioned (support for iterators and strings) could be added to asio or deferred to a higher level interface. I am not sure where the line should be drawn with respect to these pieces of functionality. Furthermore, they only seem additive upon the design expressed in asio as is. It is with that in mind that I vote YES to accept asio into Boost. I do hope, however, that higher-level interfaces to the functionality in asio are forthcoming (and I hope to have some time soon to look for the iostreams example).
<snip>
2) How can one interleave asio's demultiplexing facilities with other existing event-based libraries. I'm afraid I have not had time to investigate whether and how this can be done in asio currently. But some applications will require mixing asio into some other master event loop. Something of like demuxer::run_one_event() would be a good start.
Based on other feedback, I already plan to add demuxer::run() overloads that take a timeout indicating the maximum time that they are allowed to execute.
This sounds promising. The interface I had in mind above would block until one asynchronous event were handled (or return immediately if none are pending). I am not sure if this is a worthwhile to have in addition to the hard timer.
<snip>
Code Comments:
It was necessary for me to make the following changes to the library in order to prevent warnings and to compile under OS X (Panther/10.3): <snip>
Unfortunately there are other issues with support for 10.3, such as the lack of thread-safe DNS functions.
Can 10.3 still support much of asio, or is the above a killer? Cheers, ron

Hi Ron, --- Ronald Garcia <garcia@cs.indiana.edu> wrote: <snip>
Unfortunately there are other issues with support for 10.3, such as the lack of thread-safe DNS functions.
Can 10.3 still support much of asio, or is the above a killer?
Witk the kqueue workaround you made (something that really requires more investigation) I believe it will work OK, provided you don't call DNS functions from multiple threads, don't do more than one asynchronous host_resolver call at a time, and don't mix synchronous and asynchronous host_resolver calls. Actually come to think of it, I'm not sure if the DNS thread safety problem is in 10.3. I know it was in 10.1 and 10.2, and is not in 10.4. Can you have a look at the man page for gethostbyname and see if it says anything about thread safety, possibly under "BUGS"? If necessary, this limitation can also be worked around for 10.3 by adding a wrapper around all DNS calls that uses a mutex to ensure that only one call to the underlying functions is made at a time. Cheers, Chris

Hi Chris,
Actually come to think of it, I'm not sure if the DNS thread safety problem is in 10.3. I know it was in 10.1 and 10.2, and is not in 10.4. Can you have a look at the man page for gethostbyname and see if it says anything about thread safety, possibly under "BUGS"?
Here is the text you asked about: BUGS These functions use static data storage; if the data is needed for future use, it should be copied before any subsequent calls overwrite it. Only the Internet address format is currently understood.
If necessary, this limitation can also be worked around for 10.3 by adding a wrapper around all DNS calls that uses a mutex to ensure that only one call to the underlying functions is made at a time.
That is what I expected. Thanks for the confirmation. ron
participants (3)
-
Christopher Kohlhoff
-
Ronald Garcia
-
Sebastian Redl