modules/up/src/Core/sys/Pipe.cc

/* [<][>]
[^][v][top][bottom][index][help] */

FUNCTIONS

This source file includes following functions.
  1. pipeRead
  2. pipeWrite
  3. pipeTimeout
  4. Pipe
  5. Pipe
  6. Pipe
  7. read
  8. write
  9. send
  10. timeout
  11. flush
  12. flushAndClose

//
// $Id: Pipe.cc,v 1.1.1.1 2000/03/10 16:32:20 engin Exp $
//
// Author(s): Ramesh Govindan

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "util/Types.hh"
#include "util/Trail.hh"
#include "util/Handler.hh"
#include "util/Buffer.hh"
#include "sys/Time.hh"
#include "sys/File.hh"
#include "sched/Timer.hh"
#include "sched/Dispatcher.hh"
#include "sys/Pipe.hh"

// Constants
static const int        ReadBufferSize = 512;

// File locals
static TraceCode        tracePipe("pipe");

static void
pipeRead(void* ptr,
/* [<][>][^][v][top][bottom][index][help] */
         void*)
{
    ((Pipe*) ptr)->read();
}

static void
pipeWrite(void* ptr,
/* [<][>][^][v][top][bottom][index][help] */
          void*)
{
    ((Pipe*) ptr)->write();
}

static void
pipeTimeout(void* ptr,
/* [<][>][^][v][top][bottom][index][help] */
            void*)
{
    ((Pipe*) ptr)->timeout();
}

Pipe::Pipe(ListenSocket* s,
/* [<][>][^][v][top][bottom][index][help] */
           const TimeShort& t)
        : ListNode()
{
    Handler     readH(pipeRead, this);
    Handler     nullH((CallBackFunc)NULL, (void *)NULL);
    Handler     tH(pipeTimeout, this);
    TimeLong    at;

    incoming = new Buffer(ReadBufferSize);
    if (s->accept(&socket, address, &port, readH, nullH) < 0) {
        return;         // XXX: cleanup?
    }

    timer = (Timer *)NULL;
    gotsome = false;
    connected = true;
    interval = t;
    passive = true;
    logicallyDeleted = false;

    if (interval < InfiniteInterval) {
        dispatcher.systemClock.sync();
        at = dispatcher.systemClock;
        at = at + interval;
        timer = new Timer(tH, at);
    }

    TRACE(tracePipe,
          "new passive end of pipe for %s:%u\n",
          address.name(), port);
    return;
}

Pipe::Pipe(const Address& addr,
/* [<][>][^][v][top][bottom][index][help] */
           Port p)
        : ListNode()
{
    Handler     rh((CallBackFunc)NULL, (void *)NULL);
    Handler     wh(pipeWrite, this);
    
    incoming = new Buffer(ReadBufferSize);
    address = addr; 
    port = p;
    connected = false;

    passive = false;
    gotsome = false;
    interval = InfiniteInterval;
    timer = (Timer *)NULL;
    logicallyDeleted = false;

    socket = new StreamSocket(address, port, rh, wh);
    TRACE(tracePipe,
          "new active end for pipe to %s:%u\n",
          address.name(),
          port);
    return;
}

Pipe::~Pipe()
/* [<][>][^][v][top][bottom][index][help] */
{
    TRACE(tracePipe,
          "deleting server %s:%u\n",
          address.name(),
          port);
    
    if (timer) {
        delete timer;
    }
    delete socket;
    delete incoming;
    outgoing.clear();
}

void
Pipe::read()
/* [<][>][^][v][top][bottom][index][help] */
{
    int         length;

    ASSERT(!logicallyDeleted);

    gotsome = true;
    length = socket->read((char *) (incoming->contents + incoming->size),
                              incoming->capacity - incoming->size);

    if (length == 0 || length == FileOpHardError) {
        terminate();
        return;
    } else if (length == FileOpSoftError) {
        return;
    }

    TRACE(tracePipe,
          "read %u bytes from %s:%d, size %u\n",
          length, address.name(), port, incoming->size);

    // Check if we can flush what we've read so far
    incoming->size += length;
    receive();

    // If we have exceeded buffer capacity...
    if (incoming->size == incoming->capacity) {
        Buffer*         old = incoming;

        incoming = new Buffer((old->capacity << 1));
        incoming->append(old->contents, old->size);
        delete old;
    }

    return;
}

void
Pipe::write()
/* [<][>][^][v][top][bottom][index][help] */
{
    int         length;
    Handler     rh(pipeRead, this);
    Handler     nh((CallBackFunc)NULL, (void *)NULL);
    Buffer*     send;

    if (!connected) {
        TRACE(tracePipe, "client connected to %s:%u\n", 
              address.name(), port);

        connected = true;
        if (outgoing.isEmpty()) {
            socket->setHandlers((logicallyDeleted) ? nh : rh, nh);
        }
        return;
    }

    gotsome = true;
    send = outgoing.head();
    length = socket->write((char *) (send->contents + send->offset),
                           send->size - send->offset);

    if (length < 0) {
        switch (length) {
            case FileOpHardError:
                terminate();
                return;
            case FileOpSoftError:
                return;
            default:
                ASSERT(false);
        }
    }

    if (length == 0) {
        TRACE(tracePipe,
              "remote end %s:%u closed connection\n",
              address.name(), port);
        terminate();
        return;
    }

    TRACE(tracePipe,
          "wrote %u bytes to %s:%u\n",
          length,
          address.name(),
          port);
    
    send->offset += length;
    if (send->offset == send->size) {
        TRACE(tracePipe,
              "completed send of length %u on %s:%u\n",
              send->size, address.name(), port);
        
        outgoing.remove(send);
        delete send;
        if (outgoing.isEmpty()) {
            if (logicallyDeleted) {
                TRACE(tracePipe, "closing logically deleted pipe %s:%u\n",
                      address.name(), port);
                terminate();
                return;
            } else {
                socket->setHandlers(rh, nh);
            }
        }
    }
    return;
}

void
Pipe::send(Buffer *buf)
/* [<][>][^][v][top][bottom][index][help] */
{
    Handler     rh(pipeRead, this);
    Handler     wh(pipeWrite, this);
    
    // Can't call send after deleting pipe
    if (logicallyDeleted) {
      delete buf;
      return;
    }

    buf->offset = 0;
    gotsome = true;
    if (outgoing.isEmpty()) {
        socket->setHandlers(rh, wh);
    }
    outgoing.append(buf);
    
    TRACE(tracePipe,
          "queued response of length %u on %s:%u\n",
          buf->size, address.name(), port);
    return;
}

void
Pipe::timeout()
/* [<][>][^][v][top][bottom][index][help] */
{
    Handler     th(pipeTimeout, this);
    TimeLong    at;
    
    timer = (Timer *)NULL;
    if (!gotsome) {
        TRACE(tracePipe,
              "no activity on connection to %s:%u, closing\n",
              address.name(), port);
        terminate();
        return;
    }

    gotsome = false;
    dispatcher.systemClock.sync();
    at = dispatcher.systemClock;
    at = at + interval;
    timer = new Timer(th, at);
    return;
}

void
Pipe::flush(int length)
/* [<][>][^][v][top][bottom][index][help] */
{
    Buffer*     buf;
    int         rlen;

    // Create a new buffer, and move up remaining data
    rlen = incoming->size - length;
    ASSERT(rlen >= 0);
    TRACE(tracePipe,
          "flushing %d bytes from incoming buffer for %s:%u\n",
          length, address.name(), port);
    if (rlen == 0) {
      incoming->size = 0;
      return;
    }
    buf = new Buffer(ReadBufferSize >? rlen);
    buf->append(incoming->contents + length, rlen);
    delete incoming;
    incoming = buf;

    return;
}

void
Pipe::flushAndClose()
/* [<][>][^][v][top][bottom][index][help] */
{
    Handler     nh((CallBackFunc) NULL, (void*) NULL);
    Handler     wh(pipeWrite, this);

    logicallyDeleted = true;
    if (outgoing.isEmpty()) {
        terminate();
        return;
    } 
    socket->setHandlers(nh, wh);        // Prevent reads
    return;
}

//  Copyright (c) 1994 by the University of Southern California.
//  All rights reserved.
//
//  Permission to use, copy, modify, and distribute this software and
//  its documentation in source and binary forms for lawful
//  non-commercial purposes and without fee is hereby granted, provided
//  that the above copyright notice appear in all copies and that both
//  the copyright notice and this permission notice appear in supporting
//  documentation, and that any documentation, advertising materials,
//  and other materials related to such distribution and use acknowledge
//  that the software was developed by the University of Southern
//  California and/or Information Sciences Institute.
//  The name of the University of Southern California may not
//  be used to endorse or promote products derived from this software
//  without specific prior written permission.
//
//  THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
//  ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.  THIS SOFTWARE IS
//  PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
//  INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
//  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND 
//  NON-INFRINGEMENT.
//
//  IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
//  SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
//  TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
//  THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
//  Questions concerning this software should be directed to 
//  info-ra@isi.edu.
//


/* [<][>][^][v][top][bottom][index][help] */