/* * Ivy League * * File descriptors, channels * * Copyright 1990-2000 * Laboratoire de Recherche en Informatique (LRI) * Centre d'Etudes de la Navigation Aerienne (CENA) * * original code by Michel Beaudouin-Lafon, * heavily modified by Stephane Chatty and Stephane Sire * * $Id$ * */ #ifdef __GNUG__ #pragma implementation "Channel.h" #endif #include "Channel.h" #include "Scheduler.h" #include "MsgBuffer.h" #include "error.h" #include "ivl/String.h" #include #include #include #include // // 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 IvlFd :: Use (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]++; } /*?nodoc?*/ void IvlFd :: Unuse (int fd) { if (fd < 0) return; if (--Refs [fd] == 0) close (fd); } /*?nodoc?*/ char* IvlFd :: StrRepr (char* buf) { sprintf (buf, "%d", Fd); return buf; } /*?class IvlFd This class implements objects representing Unix file descriptors. When the associated file descriptor is -1, the \typ{IvlFd} 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{IvlFd(const IvlFd\&)}. All member functions described here are inline for maximum efficiency. ?*/ #ifdef DOC /*? Construct a closed file descriptor. ?*/ IvlFd :: IvlFd () { } /*? 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{IvlFd} is normally expected. This is most useful when using Unix file descriptors. ?*/ IvlFd :: IvlFd (int fd) { } /*? This conversion operator makes it possible to pass a \typ{IvlFd} argument where an integer file descriptor is expected. This is most useful for system calls. ?*/ int IvlFd :: operator int () { } /*? Open the object on file descriptor \var{fd}. If it was already opened, it is closed first. ?*/ void IvlFd :: 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 IvlFd :: Close () { } /*? Return true if the object is opened, false else. ?*/ bool IvlFd :: 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 IvlFd :: 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 IvlFd :: Write (byte* b, int n) { } #endif /* DOC */ /*?class IvlChannel A \typ{IvlChannel} is basically a file descriptor, with more strict semantics than a \typ{IvlFd}. 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 IvlChannel.has mode \var{IONone} and is said to be closed. A IvlChannel.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{IvlChannel} (like in the class \typ{IvlSocket}). 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{IvlScheduler}. A \typ{IvlChannel} can be associated with only one \typ{IvlBaseScheduler} at a time by using the functions \fun{Add} and \fun{Remove} of the Channel. When it is added/removed from its Multiplexor, virtual function \fun{Added/Removed} is called. ?*/ /*? Construct a closed channel. ?*/ IvlChannel :: IvlChannel () : IvlIOS (), Fd (), Mode (IONone), Mpx (0) { } /*? Construct an open channel on file descriptor \var{fd} with mode \var{io}. ?*/ IvlChannel :: IvlChannel (int fd, IOMODE io) : IvlIOS (), Fd (fd), Mode (io), Mpx (0) { } /*?nodoc?*/ IvlChannel :: IvlChannel (const IvlChannel& ch) : IvlIOS (ch), Fd (ch.Fd), Mode (ch.Mode), Mpx (ch.Mpx) { } #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 IvlChannel :: Open (int fd) { } /*?nextdoc?*/ IOMODE IvlChannel :: IOMode () { } /*? Return/set the mode of the channel. ?*/ void IvlChannel :: SetMode (IOMODE io) { } #endif /* DOC */ /*? Destroy the channel. It is removed from its multiplexor if necessary. ?*/ IvlChannel :: ~IvlChannel () { Remove (); } /*?nextdoc?*/ void IvlChannel :: Added () { } /*? These virtual functions are called whenever a channel is added to (resp. removed from) a multiplexer. The default implementation does nothing. ?*/ void IvlChannel :: Removed () { } /*?nextdoc?*/ void IvlChannel :: Add (IvlBaseScheduler* m) { if (Mpx != m) { if (Mpx != 0) Remove (); if (m->Add (this)) { Mpx = m; Added (); } } } /*? Add (resp. remove) a channel to a (resp. from its) multiplexer. If the file descriptor of this channel is invalid, nothing is done. If a channel with the same file descriptor as the one being added is already in the multiplexor, the old channel is first removed. ?*/ void IvlChannel :: Remove () { if (Mpx && Mpx->Remove (*this)) { Removed (); Mpx = 0; } } /*?nextdoc?*/ void IvlChannel :: HandleWrite () { } /*? These virtual functions are called by a \typ{IvlScheduler} when data can be written to the channel or read from the channel. The default implementation does nothing. ?*/ void IvlChannel :: HandleRead () { } #if 0 /*? This virtual function is called by a \typ{IvlScheduler} 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{IvlScheduler} for more details. The default implementation does nothing but returning false. ?*/ bool IvlChannel :: HandleSelect () { return false; } #endif #ifdef DOC /*?nextdoc?*/ int IvlChannel :: Read (byte* b, int n) { } /*? The Unix \fun{read} and \fun{write} system calls, as in class \typ{IvlFd}. ?*/ int IvlChannel :: Write (byte* b, int n) { } #endif /* DOC */ /*?nextdoc?*/ int IvlChannel :: Read (IvlMsgBuffer& 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{IvlMsgBuffer}. 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 IvlChannel :: Write (IvlMsgBuffer& 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 IvlChannel :: ReadBuffer (IvlMsgBuffer& 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 IvlChannel :: WriteBuffer (IvlMsgBuffer& b) { int n; errno = 0; while ((n = Write (b)) > 0) ; return bool (n == -2); } void IvlChannel :: WriteLong (lword l) { Write ((byte*)&l, lwsize); } void IvlChannel :: WriteShort (sword s) { Write ((byte*)&s, swsize); } void IvlChannel :: WriteByte (byte b) { Write (&b, 1); } void IvlChannel :: WriteChar (char c) { Write ((byte*)&c, 1); } void IvlChannel :: WriteString (const char* s) { Write ((byte*) s, strlen (s) + 1); } void IvlChannel :: WriteBuf (const byte* b, int n) { Write (b, n); } bool IvlChannel :: ReadLong (lword& l) { return bool (Read ((byte*)&l, lwsize) == lwsize); } bool IvlChannel :: ReadShort (sword& s) { return bool (Read ((byte*)&s, swsize) == swsize); } bool IvlChannel :: ReadByte (byte& b) { return bool (Read (&b, 1) == 1); } bool IvlChannel :: ReadChar (char& c) { return bool (Read ((byte*)&c, 1) == 1); } int IvlChannel :: ReadString (char* s, int n) { int i = 0; // this is extremely non optimal do { Read ((byte*)s, 1); i++; } while (*s++ && n--); return i; } int IvlChannel :: ReadString (IvlString& s) { // this enforces an undue limitation char c [1024]; int i = ReadString (c, -1); s = c; return i; } bool IvlChannel :: ReadBuf (byte* b, int n) { return bool (Read (b, n) == n); }