Package kilim

Source Code of kilim.Cell

/* Copyright (c) 2006, Sriram Srinivasan
*
* You may distribute this software under the terms of the license
* specified in the file "License"
*/

package kilim;

import java.util.LinkedList;
import java.util.TimerTask;

/**
* A cell is a single-space buffer that supports multiple producers and a single
* consumer, functionally identical to Mailbox bounded to a size of 1 (and hence
* optimized for this size)
*/

public class Cell<T> implements PauseReason, EventPublisher {
    T message;
    EventSubscriber sink;
   
    public static final int SPACE_AVAILABLE = 1;
    public static final int MSG_AVAILABLE = 2;
    public static final int TIMED_OUT = 3;
    public static final Event spaceAvailble = new Event(MSG_AVAILABLE);
    public static final Event messageAvailable = new Event(SPACE_AVAILABLE);
    public static final Event timedOut = new Event(TIMED_OUT);
   
    LinkedList<EventSubscriber> srcs = new LinkedList<EventSubscriber>();

    // DEBUG stuff
    // To do: move into monitorable stat object
    /*
     * public int nPut = 0; public int nGet = 0; public int nWastedPuts = 0;
     * public int nWastedGets = 0;
     */
    public Cell() {
    }

    /**
     * Non-blocking, nonpausing get.
     * @param eo. If non-null (and if there is no message), registers this observer. The observer is notified with a
     * MessageAvailable event when a put() is done.
     * 
     * @return buffered message if there's one, or null
     */
    public T get(EventSubscriber eo) {
        EventSubscriber producer = null;
        T ret;
        synchronized(this) {
            if (message == null) {
                ret = null;
                addMsgAvailableListener(eo);
            } else {
                ret = message;
                message = null;
                if (srcs.size() > 0) {
                    producer = srcs.poll();
                }
            }
        }
        if (producer != null)  {
            producer.onEvent(this, spaceAvailble);
        }
        return ret;
    }
   
    /**
     * Non-blocking, nonpausing put.
     * @param eo. If non-null, registers this observer and calls it with an SpaceAvailable event
     * when there's space.
     * @return buffered message if there's one, or null
     */
    public boolean put(T amsg, EventSubscriber eo) {
        boolean ret = true; // assume we'll be able to enqueue
        EventSubscriber subscriber;
        synchronized(this) {
            if (amsg == null) {
                throw new NullPointerException("Null message supplied to put");
            }
            if (message == null) { // space available
                message = amsg;
                subscriber = sink;
                sink = null;
            } else {
                ret = false;
                // unable to enqueue. Cell is full
                subscriber = null;
                if (eo != null) {
                    srcs.add(eo);
                }
            }
        }
        // notify get's subscriber that something is available
        if (subscriber != null) {
            subscriber.onEvent(this, messageAvailable);
        }
        return ret;
    }
   
    /**
     * Get, don't pause or block.
     *
     * @return stored message, or null if no message found.
     */
    public T getnb() {
        return get(null);
    }

    /**
     * @return non-null message.
     * @throws Pausable
     */
    public T get() throws Pausable{
        Task t = Task.getCurrentTask();
        T msg = get(t);
        while (msg == null) {
            Task.pause(this);
            removeMsgAvailableListener(t);
            msg = get(t);
        }
        return msg;
    }

   
    /**
     * @return non-null message.
     * @throws Pausable
     */
    public T get(long timeoutMillis) throws Pausable {
        final Task t = Task.getCurrentTask();
        T msg = get(t);
        long begin = System.currentTimeMillis();
        while (msg == null) {
            TimerTask tt = new TimerTask() {
                    public void run() {
                        Cell.this.removeMsgAvailableListener(t);
                        t.onEvent(Cell.this, timedOut);
                    }
                };
            Task.timer.schedule(tt, timeoutMillis);
            Task.pause(this);
            tt.cancel();
            if (System.currentTimeMillis() - begin > timeoutMillis) {
                break;
            }
            removeMsgAvailableListener(t);
            msg = get(t);
        }
        return msg;
    }
   
    public synchronized void addSpaceAvailableListener(EventSubscriber spcSub) {
            srcs.add(spcSub);
        }

    public synchronized void removeSpaceAvailableListener(EventSubscriber spcSub) {
            srcs.remove(spcSub);
        }


    public synchronized void addMsgAvailableListener(EventSubscriber msgSub) {
            if (sink != null) {
                throw new AssertionError(
                  "Error: A mailbox can not be shared by two consumers.  New = "
                  + msgSub + ", Old = " + sink);
            }
            sink = msgSub;
        }

    public synchronized void removeMsgAvailableListener(EventSubscriber msgSub) {
            if (sink == msgSub) {
                sink = null;
            }
        }

    public boolean putnb(T msg) {
        return put(msg, null);
    }

    public void put(T msg) throws Pausable {
        Task t = Task.getCurrentTask();
        while (!put(msg, t)) {
            Task.pause(this);
            removeSpaceAvailableListener(t);
        }
    }

    public boolean put(T msg, int timeoutMillis) throws Pausable {
        final Task t = Task.getCurrentTask();
        long begin = System.currentTimeMillis();
        while (!put(msg, t)) {
            TimerTask tt = new TimerTask() {
                    public void run() {
                        Cell.this.removeSpaceAvailableListener(t);
                        t.onEvent(Cell.this, timedOut);
                    }
                };
            Task.timer.schedule(tt, timeoutMillis);
            Task.pause(this);
            removeSpaceAvailableListener(t);
            if (System.currentTimeMillis() - begin >= timeoutMillis) {
                return false;
            }
        }
        return true;
    }
   
    public void putb(T msg) {
        putb(msg, 0 /* infinite wait */);
    }

    public class BlockingSubscriber implements EventSubscriber {
        public volatile boolean eventRcvd = false;
        public void onEvent(EventPublisher ep, Event e) {
            synchronized (Cell.this) {
                eventRcvd = true;
                Cell.this.notify();
            }
        }
        public void blockingWait(final long timeoutMillis) {
            long start = System.currentTimeMillis();
            long remaining = timeoutMillis;
            boolean infiniteWait = timeoutMillis == 0;
            synchronized (Cell.this) {
                while (!eventRcvd && (infiniteWait || remaining > 0)) {
                    try {
                        Cell.this.wait(infiniteWait? 0 : remaining);
                    } catch (InterruptedException ie) {}
                    long elapsed = System.currentTimeMillis() - start;
                    remaining -= elapsed;
                }
            }
        }
    }
   
    public void putb(T msg, final long timeoutMillis) {
        BlockingSubscriber evs = new BlockingSubscriber();
        if (!put(msg, evs)) {
            evs.blockingWait(timeoutMillis);
        }
        if (!evs.eventRcvd) {
            removeSpaceAvailableListener(evs);
        }
    }

    public synchronized boolean hasMessage() {
            return message != null;
        }

    public synchronized boolean hasSpace() {
            return message == null;
        }

    /**
     * retrieve a message, blocking the thread indefinitely. Note, this is a
     * heavyweight block, unlike #get() that pauses the Fiber but doesn't block
     * the thread.
     */

    public T getb() {
        return getb(0);
    }

    /**
     * retrieve a msg, and block the Java thread for the time given.
     *
     * @param millis.
     *            max wait time
     * @return null if timed out.
     */
    public T getb(final long timeoutMillis) {
        BlockingSubscriber evs = new BlockingSubscriber();
        T msg;
       
        if ((msg = get(evs)) == null) {
            evs.blockingWait(timeoutMillis);
            if (evs.eventRcvd) {
                msg = get(null); // non-blocking get.
                assert msg  != null: "Received event, but message is null";
            }
        }
        if (msg == null) {
            removeMsgAvailableListener(evs);
        }
        return msg;
    }

    public synchronized String toString() {
            return "id:" + System.identityHashCode(this) + " " + message;
        }

    // Implementation of PauseReason
    public boolean isValid(Task t) {
        synchronized(this) {
            return (t == sink) || srcs.contains(t);
        }
    }
}
TOP

Related Classes of kilim.Cell

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.