From ba066c34dde204aa192d03a23a81356374d93731 Mon Sep 17 00:00:00 2001 From: chatty Date: Wed, 7 Apr 1993 11:50:31 +0000 Subject: Initial revision --- comm/Channel.cc | 402 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 402 insertions(+) create mode 100644 comm/Channel.cc (limited to 'comm/Channel.cc') diff --git a/comm/Channel.cc b/comm/Channel.cc new file mode 100644 index 0000000..b8178aa --- /dev/null +++ b/comm/Channel.cc @@ -0,0 +1,402 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1990-1993 + * Laboratoire de Recherche en Informatique (LRI) + * + * File descriptors, channels + * + * $Id$ + * $CurLog$ + */ + +#include "Channel.h" +#include "MsgBuffer.h" +#include "error.h" + +#include +#include +#include + +CH_HANDLER UchChannel::ReadHandler = 0; +CH_HANDLER UchChannel::WriteHandler = 0; + +// +// this section implements refcounts for file descriptors, so that they +// are closed only when the last occurence is deleted +// +static int Refs [NFILE]; +static int Inited = 0; + +/*?nodoc?*/ +void +_AddRef (int fd) +{ + if (fd < 0) + return; + if (! Inited) { + Inited = 1; + for (int i = 0; i < NFILE; i++) + Refs [i] = 0; + Refs [0] = Refs [1] = Refs [2] = 1; + } + Refs [fd]++; +#ifdef SMART_DEBUG + dbgprintf ("AddRef %d -> %d\n", fd, Refs [fd]); +#endif +#ifdef _TRACE2 + _TRACE2 (fd, Refs [fd]); +#endif +} + +/*?nodoc?*/ +void +_DelRef (int fd) +{ + if (fd < 0) + return; + if (--Refs [fd] == 0) + close (fd); +#ifdef SMART_DEBUG + dbgprintf ("DelRef %d -> %d\n", fd, Refs [fd]); +#endif +#ifdef _TRACE2 + _TRACE2 (fd, Refs [fd]); +#endif +} + +/*?nodoc?*/ +char* +UchFilDes :: StrRepr (char* buf) +{ + sprintf (buf, "%d", Fd); + return buf; +} + +/*?class UchFilDes +This class implements objects representing Unix file descriptors. +When the associated file descriptor is -1, the \typ{UchFilDes} is said to be closed, +else it is said to be opened. +Because several objects of this class can refer to the same file descriptor, +the destructor closes the file descriptor only if it is the last object referring to this file descriptor. +This is implemented with the help of a reference count, the overloading of \fun{operator =} +and the definition of the constructor \fun{UchFilDes(UchFilDes &)}. +All member functions described here are inline for maximum efficiency. +?*/ + +#ifdef DOC + +/*? +Construct a closed file descriptor. +?*/ +UchFilDes :: UchFilDes () +{ } + +/*? +Construct an open file descriptor for the Unix file descriptor \var{fd}. +In particular, this makes it possible to pass an integer where a \typ{UchFilDes} is normally expected. +This is most useful when using Unix file descriptors. +?*/ +UchFilDes :: UchFilDes (int fd) +{ } + +/*? +This conversion operator makes it possible to pass a \typ{UchFilDes} argument where +an integer is expected. This is most useful for system calls. +?*/ +int +UchFilDes :: operator int () +{ } + +/*? +Return the Unix file descriptor, or -1 if it is closed. +?*/ +int +UchFilDes :: FilDes () +{ } + +/*? +Open the object on file descriptor \var{fd}. +If it was already opened, it is closed first. +?*/ +void +UchFilDes :: Open (int fd) +{ } + +/*? +Close a file descriptor. If it is opened, this actually closes the file descriptor +only if it was the last reference to it. +\fun{Close} is automatically called by the destructor. +?*/ +void +UchFilDes :: Close () +{ } + +/*? +Return TRUE if the object is opened, FALSE else. +?*/ +bool +UchFilDes :: Opened () +{ } + +/*? +Read at most \var{n} bytes into buffer \var{b} and return the number of bytes actually read. +This is the Unix \fun{read} system call. +For efficiency, this function does not test whether the file descriptor is opened. +?*/ +int +UchFilDes :: Read (byte* b, int n) +{ } + +/*? +Write at most \var{n} bytes of buffer \var{b} and return the number of bytes actually written. +This is the Unix \fun{write} system call. +For efficiency, this function does not test whether the file descriptor is opened. +?*/ +int +UchFilDes :: Write (byte* b, int n) +{ } + +#endif /* DOC */ + +/*?class UchChannel +A \typ{UchChannel} is basically a file descriptor, with more strict semantics than a \typ{UchFilDes}. +A channel is immutable: once initialized, it cannot change its file descriptor. +It has a mode, of type \typ{^{IOMODE}}, with possible values +\var{IONone}, \var{IORead}, \var{IOWrite}, \var{IOReadWrite}, +\var{IOSelect}, \var{IOReadSelect}, \var{IOWriteSelect}, \var{IOAll}. +\index{IOMODE :: IONone}\index{IOMODE :: IORead}\index{IOMODE :: IOWrite}\index{IOMODE :: IOReadWrite}\index{IOMODE :: IOSelect}\index{IOMODE :: IOReadSelect}\index{IOMODE :: IOWriteSelect}\index{IOMODE :: IOAll} +Before being initialized, a UchChannel.has mode \var{IONone} and is said to be closed. + +A UchChannel.has a number of virtual functions. +The destructor is virtual; this is useful for classes that store pointers to objects +of a class derived from \typ{UchChannel} (like in the class \typ{UchSocket}). +The functions \fun{HandleSelect}, \fun{HandleRead} and \fun{HandleWrite} +are intended to give a channel the ability to automatically handle input/output, +for instance for use with the \fun{select} system call. +Such capabilities are used and thus illustrated in class \typ{UchMultiplexer}. + +The class \typ{^{pUchChannel}} implements smart pointers to channels. +Smart pointers behave like pointers but they manage a reference count on the +pointed to objects for an automatic reclaim of dynamically allocated objects. +This is very useful especially in the class \typ{UchMultiplexer} because arrays of +(smart) pointers to buffers are used. +?*/ + +/*? +Construct a closed channel. +?*/ +UchChannel :: UchChannel () +: Fd (), + Mode (IONone) +{ +} + +/*? +Construct an open channel on file descriptor \var{fd} with mode \var{io}. +?*/ +UchChannel :: UchChannel (int fd, IOMODE io) +: Fd (fd), + Mode (io) +{ +} + +/*?nodoc?*/ +UchChannel :: UchChannel (const UchChannel& ch) +: Fd (ch.Fd), + Mode (ch.Mode) +{ +} + +#ifdef DOC +/*? +Open the channel on file descriptor \var{fd}. +A channel can only be opened once, and it cannot be closed. +This function is useful when the channel was created with the default contructor, +in order to associate a file descriptor to it. +Because a channel is immutable, this can be done only once. +?*/ +void +UchChannel :: Open (int fd) +{ } + +/*?nextdoc?*/ +IOMODE +UchChannel :: IOMode () +{ +} + +/*? +Return/set the mode of the channel. +?*/ +void +UchChannel :: SetMode (IOMODE io) +{ } + +#endif /* DOC */ + + +/*?nodoc?*/ +UchChannel :: ~UchChannel () +{ + // nothing special here +} + +/*?nodoc?*/ +UchChannel* +UchChannel :: Copy () const +{ + return new UchChannel (*this); +} + +/*?nextdoc?*/ +void +UchChannel :: AddNotify (UchMultiplexer&) +{ +} + +/*? +These virtual functions are called whenever a channel is added to (resp. removed from) +a multiplexer. The default implementation does nothing. +?*/ +void +UchChannel :: RemoveNotify (UchMultiplexer&) +{ +} + +/*?nextdoc?*/ +void +UchChannel :: HandleWrite () +{ + if (! UchChannel::WriteHandler) + Error (ErrFatal, "UchChannel::HandleWrite", ErrorTable [ErrShouldImplement]); + else + (* UchChannel::WriteHandler) (this); +} + +/*? +These virtual functions are called by a \typ{UchMultiplexer} when data can be written +to the channel or read from the channel. +They are are normally redefined in derived classes. +The default implementation is the following: +if the static members \var{UchChannel::ReadHandler} (resp. \var{UchChannel::WriteHandler}) is set, +it is called by \fun{HandleRead} (resp. \fun{HandleWrite}); +else an error message is output. +These members are public so that they can be assigned directly at any time. +By default they are not set. +The type of these static members is \typ{^{CH_HANDLER}}: +pointer to void function with one argument of type \typ{UchChannel*}. +?*/ +void +UchChannel :: HandleRead () +{ + if (! UchChannel::ReadHandler) + Error (ErrFatal, "UchChannel::HandleRead", ErrorTable [ErrShouldImplement]); + else + (* UchChannel::ReadHandler) (this); +} + +/*? +This virtual function is called by a \typ{UchMultiplexer} before making a \fun{select} call. +It is intended to handle any background task or buffered input/output associated to the channel. +If it returns TRUE, the channel set scan functions will return before performing the select call. +See the class \typ{UchMultiplexer} for more details. +The default implementation does nothing but returning FALSE. +?*/ +bool +UchChannel :: HandleSelect () +{ + return FALSE; +} + +#ifdef DOC + +/*?nextdoc?*/ +int +UchChannel :: Read (byte* b, int n) +{ } + +/*? +The Unix \fun{read} and \fun{write} system calls, as in class \typ{UchFilDes}. +?*/ +int +UchChannel :: Write (byte* b, int n) +{ } + +#endif /* DOC */ + +/*?nextdoc?*/ +int +UchChannel :: Read (UchMsgBuffer& b) +{ + int l = b.FreeLength (); + if (! l) + return -2; + int n = read (Fd, (char*) b.Free (), l); + if (n > 0) + b.More (n); + return n; +} + +/*? +The Unix \fun{read} and \fun{write} system calls applied to a \typ{UchMsgBuffer}. +They return the number of bytes transferred, -1 if a system error occurred, +-2 if the buffer is empty when writing or full when reading. +?*/ +int +UchChannel :: Write (UchMsgBuffer& b) +{ + int l = b.BufLength (); + if (! l) + return -2; + int n = write (Fd, (const char*) b.Buffer (), l); + if (n > 0) + b.Flush (n); + return n; +} + +/*?nextdoc?*/ +bool +UchChannel :: ReadBuffer (UchMsgBuffer& b) +{ + int n; + errno = 0; + while ((n = Read (b)) > 0); + return bool (n == -2); +} + +/*? +These functions repeatedly call \fun{Read} or \fun{Write} until the whole buffer is +transferred or a system error occurred. In case of an error, they return FALSE, else TRUE. +?*/ +bool +UchChannel :: WriteBuffer (UchMsgBuffer& b) +{ + int n; + errno = 0; + while ((n = Write (b)) > 0); + return bool (n == -2); +} + +/*? +This function is intended for sockets that accept connections. +It returns a dynamically allocated new channel that is opened on the accepted connection. +If it fails, it returns 0. +?*/ +UchChannel* +UchChannel :: Accept () +{ + int fd; + + errno = 0; + if (Fd < 0) + return (UchChannel*) 0; + + if ((fd = accept (Fd, 0, 0)) < 0) + return (UchChannel*) 0; + + return new UchChannel (fd); +} + -- cgit v1.1