view dreactor/core/Dispatcher.d @ 9:5412a1ff2e49

adding chat client and more updates
author rick@minifunk
date Sat, 12 Jul 2008 10:42:41 -0400
parents 287ba7de97c4
children 5836613d16ac
line wrap: on
line source

module dreactor.core.Dispatcher;

import tango.io.selector.model.ISelector;
import tango.util.collection.CircularSeq;
import tango.net.Socket;
public  import  tango.core.Exception;
import dreactor.transport.AsyncSocketConduit;

import tango.util.log.Log;
import tango.util.log.Config;

alias bool delegate(Dispatcher) RegisterD;

alias int delegate(Dispatcher)   IncomingHandlerD;
alias int delegate(Dispatcher)   OutgoingHandlerD;
alias int delegate(Dispatcher, RegisterD)    ErrorHandlerD;
alias int delegate(Dispatcher)   DisconnectHandlerD;
alias int delegate(Conduit, RegisterD)  ConnectHandlerD;

alias int function(Dispatcher)   IncomingHandlerF;
alias int function(Dispatcher)   OutgoingHandlerF;
alias int function(Dispatcher, RegisterD)    ErrorHandlerF;
alias int function(Dispatcher)   DisconnectHandlerF;
alias int function(Conduit, RegisterD)  ConnectHandlerF;


/******************************************************************************
    Dispatcher object. To be used by the SelectLoop to manage callbacks 
    for events. It may also be used to buffer data inbetween requests.
    These can be populated passed to a SelectLoop directly by the end user, 
    or may be managed by a chosen Protocol. 
******************************************************************************/
class Dispatcher
{
 public 
    enum State { init, connected, listening, idle,  closing };
  
    /**************************************************************************

        Standard Ctor, takes a transport_

    **************************************************************************/ 
    this (Conduit trans, bool listener = false)
    {
        transport_ = trans;
        ibuf_len = 0;
        i_offset = 0;
        o_offset = 0;
        out_buffers = new CircularSeq!(char[]);
        log = Log.lookup("dreactor.core.Dispatcher");
    }
    
    /**********************************************************************
    
        Setters for the handlers. These are set by the Protocols as well 
    
    **********************************************************************/
    
    void setIncomingHandler(IncomingHandlerD hand)
    {
        inD = hand;
        inF = null;
    }

    void setIncomingHandler(IncomingHandlerF hand)
    {
        inF = hand;
        inD = null;
    }

    void setOutgoingHandler(OutgoingHandlerD hand)
    {
        outD = hand;
        outF = null;
    }

    void setOutgoingHandler(OutgoingHandlerF hand)
    {
        outF = hand;
        outD = null;
    }

    void setErrorHandler(ErrorHandlerD hand)
    {
        errD = hand;
        errF = null;
    }

    void setErrorHandler(ErrorHandlerF hand)
    {
        errF = hand;
        errD = null;
    }

    void setDisconnectHandler(DisconnectHandlerD hand)
    {
        disD = hand;
        disF = null;
    }

    void setDisconnectHandler(DisconnectHandlerF hand)
    {
        disF = hand;
        disD = null;
    }

    void setConnectHandler(ConnectHandlerD hand)
    {
        conD = hand;
        conF = null;
    }

    void setConnectHandler(ConnectHandlerF hand)
    {
        conF = hand;
        conD = null;
    }

    /**********************************************************************
        
        Handlers to be called by the SelectLoop when events occur
    
    **********************************************************************/
    int handleIncoming()
    {
        if (inD !is null)
            return inD(this);
        else if (inF !is null)
            return inF(this);
        else 
            throw new Exception("no Incoming handler set");
    }

    int handleOutgoing()
    {
        if (outD !is null)
            return outD(this);
        else if (outF !is null)
            return outF(this);
        else 
            throw new Exception("no Outgoing handler set");
    }

    int handleError(RegisterD reg)
    {
        if (errD !is null)
            return errD(this, reg);
        else if (errF !is null)
            return errF(this, reg);
    }

    int handleDisconnect()
    {
        if (disD !is null)
            return disD(this);
        else if (disF !is null)
            return disF(this);
    }

    int handleConnection(Conduit cond, RegisterD reg )
    {
        if (conD !is null)
        {
            return conD(cond, reg);
        }
        else if (conF !is null)
        {
            return conF(cond, reg);
        }
    }

    /**************************************************************************

        Sending / Receiving helpers

    **************************************************************************/

    /**************************************************************************
    
        appendOutBuffer

        Adds an outgoing buffer to the list. This returns true if the list
        was empty, indicating that the handler should be registered with the
        SelectLoop. If it returns false, it was probably already registered.
        
    **************************************************************************/
    synchronized bool appendOutBuffer(char[] outbuf)
    {
        out_buffers.append(outbuf);
        out_buffers_len++;
        if (out_buffers_len == 1)
            return true;
        else
            return false;
    }
   
    /**************************************************************************

        addOffset 
        Use this function to update the offset position after a successful data
        send. This not only manages the current offset, but will update the 
        out buffer chain if necessary. 

        Returns: false if there is nothing left to send, true if there is.

    **************************************************************************/ 
    synchronized bool addOffset(int off)
    in
    {
        assert(out_buffers_len > 0);
    }
    body
    {
        char[] hd = out_buffers.head();
        if ((off + o_offset) >= hd.length)
        {
            out_buffers.removeHead();
            o_offset = 0;
            out_buffers_len--;
            return (out_buffers_len > 0);
        }
        else
            o_offset += off;
        return true;
    }

    /**************************************************************************

        char[] nextBuffer

        Returns a slice of the current outbound buffer, returns a char[] pointing
        to null if there is no current outbound buffer

    **************************************************************************/
    synchronized char[] nextBuffer()
    {
        if (out_buffers_len < 1)
        {
            return null; 
        }

        return out_buffers.head()[o_offset .. $];
    }

    /**************************************************************************

        listen
        Enable listening on the socket attached to this connectionhandler

    **************************************************************************/
    int listen(IPv4Address addr)
    {
        (cast(AsyncSocketConduit)transport_).bind(addr).listen();
        state_ = State.listening;
        return 0;
    }

    Conduit transport()
    {
        return transport_;
    }
    /**************************************************************************

        Configuration functions

    **************************************************************************/
    Event events()
    {
        return events_;
    }
    void events(Event e)
    {
        events_ = e;
    }
    void addEvent(Event e)
    {
        events_ |= e;
    }
    void remEvent(Event e)
    {
        events_ &= ~e;
    }

    State getState() {return state_;}
    
    /*
       connection handlers are left out of this because 
       this method is used by the listener socket to pass 
       on its handlers to the accepted socket. An accepted
       socket will generally do different things onConnection
    */
    void setHandlers(Dispatcher other)
    {
        inD  = other.inD; 
        outD = other.outD;
        errD = other.errD;
        disD = other.disD;
        inF  = other.inF;
        outF = other.outF;
        errF = other.errF;
        disF = other.disF;
    }

    /**************************************************************************
    
        Freelist allocators and deallocators

    **************************************************************************/
    static synchronized Dispatcher New(Conduit tran, Dispatcher other = null)
    {
        Dispatcher hand;
	    if (freelist)
	    {   
            hand = freelist;
	        freelist = hand.next;
            hand.transport_ = tran;
	    }
	    else
	        hand = new Dispatcher(tran);

        if (!(other is null))
        {
            hand.setHandlers(other);
        }
	    return hand;
    }

    static synchronized void Delete(Dispatcher hand)
    {   
        hand.next = freelist;
        freelist = hand.initialize();
    } 

private
   
    char[] in_buffer; 
    CircularSeq!(char[]) out_buffers;
    int out_buffers_len;
    int ibuf_len;
    int i_offset;
    int o_offset;
    Logger log; 

    package Conduit transport_;
    State state_;
    Event events_; 
    IncomingHandlerD    inD;
    OutgoingHandlerD    outD;
    ErrorHandlerD       errD;
    DisconnectHandlerD  disD;
    ConnectHandlerD     conD;

    IncomingHandlerF    inF;
    OutgoingHandlerF    outF;
    ErrorHandlerF       errF;
    DisconnectHandlerF  disF;
    ConnectHandlerF     conF;
    
    static Dispatcher freelist;
    Dispatcher next;

    /**************************************************************************
        Copy ctor, creates a new Dispatcher using the settings
        of an existing handler. 
    **************************************************************************/ 
    Dispatcher initialize()
    {
        transport_ = null;
        state_ = State.init;
        ibuf_len = 0;
        i_offset = 0;
        o_offset = 0;
        out_buffers.clear();
        inD  = null;
        outD = null;
        errD = null;
        disD = null;
        conD = null;
        inF  = null;
        outF = null;
        errF = null;
        disF = null;
        conF = null;
        return this;
    }
}