
On Sun, 12 Sep 2004 22:18:05 -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.
My guess is that Carlo was saying that the library must be thread-safe at a minimum. That is, I could, for example add/release an event callback from a different thread than the multiplexor is running in. But even if he meant more, my take is we might want some threading capabilities...see below.
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.
It's easy to blow this if you start monitoring any significant hardware. Start monitoring some serial ports and setting various timeouts associated with those prots and you can run into troulble easily. In fact, timers is a big problem -- you need to have a smart queing implementation that keeps the number of timers down to the bare minimum....
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.
Well, it's ugly for the user because it's tough to predict when you are going to hit the 64. So I disagree, I'd like to see the user shielded from this issue.
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.
Well, I think there might need to be some interface here. For example, it would be nice for the multiplexor would have a pool of threads and dispatch each event to execute in a thread. The size of that pool might be '0' in which case the multiplexor uses its' thread to dispatch in -- hence degenerating into a single-threaded arrangement.
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. In addition, the design should be as unsophisticated as possible. In particular, event notification might be simple functors (no 'observer' frameworks)
I'd like to see a template approach (see below) that allows new multiplexor and event handler types to be added in as they are developed. The core then just sets up and manages the core of the dispatching.
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.
Agreed. BTW, I'd like to see an attempt to remove all virtual methods from the mix.
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.
Well, I'll disagree with this one as well. I think it should be coupled with both thread and date_time, since you picked those two ;-) Here's why. I think the interface should look something like this: class mutliplexor { public: //returns a unique event handler id template<class EventHandler, class EventMultiplexor> boost::int32_t register(EventHandler eh, EventMultiplexor em, unsigned int priority); void remove(boost::int32_t event_handler_id); void suspend(boost::int32_t event_handler_id); void run_event_loop(time_duration td = time_duration(pos_infinity)); void end_event_loop(time_duration td = time_duration(pos_infinity)); }; Note that the amount of time to run the event loop is specified as a time_duration which allows things like 'pos_infinity' or run forever to be specified more cleanly than the typical interface which passes '0' meaning run forever. Now I can write code that looks like and it's perfectly clear what it means: multiplexor m(...); //setup while (!done) { m.run_event_loop(seconds(1)); //do other stuff like set done } So there's the hook to date_time. BTW, for this part of date_time you only need headers -- you don't need to link the lib. As for boost.thread, that will be needed because the mutliplexor implemenation of register will need to manage a list of event handlers and will need to be capable of dealing with the remove, suspend, and register running in different user threads. This means it will need to lock. So even if you argue away date-time I don't see how you avoid boost.thread. Jeff