
On Sun, Sep 12, 2004 at 10:18:05PM -0500, Aaron W. LaFramboise wrote:
Carlo Wood wrote:
2. It is unavoidable that this library uses threads.
I disagree strongly. I think spawning additional threads is both unnecessary and undesirable.
I have no problem whatsoever to stick to a single thread if you are right. But, I am convinced it will not be possible to do this without threads. If you know otherwise then I suggest you tell me the solution for problems I will run into when trying to code this within one thread.
1) On windows we have a limitation of at most 64 'Event' objects that can be 'waited' for at a time. This is not enough for large server applications that might need thousands of TCP/IP sockets.
In the case of sockets, Winsock has other mechanisms for scaling in this respect, such as I/O completion routines. On pre-Winsock2 platforms, which hopefully are dwindling, I don't think falling back to the 64 handle limit will be a problem.
I have no problem not supporting winsock1, or with at most 64 sockets. But, it could be supported without that the user even KNOWS it created additional threads - why would it be bad to do that?
It seems unlikely to me that there are many cases were the limit would be exceeded. However, in those cases, I don't think it would be a problem if the multiplex user were required to create another thread, and another multiplex. I don't think the multiplex should do this.
Why not? This is what libACE does too; I am interested to hear why you think it is wrong.
2) On windows there are different types of handles/events. It seems to make a lot more sense to use different threads to wait for different types. For example, there is a WSAWaitForMultipleObjects (for sockets) and a WaitForMultipleObjects that allows one to wait for arbitrary events (but not socket events(?)). More in general however - there seems to be a need to use different ways to demultiplex and handle different types - even if the handles of all different types are the same (ie, 'int' on UNIX). Consider the major difference between a listen socket, a very busy UDP socket and a very busy memory mapped filedescriptor. It might be easier to regular priority issues between the different types by putting their dispatchers in separate threads. Note however that I DO think that the callback functions for each event (that is, the moment we start calling IOstream functions) should happen in the same thread again; this new library should shield the use of threads for the user as much as possible!
I also don't think the multiplex should do this. Boost shouldn't second-guess what the user is trying to do. If the user knows he needs two separate threads to handle two separate resources, then let the user create two threads and put a multiplex in each.
But while that might be possible on GNU/Linux, it might be impossible on windows (for example). The demand to provide a portable interface forces us to create (hidden) threads therefore. If a user decided that he only needs one thread and the library is not allowed to implement the requested interface by running two or more threads internally, then how can I implement an interface that allows one to wait for events on 100 sockets, a timer, a few named pipes, a fifo and some large diskfile I/O at the same time? That is possible on linux, but I failed to figure out how this can be done on windows :/
By _multiplex_ I mean the class (or whatever entity) that implements the core of the demultiplexing of various resources. (I'm using this name because thats what I called it in my own library.) I beleive this class should have these characteristics:
1) Minimal - It should handle every sort of event that might need to be handled, but nothing more. More complex logic, such as pooling and balancing, should be handled elsewhere, possibly by a derived class.
Agreed.
In addition, the design should be as unsophisticated as possible. In particular, event notification might be simple functors (no 'observer' frameworks)
How would one use functors to wait for the plethora of different events to be handled? Surely not as template parameter of the multiplexor class. You mean as template parameter of methods of that class? That would still involve a dereference (some virtual function) in the end, somewhere imho; you don't seem to gain anything from this in terms of inlining (the main reason for functors I thought). Templates do however tend to cause a code bloat :/
2) Efficient - For many applications, performance will be paramount. Many asynchronous algorithms will depend on the multiplex core having negligable overhead, and Boost should not disappoint. As it may be a crucial building block of nearly any real-world program, it should also be storage efficient, to not rule out application in embedded areas.
Hmm. I agree with the efficiency in terms of cpu. But as always, storage efficiency and cpu efficiency are eachothers counter parts. You cannot persuade both at the same time. I think that embedded applications need a different approach - they are a different field. It will not necessarily be possible to serve both: high performance, real time server applications AND embedded applications at the same time. In that case I will choose for the high-end server applications at any time :/ (cause that is were my personal interests are).
3) Compatible - See http://article.gmane.org/gmane.comp.lib.boost.devel/109475
It is my opinion, in fact, that this multiplex class should be in its own library, isolated from any other particular library that would depend on it. In other words, it wouldn't be any more coupled with I/O than it would be with Boost.Thread or date_time.
I still think we will need threads - not only internally but even as part of the interface to support users that WANT to write multi-threaded applications. Consider a user who has two threads running and he wants to wait for events in both threads. He will then need a 'Reactor' object for both threads: both threads will need their own 'main loop'. Supporting that (and we have to support it imho) means that the library is thread aware. I think we have to depend on Boost.Thread as soon as threads are being used; I am not willing duplicate the code from Boost.Thread inside Boost.Multiplexor, just to be independend of of Boost.Thread. -- Carlo Wood <carlo@alinoe.com>