diff options
author | chatty | 2000-11-28 17:07:41 +0000 |
---|---|---|
committer | chatty | 2000-11-28 17:07:41 +0000 |
commit | 66caeca4d381a66409c3fc4e812a3e2e99fae081 (patch) | |
tree | 76d2ad33e700fed53c7862c326e8253ba425d73c /comm/Multiplexer.cc | |
parent | 4eb666da31191588d89629ebc38ee98f02ee89b4 (diff) | |
download | ivy-league-66caeca4d381a66409c3fc4e812a3e2e99fae081.zip ivy-league-66caeca4d381a66409c3fc4e812a3e2e99fae081.tar.gz ivy-league-66caeca4d381a66409c3fc4e812a3e2e99fae081.tar.bz2 ivy-league-66caeca4d381a66409c3fc4e812a3e2e99fae081.tar.xz |
Modification du mecanisme d'ajout d'un Channel
Remplacement du HandleSelect par un systeme de Hook
Integration avec DnnMultiplexer
Diffstat (limited to 'comm/Multiplexer.cc')
-rw-r--r-- | comm/Multiplexer.cc | 315 |
1 files changed, 178 insertions, 137 deletions
diff --git a/comm/Multiplexer.cc b/comm/Multiplexer.cc index c1bf8b6..1edb225 100644 --- a/comm/Multiplexer.cc +++ b/comm/Multiplexer.cc @@ -3,7 +3,7 @@ * * by Michel Beaudouin-Lafon * - * Copyright 1990-1995 + * Copyright 1990-1996 * Laboratoire de Recherche en Informatique (LRI) * * Channel sets, or multiplexers @@ -13,8 +13,8 @@ */ #include "Multiplexer.h" -#include "TimeOut.h" -#include "SignalHandler.h" +#include "../UCH/TimeOut.h" +#include "../UCH/SignalHandler.h" #include <signal.h> // for NSIG #include <sys/errno.h> @@ -58,41 +58,55 @@ Multiplexers make it possible to handle several communication channels at the same time; their implementation is based on the Unix \com{select} system call or similar mechanisms. Multiplexers are also able to implement safe (ie. not interruption-based) timers and signal handlers. -Some of the channels in the set are active, others can be inactive. -A multiplexer can be scanned so that the functions \fun{HandleRead} -and \fun{HandleWrite} are called for every active channel that is ready to read or write. -The functions \fun{HandleSelect} of each channel is called before actually scanning -so that each channel can perform a background task or buffer input/output. - -Multiplexers may have different implementations, depending on the environment. -When no other library is involved, they are simply implemented on top of Unix. -But if \uch\ channels have to be mixed with the X Toolkit for instance, the -implementation uses the mechanisms provided by the X Toolkit to add new channels. -The same holds for Tcl/Tk or other environments. Every such implentation corresponds -to a derived class of \typ{UchBaseMultiplexer}, which offers a uniform interface -that hides the implementation differences. See the class \typ{UchMultiplexer} for -a ``pure \uch'' implementation of multiplexers. - -Note that the functions that take an integer file descriptor as argument can be passed -a \typ{FILDES} or a \typ{UchChannel} because the corresponding conversion operators are defined. + +Some of the channels in the set are active, others can be inactive. A +multiplexer can be scanned so that the functions \fun{HandleRead} and +\fun{HandleWrite} are called for every active channel that is ready to +read or write. + +If a channel needs to perform a background task or buffer input/output, +it can add a hook on the multiplexer with \fun{AddHook} which will be +called before actually scanning. For example that's the case for a connection +with the X server, when some pending events have been buffered into the +client by the previous \fun{HandleRead} call and need to be processed. + +Multiplexers may have different implementations, depending on the +environment. When no other library is involved, they are simply +implemented on top of Unix. But if \uch\ channels have to be mixed +with the X Toolkit for instance, the implementation uses the +mechanisms provided by the X Toolkit to add new channels. The same +holds for Tcl/Tk or other environments. Every such implentation +corresponds to a derived class of \typ{UchBaseMultiplexer}, which +offers a uniform interface that hides the implementation +differences. See the class \typ{UchMultiplexer} for a ``pure \uch'' +implementation of multiplexers. + +Note that the functions that take an integer file descriptor as +argument can be passed a \typ{FILDES} or a \typ{UchChannel} because +the corresponding conversion operators are defined. + +The semantic to Add/Remove a channel from a multiplexer is to call +\fun{Add} and \fun{Remove} of the channel with the multiplexer as +an argument for the first one. ?*/ /*? Create an empty channel set. ?*/ UchBaseMultiplexer :: UchBaseMultiplexer () -: Channels (new pUchChannel [NFILE]), +: DnnBaseMultiplexer (), + Channels (new (UchChannel*) [NFILE]), Timers (), ReadCount (0), WriteCount (0), SelectCount (0), - Looping (false), SigFired (false), Handlers (new UchBaseSignalHandler* [NSIG]), NbSignals (new int [NSIG]) -{ +{ memset (Handlers, 0, NSIG * sizeof (UchBaseSignalHandler*)); memset (NbSignals, 0, NSIG * sizeof (int)); + memset (Channels, 0, NFILE * sizeof (UchChannel*)); } /*?nodoc?*/ @@ -100,6 +114,8 @@ UchBaseMultiplexer :: ~UchBaseMultiplexer () { #ifdef CPLUS_BUG4 delete [NFILE] Channels; + delete [NSIG] NbSignals; + delete [NSIG] Handlers; #else delete [] Channels; delete [] NbSignals; @@ -109,41 +125,51 @@ UchBaseMultiplexer :: ~UchBaseMultiplexer () } /*? -Add a channel to the set. -Note that the argument is a pointer to the channel; no copy is made by this function. -If a channel with the same file descriptor as the one being added is already in the set, -the old channel is first removed. +This array operator returns a pointer to the channel corresponding to file descriptor \var{fd}. +If there is no such channel in this channel set, the operator returns NIL. ?*/ -void +UchChannel* +UchBaseMultiplexer :: operator [] (int fd) +{ + return (fd < 0 || fd >= NFILE) ? 0 : Channels [fd]; +} + +/*? +Add a channel to the set. Note that the argument is a pointer to +the channel; no copy is made by this function. If a channel with the +same file descriptor as the one being added is already in the set, the +old channel is first removed. That means that for a given file descriptor, +there can be only one channel working on it at a time. +?*/ +bool UchBaseMultiplexer :: Add (UchChannel* chan) { int fd = chan->FilDes (); if (fd < 0) - return; - pUchChannel ochan = Channels [fd]; + return false; + UchChannel* ochan = Channels [fd]; if (ochan) - Remove (fd); - chan->Added (*this); + ochan->Remove (); Channels [fd] = chan; SetMasks (fd, chan->IOMode ()); + return true; } -pUchChannel NIL_CHAN (0); /*? Remove a channel from the set. This function can be passed a \var{UchChannel}. ?*/ -void +bool UchBaseMultiplexer :: Remove (int fd) { if (fd < 0) - return; + return false; UchChannel* ch = Channels [fd]; if (ch) { - ch->Removed (*this); SetMasks (fd, IONone); Channels [fd] = 0; } + return true; } /*? @@ -154,8 +180,11 @@ This is a way of exiting from \fun{LoopScan}. void UchBaseMultiplexer :: RemoveAll () { - for (int fd = 0; fd < NFILE; fd++) - Remove (fd); + for (int fd = 0; fd < NFILE; fd++) { + UchChannel* ch = Channels [fd]; + if (ch) + ch->Remove (); // Calls UchBaseMultiplexer::Remove + } } /*? @@ -173,26 +202,6 @@ UchBaseMultiplexer :: SetMode (int fd, IOMODE mode) Channels [fd] -> SetMode (mode); } -#ifdef DOC - -/*? -This array operator returns a (smart) pointer to the channel corresponding to file descriptor \var{fd}. -If there is no such channel in this channel set, the operator returns NIL. -?*/ -pUchChannel -UchBaseMultiplexer :: operator [] (int fd) -{ } - -/*? -Add a channel to the set. -Here the argument is a reference, and a dynamically allocated copy of it is actually added. -Compare with previous function. -?*/ -void -UchBaseMultiplexer :: Add (const UchChannel& ch) -{ } - -#endif /* DOC */ /*! This function is called by UchSignalHandlers when a signal is @@ -253,6 +262,7 @@ UchBaseMultiplexer :: SetMasks (int, IOMODE) { } +#if 0 static jmp_buf reset_run; class UchMpxAborter : public UchBaseSignalHandler { @@ -260,6 +270,7 @@ public: UchMpxAborter (UchBaseMultiplexer& m, int s) : UchBaseSignalHandler (m, s) {} void DeferredHandle (int) { MyMpx.Abort (); } }; +#endif /*? Run a multiplexer: make it repeatedly scan its channels, and take the @@ -277,15 +288,17 @@ or signals SIGINT or SIGTERM were received.\\ MPX_RES UchBaseMultiplexer :: Run () { +#if 0 if (setjmp (reset_run)) return isMpxAborted; UchMpxAborter h1 (*this, SigTerm); UchMpxAborter h2 (*this, SigInt); - +#endif return Loop (); } +#if 0 /*? Remove all channels from the multiplexer, thus stopping it if running. ?*/ @@ -305,6 +318,7 @@ UchBaseMultiplexer :: Abort () if (Looping) longjmp (reset_run, 1); } +#endif /*?class UchMultiplexer The class \typ{UchMultiplexer} is the default implementation of multiplexers, @@ -317,7 +331,9 @@ Build an empty multiplexer. ?*/ UchMultiplexer :: UchMultiplexer () : UchBaseMultiplexer (), - TimeOut (-1) + TimeOut (-1), + Looping (false), + Hooks () { FD_ZERO (&ReadMask); FD_ZERO (&WriteMask); @@ -339,66 +355,45 @@ UchMultiplexer :: SetMasks (int fd, IOMODE mode) { if (mode & IORead) { if (! FD_ISSET (fd, &ReadMask)) { - ReadCount ++; + ReadCount++; FD_SET (fd, &ReadMask); } } else { if (FD_ISSET (fd, &ReadMask)) { FD_CLR (fd, &ReadMask); - ReadCount --; + ReadCount--; } } if (mode & IOWrite) { if (! FD_ISSET (fd, &WriteMask)) { - WriteCount ++; + WriteCount++; FD_SET (fd, &WriteMask); } } else { if (FD_ISSET (fd, &WriteMask)) { FD_CLR (fd, &WriteMask); - WriteCount --; + WriteCount--; } } if (mode & IOSelect) { if (! FD_ISSET (fd, &SelectMask)) { - SelectCount ++; + SelectCount++; FD_SET (fd, &SelectMask); } } else { if (FD_ISSET (fd, &SelectMask)) { FD_CLR (fd, &SelectMask); - SelectCount --; + SelectCount--; } } } -/*? -This function calls the select handler of each channel of the set in select mode. -It returns true as soon as one select handler returns true, -else it returns false when the select handlers have been called. -?*/ -bool -UchMultiplexer :: HandleSelect () -{ - int fd, nfd; - - if (fd0 >= NFILE) - fd0 = 0; - for (fd = fd0++, nfd = SelectCount; nfd; (fd < NFILE) ? fd++ : (fd = 0)) { - if (! FD_ISSET (fd, &SelectMask)) - continue; - if (Channels [fd] -> HandleSelect ()) - return true; - nfd--; - if (! Looping) - return false; - } - return false; -} - +#ifdef 0 +Scan n'est plus a jour avec LoopScan et n'est jamais appelee de +toutes facons.... ////// should add signal handling /*?nextdoc?*/ @@ -419,7 +414,8 @@ UchMultiplexer :: Scan (bool nointr, bool poll) while (ret <= 0) { if (ReadCount == 0 && WriteCount == 0 && SelectCount == 0) return 0; - + +/* Old version with HandleSelect, to be replaced by Hooks for (fd = 0, nfd = SelectCount; nfd; fd++) { if (! FD_ISSET (fd, &SelectMask)) continue; @@ -428,6 +424,7 @@ UchMultiplexer :: Scan (bool nointr, bool poll) return -2; nfd--; } +*/ rmsk = ReadMask; wmsk = WriteMask; @@ -486,6 +483,8 @@ UchMultiplexer :: Scan (bool nointr, bool poll) return ret; } +#endif /* Scan */ + /*? Scan the channels in the set and call the channel's handlers. First the select handler (\fun{UchChannel::HandleSelect}) of each channel with mode @@ -516,9 +515,9 @@ MPX_RES UchMultiplexer :: LoopScan (bool nointr) { fd_set rmsk, wmsk; - register int nfd, fd; + register int nb_pending_fd, fd, fd0; Looping = true; - + for (fd0 = 0; Looping; fd0 < NFILE ? fd0++ : (fd0 = 0)) { /* First, handle signals */ @@ -528,27 +527,19 @@ UchMultiplexer :: LoopScan (bool nointr) #if 0 /* Then, timers */ CcuCoreTimer::Fire (&Timers); -#endif - - /* Then, I/Os */ - if (ReadCount == 0 && WriteCount == 0 && SelectCount == 0) +#endif + /* Then, check I/Os */ + if (ReadCount == 0 && WriteCount == 0 && SelectCount == 0) { + ExecHooks (true); return isMpxEmpty; - - for (fd = fd0, nfd = SelectCount; nfd; (fd < NFILE) ? fd++ : (fd = 0)) { - if (! FD_ISSET (fd, &SelectMask)) - continue; - UchChannel* ch = Channels [fd]; - if (ch && ch->HandleSelect ()) - break; - nfd--; - if (! Looping) - return isMpxTerminated; } - if (nfd) // a handler returned true. - continue; - /* let's fiddle with ret to skip select if we are late with timers */ - nfd = -1; + /* Then other hooks */ + ExecHooks (false); + + /* Lets get ready for calling select: set masks and compute next time out. + We fiddle with nb_pending_fd to skip select if we are late with timers */ + nb_pending_fd = -1; rmsk = ReadMask; wmsk = WriteMask; struct timeval tv; @@ -557,41 +548,55 @@ UchMultiplexer :: LoopScan (bool nointr) CcuTimeStamp now; Millisecond delay = TimeOut - now; if (delay < 0) - nfd = 0; + nb_pending_fd = 0; tv.tv_sec = delay / 1000; tv.tv_usec = 1000 * (delay % 1000); timeout = &tv; } - if (nfd != 0) - nfd = select (NFILE, FD_SET_TYPE(&rmsk), FD_SET_TYPE(&wmsk), 0, timeout); - if (nfd == 0) { + + /* Call select, except if late with timers */ + if (nb_pending_fd != 0) + nb_pending_fd = select (NFILE, FD_SET_TYPE(&rmsk), FD_SET_TYPE(&wmsk), 0, timeout); + /* Handle time out */ + if (nb_pending_fd == 0) { if (TimeOut != -1) CcuCoreTimer::Fire (&Timers); else fprintf (stderr, "select returned 0 without reason!\n"); continue; - } else if (nfd < 0) { - if (nointr && nfd == -1 && errno == EINTR) + + /* Handle errors */ + } else if (nb_pending_fd < 0) { + if (nointr && nb_pending_fd == -1 && errno == EINTR) continue; - else + else { + ExecHooks (true); return isMpxError; + } } - for (fd = fd0; nfd; (fd < NFILE) ? fd++ : (fd = 0)) { + /* Finally, handle pending input and output */ + for (fd = fd0; nb_pending_fd; (fd < NFILE) ? fd++ : (fd = 0)) { if (FD_ISSET (fd, &wmsk)) { - nfd--; - Channels [fd] -> HandleWrite (); - if (! Looping) + nb_pending_fd--; + Channels[fd]->HandleWrite (); + if (! Looping) { + ExecHooks (true); return isMpxTerminated; + } } if (FD_ISSET (fd, &rmsk)) { - nfd--; - Channels [fd] -> HandleRead (); - if (! Looping) + nb_pending_fd--; + Channels[fd]->HandleRead (); + if (! Looping) { + ExecHooks (true); return isMpxTerminated; + } } } } + + ExecHooks (true); return isMpxTerminated; } @@ -603,18 +608,6 @@ UchMultiplexer :: StrRepr (char* buf) return buf; } -#ifdef DOC -// fake entries for inline functions -/*? -This function makes \fun{LoopScan} exit immediately. -Thus it must be called from within a channel's handler. -?*/ -void -UchMultiplexer :: LoopEnd () -{ } - -#endif /* DOC */ - /*?hidden?*/ void UchMultiplexer :: SetTimeOut (Millisecond delay) @@ -636,3 +629,51 @@ UchMultiplexer :: Loop () { return LoopScan (); } + +void +UchMultiplexer :: Stop () +{ + Looping = false; +} + + +void +UchMultiplexer :: AddHook (UchMpxHook* h, bool final) +{ + Hooks.Append ((void*) h); + if (final) + FinalHooks.Append ((void*) h); +} + +void +UchMultiplexer :: RemoveHook (UchMpxHook* h, bool final) +{ + Hooks.Remove ((void*) h); + if (final) + FinalHooks.Remove ((void*) h); +} + +void +UchMultiplexer :: AddFinalHook (UchMpxHook* h) +{ + FinalHooks.Append ((void*) h); +} + +void +UchMultiplexer :: RemoveFinalHook (UchMpxHook* h) +{ + FinalHooks.Remove ((void*) h); +} + + +void +UchMultiplexer :: ExecHooks (bool final) +{ + CcuList* hooks = final ? &FinalHooks : &Hooks; + CcuListIter li = *hooks; + while (++li) { + UchMpxHook* h = (UchMpxHook*) *li; + (*h)(); + } +} + |