netmanager.cpp   [plain text]


/*
 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
 * 
 * The contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License. Please obtain
 * a copy of the License at http://www.apple.com/publicsource and read it before
 * using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
 * specific language governing rights and limitations under the License.
 */


//
// manager - network protocol core manager class
//
#include "netmanager.h"
#include "protocol.h"
#include "transfer.h"
#include "netconnection.h"
#include "neterror.h"


namespace Security {
namespace Network {


Manager::Manager() : mActiveTransfers(0), mRetainConnections(true), mObserver(NULL)
{
}

Manager::~Manager()
{
    //@@@ cleanup, s'il vous plait:
    //@@@ abort transfers and destroy them
    //@@@ notify any observers
    //@@@ destroy protocol objects
}


//
// Add a new Transfer to this Manager.
// This does not start it; it'll just sit around until started.
//
void Manager::add(Transfer *xfer)
{
    assert(xfer->state() == Transfer::cold);
    mTransfers.insert(xfer);
    xfer->mState = Transfer::warm;
}


//
// Remove a Transfer from this Manager.
// You can remove a pre-active Transfer, or one that has finished or failed.
// You can't remove an active Transfer - abort it first.
//
void Manager::remove(Transfer *xfer)
{
    assert(mTransfers.find(xfer) != mTransfers.end());	// is ours
    assert(xfer->state() != Transfer::active);
    mTransfers.erase(xfer);
}


//
// Start a Transfer. It must already have been added, and must be in a pre-active state.
//
void Manager::start(Transfer *xfer)
{
    assert(mTransfers.find(xfer) != mTransfers.end());	// is ours
    assert(xfer->state() == Transfer::warm);
    try {
        xfer->start();
        xfer->mState = Transfer::active;
        xfer->observe(Observer::transferStarting);
        mActiveTransfers++;
        debug("netmanager", "%ld active transfers", mActiveTransfers);
    } catch (...) {
        xfer->mState = Transfer::failed;
        debug("netmanager", "Transfer %p failed to start", xfer);
        throw;
    }
}


//
// Abort a Transfer.
// If it is active, try to make it stop as soon as it's safe. This may return while
// the Transfer's state is still active; it will eventually switch to failed unless it
// happened to succeed before we got to it (in which case it'll be finished).
// You can't abort a Transfer that isn't active.
//@@@ Phasing problem? Perhaps aborting non-active Transfers should be
//@@@ allowed (and ignored or flagged).
//
void Manager::abort(Transfer *xfer)
{
    assert(mTransfers.find(xfer) != mTransfers.end());	// is ours
    switch (xfer->state()) {
    case Transfer::active:
        try {
            debug("netmanager", "xfer %p request abort", xfer);
            xfer->abort();
        } catch (...) {
            debug("netmanager", "xfer %p failed to abort; forcing the issue", xfer);
            xfer->Transfer::abort();
        }
        break;
    case Transfer::finished:
    case Transfer::failed:
        // no longer running; ignore cancel request
        debug("netmanager", "xfer %p abort ignored (already done)", xfer);
        break;
    default:
        assert(false);		// mustn't call in this state
    }
}


//
// Do bookkeeping for a Transfer that wants to be done.
// This method can only be called from a Transfer that belongs
// to this Manager and was started.
//
void Manager::done(Transfer *xfer)
{
    assert(mTransfers.find(xfer) != mTransfers.end());	// is ours
    assert(xfer->state() == Transfer::finished || xfer->state() == Transfer::failed);
    assert(mActiveTransfers > 0);
    mActiveTransfers--;
    debug("netmanager", "%ld active transfers", mActiveTransfers);
}


//
// Manage engine clients on behalf of active Transfers.
//@@@ Currently the API doesn't specify which Transfer these belong to.
//@@@ Perhaps it should.
//
void Manager::addIO(TransferEngine::Client *client)
{
    mEngine.add(client);
}

void Manager::removeIO(TransferEngine::Client *client)
{
    mEngine.remove(client);
}


//
// Manage Connections on behalf of Transfers (and perhaps Protocols)
//
void Manager::retainConnection(Connection *connection)
{
    if (mRetainConnections)
        mConnections.retain(connection);
    else
        closeConnection(connection);
}

void Manager::closeConnection(Connection *connection)
{
    mConnections.remove(connection);
    mMorgue.insert(connection);
}


//
// Try to find a live retained Connection for a HostTarget and return it.
//
Connection *Manager::pickConnection(const HostTarget &host)
{
    while (Connection *connection = mConnections.get(host)) {
        if (connection->validate()) {
            connection->restarting(true);	// mark restarting
            return connection;				// good to go
        }
        // if validate returned false, the connection has self-destructed (so ignore it)
        debug("netmanager", "%p connection %p failed to validate",
            this, connection);
    }
    return NULL;	// no joy, caller must make a new one
}


//
// Handle the global Connection cache
//
void Manager::reuseConnections(bool retain)
{
    mRetainConnections = retain;
}


void Manager::flushConnections()
{
    mConnections.purge();
}


//
// Timer management
//
void Manager::setTimer(Timer *timer, Time::Absolute when)
{
	mTimers.schedule(timer, when); 
}
	
void Manager::clearTimer(Timer *timer)
{
	if (timer->scheduled())
		mTimers.unschedule(timer); 
}


void Manager::runTimers()
{
    while (Timer *top = static_cast<Timer *>(mTimers.pop(Time::now()))) {
        debug("netmanager", "%p timer %p executing at %.3f",
            this, top, Time::now().internalForm());
        try {
            top->action();
            debug("machsrvtime", "%p timer %p done", this, top);
        } catch (...) {
            debug("machsrvtime",
                "%p server timer %p failed with exception", this, top);
        }
    }
}


//
// Perform a (small) incremental operations step.
//
void Manager::step()
{
    prepare();
    if (!mEngine.isEmpty()) {
        debug("mgrstep", "operations step");
        mEngine();
    }
}


//
// Run in this thread until a particular time (or until no more Transfers are active).
//
void Manager::run(Time::Absolute stopTime)
{
    debug("netmanager",
		"starting run with %ld active transfers", mActiveTransfers);
    while (mActiveTransfers > 0) {
        prepare();
        Time::Absolute limit = mTimers.empty() ? stopTime : min(stopTime, mTimers.next());
        mEngine(limit - Time::now());
        if (Time::now() > stopTime)
            break;
    }
    debug("netmanager", "ending run");
}

void Manager::run()
{
    run(Time::heatDeath());
}


//
// Internal stepper
//
void Manager::prepare()
{
    // clear the morgue
    if (!mMorgue.empty()) {
        debug("netmanager",
            "clearing morgue of %ld connections", mMorgue.size());
        for (set<Connection *>::iterator it = mMorgue.begin(); it != mMorgue.end(); it++)
            delete *it;
        mMorgue.erase(mMorgue.begin(), mMorgue.end());
    }
    
    // run pending timers
    runTimers();
}


}	// end namespace Network
}	// end namespace Security