summaryrefslogtreecommitdiff
path: root/comm/Multiplexer.cc
diff options
context:
space:
mode:
authorchatty1993-04-07 11:50:31 +0000
committerchatty1993-04-07 11:50:31 +0000
commitba066c34dde204aa192d03a23a81356374d93731 (patch)
tree39391f6235d2cf8a59a0634ac5ea430cdd21f5d4 /comm/Multiplexer.cc
parent05ab076e1c2a9ca16472f9a6b47b8d22914b3783 (diff)
downloadivy-league-ba066c34dde204aa192d03a23a81356374d93731.zip
ivy-league-ba066c34dde204aa192d03a23a81356374d93731.tar.gz
ivy-league-ba066c34dde204aa192d03a23a81356374d93731.tar.bz2
ivy-league-ba066c34dde204aa192d03a23a81356374d93731.tar.xz
Initial revision
Diffstat (limited to 'comm/Multiplexer.cc')
-rw-r--r--comm/Multiplexer.cc463
1 files changed, 463 insertions, 0 deletions
diff --git a/comm/Multiplexer.cc b/comm/Multiplexer.cc
new file mode 100644
index 0000000..497e041
--- /dev/null
+++ b/comm/Multiplexer.cc
@@ -0,0 +1,463 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Channel sets, or multiplexers
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "Multiplexer.h"
+#include "TimeOut.h"
+
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+
+#if defined(__hpux) && !defined(__GNUG__)
+#define FD_SET_TYPE(x) ((int*) x)
+#else
+#define FD_SET_TYPE(x) (x)
+#endif
+
+extern int errno;
+
+#ifndef FD_SET
+#ifndef NFDBITS
+#define FD_SET(n, p) ((p)->fds_bits[0] |= (1 << (n)))
+#define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1 << (n)))
+#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1 << (n)))
+#define FD_ZERO(p) ((p)->fds_bits[0] = 0)
+#else
+#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
+#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
+#define FD_ZERO(p) memset((char *)(p), 0, sizeof(*(p)))
+#endif
+#endif
+
+
+/*?class UchMultiplexer
+An object of class \typ{UchMultiplexer} is a set of channels.
+Some of the channels in the set are active, others can be inactive.
+A channel set can be scanned so that the functions \fun{HandleRead}
+and \fun{HandleWrite} are called for each 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.
+This provides a level of abstraction over the Unix \fun{select} system call.
+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 class \typ{^{pCHAN_SET}} implements smart pointers to channel sets.
+Smart pointers behave like pointers but they manage a reference count on the
+pointed to objects for an automatic reclaim of dynamically allocated objects.
+?*/
+
+// constructor: initialize states
+//
+
+/*?
+Create an empty channel set.
+?*/
+UchMultiplexer :: UchMultiplexer ()
+: Channels (new pUchChannel [NFILE]),
+ Timers (),
+ ReadCount (0),
+ WriteCount (0),
+ SelectCount (0),
+ Looping (FALSE)
+{
+ FD_ZERO (&ReadMask);
+ FD_ZERO (&WriteMask);
+ FD_ZERO (&SelectMask);
+}
+
+/*?nodoc?*/
+UchMultiplexer :: ~UchMultiplexer ()
+{
+#ifdef CPLUS_BUG4
+ delete [NFILE] Channels;
+#else
+ delete [] Channels;
+#endif
+ Channels = 0;
+}
+
+/*?
+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, thus calling \fun{RemoveNotify} for that channel.
+The virtual function \fun{AddNotify} is called before actually adding the channel
+to the set.
+?*/
+void
+UchMultiplexer :: Add (UchChannel* chan)
+{
+ int fd = chan->FilDes ();
+ if (fd < 0)
+ return;
+ pUchChannel ochan = Channels [fd];
+ if (ochan)
+ Remove (fd);
+ chan->AddNotify (*this);
+ AddNotify (chan);
+ Channels [fd] = chan;
+ SetMasks (fd, chan->IOMode ());
+}
+
+pUchChannel NIL_CHAN (0);
+
+/*?
+Remove a channel from the set.
+The virtual function \fun{RemoveNotify} is called before actually removing the channel
+from the set.
+?*/
+void
+UchMultiplexer :: Remove (int fd)
+{
+ if (fd < 0)
+ return;
+ UchChannel* ch = Channels [fd];
+ if (ch) {
+ ch->RemoveNotify (*this);
+ RemoveNotify (ch);
+ SetMasks (fd, IONone);
+ Channels [fd] = 0;
+ }
+}
+
+/*?
+Remove all channels from this channel set.
+This function calls \fun{Remove} for all channels of the set.
+This is a way of exiting from \fun{LoopScan}.
+?*/
+void
+UchMultiplexer :: RemoveAll ()
+{
+ for (int fd = 0; fd < NFILE; fd++)
+ Remove (fd);
+}
+
+/*?
+Change the mode of a channel in the set.
+Mode \var{IONone} makes the channel inactive (without removing it).
+The virtual function \fun{SetModeNotify} is called before actually changing
+the mode of the channel.
+?*/
+void
+UchMultiplexer :: SetMode (int fd, IOMODE mode)
+{
+ if (fd < 0)
+ return;
+
+ SetModeNotify (Channels [fd], mode);
+ SetMasks (fd, mode);
+ Channels [fd] -> SetMode (mode);
+}
+
+
+// update the masks when channel fd changes its mode
+//
+/*?hidden?*/
+void
+UchMultiplexer :: SetMasks (int fd, IOMODE mode)
+{
+ if (mode & IORead) {
+ if (! FD_ISSET (fd, &ReadMask)) {
+ ReadCount ++;
+ FD_SET (fd, &ReadMask);
+ }
+ } else {
+ if (FD_ISSET (fd, &ReadMask)) {
+ FD_CLR (fd, &ReadMask);
+ ReadCount --;
+ }
+ }
+
+ if (mode & IOWrite) {
+ if (! FD_ISSET (fd, &WriteMask)) {
+ WriteCount ++;
+ FD_SET (fd, &WriteMask);
+ }
+ } else {
+ if (FD_ISSET (fd, &WriteMask)) {
+ FD_CLR (fd, &WriteMask);
+ WriteCount --;
+ }
+ }
+
+ if (mode & IOSelect) {
+ if (! FD_ISSET (fd, &SelectMask)) {
+ SelectCount ++;
+ FD_SET (fd, &SelectMask);
+ }
+ } else {
+ if (FD_ISSET (fd, &SelectMask)) {
+ FD_CLR (fd, &SelectMask);
+ SelectCount --;
+ }
+ }
+
+}
+
+/*?nextdoc?*/
+void
+UchMultiplexer :: AddNotify (UchChannel*)
+{
+ // nothing
+}
+
+/*?nextdoc?*/
+void
+UchMultiplexer :: RemoveNotify (UchChannel*)
+{
+ // nothing
+}
+
+/*?
+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;
+}
+
+/*?
+These virtual functions are called by the functions \fun{Add}, \fun{Remove}
+(and thus \fun{RemoveAll}) and \fun{SetMode}.
+They provide hooks for application that need to take special action when the
+set changes. They are called before the corresponding action is actually
+carried out.
+By default these functions do nothing.
+?*/
+void
+UchMultiplexer :: SetModeNotify (UchChannel*, IOMODE)
+{
+ // nothing
+}
+
+/*?nextdoc?*/
+int
+UchMultiplexer :: Scan (bool nointr, bool poll)
+{
+ fd_set rmsk, wmsk;
+ int nfd, ret = -1;
+ register int fd;
+ struct timeval tout;
+ struct timeval* timeout = 0;
+
+ if (poll) {
+ timeout = &tout;
+ tout.tv_sec = tout.tv_usec = 0;
+ }
+
+ while (ret <= 0) {
+ CcuCoreTimer::Fire (&Timers);
+
+ if (ReadCount == 0 && WriteCount == 0 && SelectCount == 0)
+ return 0;
+
+ for (fd = 0, nfd = SelectCount; nfd; fd++) {
+ if (! FD_ISSET (fd, &SelectMask))
+ continue;
+ UchChannel* ch = Channels [fd];
+ if (ch && ch->HandleSelect ())
+ return -2;
+ nfd--;
+ }
+
+//printf ("Scan: read %x, write %x, TimeOut %d", ReadMask, WriteMask, TimeOut);
+ rmsk = ReadMask;
+ wmsk = WriteMask;
+ if (!poll && TimeOut != -1) {
+ CcuTimeStamp now;
+ Millisecond delay = TimeOut - now;
+//printf (", timeout in %d ms", delay);
+ tout.tv_sec = delay / 1000;
+ tout.tv_usec = 1000 * (delay % 1000);
+ timeout = &tout;
+ }
+//printf ("\n");
+
+ ret = select (NFILE, FD_SET_TYPE(&rmsk) /*read*/, FD_SET_TYPE(&wmsk) /*write*/, 0 /*except*/, timeout);
+ if (ret < 0 || ret == 0 && poll) {
+//printf ("select failed: %d, errno %d\n", ret, errno);
+ if (nointr && ret == -1 && errno == EINTR)
+ continue;
+ return ret;
+ }
+ }
+
+ for (fd = 0, nfd = ret; nfd; fd++) {
+//printf ("Scan: handle %d: r %d, w %d\n", fd, FD_ISSET (fd, &rmsk), FD_ISSET (fd, &wmsk));
+ if (FD_ISSET (fd, &wmsk)) {
+ nfd --;
+ UchChannel* ch = Channels [fd];
+ if (ch)
+ ch->HandleWrite ();
+ }
+ if (FD_ISSET (fd, &rmsk)) {
+ nfd--;
+ UchChannel* ch = Channels [fd];
+ if (ch)
+ ch->HandleRead ();
+ }
+ }
+
+ return ret;
+}
+
+/*?
+Scan the channels in the set and call the channel's handlers.
+First the select handler (\fun{UchChannel::HandleSelect}) of each channel with mode
+\var{IOSelect} is called.
+If it returns TRUE, \fun{Scan} exits immediately, while \fun{LoopScan} loops immediately.
+If no select handler returns TRUE, the system call \fun{select} is used to poll or
+wait for the channels that are ready.
+When the select returns normally, the write handlers \fun{HandleWrite} of the
+channels in mode \var{IOWrite} that are ready to write are called, and
+the read handlers (\fun{HandleRead}) of the channels in mode \fun{IORead}
+that are ready to read are called.
+If \var{nointr} is TRUE, ignore the interrupted system calls, else return an error
+whenever the \fun{select} system call is interrupted.
+If \var{poll} is TRUE, the call is non-blocking, else it is blocking.
+\fun{Scan} calls \fun{select} only once;
+it returns -2 if a select handler returns, it returns 0 if the channel set has no active channels,
+else it returns the return code of \fun{select}.
+\fun{LoopScan} calls \fun{select} repeatedly until \var{LoopEnd} is called, or
+an error occurs, the channel set has no more active channel
+This can occur because the select, read or write handler of a channel may remove
+the channel or change its mode.
+This is usually done when the read handler detects an end of file.
+\fun{LoopScan} returns 0 when there are no more active channel in the set,
+it returns -1 when an error occured (the code is in \var{errno}),
+and it returns 1 if \fun{LoopEnd} was called.
+?*/
+int
+UchMultiplexer :: LoopScan (bool nointr)
+{
+ fd_set rmsk, wmsk;
+ register int nfd, fd;
+ Looping = TRUE;
+
+ for (fd0 = 0; Looping; fd0 < NFILE ? fd0++ : (fd0 = 0)) {
+ CcuCoreTimer::Fire (&Timers);
+
+ if (ReadCount == 0 && WriteCount == 0 && SelectCount == 0)
+ return 0;
+
+ 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 1;
+ }
+ if (nfd) // a handler returned TRUE.
+ continue;
+
+ rmsk = ReadMask;
+ wmsk = WriteMask;
+ struct timeval tv;
+ struct timeval* timeout = 0;
+ if (TimeOut != -1) {
+ CcuTimeStamp now;
+ Millisecond delay = TimeOut - now;
+ tv.tv_sec = delay / 1000;
+ tv.tv_usec = 1000 * (delay % 1000);
+ timeout = &tv;
+ }
+ nfd = select (NFILE, FD_SET_TYPE(&rmsk) /*read*/, FD_SET_TYPE(&wmsk) /*write*/, 0 /*except*/, timeout);
+ if (nfd < 0) {
+ if (nointr && nfd == -1 && errno == EINTR)
+ continue;
+ return nfd;
+ }
+
+ for (fd = fd0; nfd; (fd < NFILE) ? fd++ : (fd = 0)) {
+ if (FD_ISSET (fd, &wmsk)) {
+ nfd--;
+ Channels [fd] -> HandleWrite ();
+ if (! Looping)
+ return 1;
+ }
+ if (FD_ISSET (fd, &rmsk)) {
+ nfd--;
+ Channels [fd] -> HandleRead ();
+ if (! Looping)
+ return 1;
+ }
+ }
+ }
+ return 1;
+}
+
+// string repr
+//
+/*?nodoc?*/
+char*
+UchMultiplexer :: StrRepr (char* buf)
+{
+ sprintf (buf, "R:%ux W:%ux", ReadMask, WriteMask);
+ return buf;
+}
+
+#ifdef DOC
+// fake entries for inline functions
+
+/*?
+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
+UchMultiplexer :: 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
+UchMultiplexer :: Add (const UchChannel& ch)
+{ }
+
+/*?
+This function makes \fun{LoopScan} exit immediately.
+Thus it must be called from within a channel's handler.
+?*/
+void
+UchMultiplexer :: LoopEnd ()
+{ }
+
+#endif /* DOC */
+