Package net.sf.cindy.session.dispatcher

Source Code of net.sf.cindy.session.dispatcher.DefaultDispatcher$Worker

/*
* Copyright 2004-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.cindy.session.dispatcher;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

import net.sf.cindy.Session;
import net.sf.cindy.util.Configuration;
import net.sf.cindy.util.ElapsedTime;
import net.sf.cindy.util.LogThreadGroup;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import edu.emory.mathcs.backport.java.util.concurrent.ArrayBlockingQueue;
import edu.emory.mathcs.backport.java.util.concurrent.BlockingQueue;
import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger;

/**
* Default dispatcher implementation, support block operation such as
* Future.complete.
*
* @author <a href="chenrui@gmail.com">Roger Chen</a>
* @version $id$
*/
public class DefaultDispatcher implements Dispatcher {

    private static final Log log = LogFactory.getLog(DefaultDispatcher.class);

    private static final ThreadGroup THREAD_GROUP = new ThreadGroup(
            LogThreadGroup.CINDY_THREAD_GROUP, "Dispatcher");
    private static final AtomicInteger COUNTER = new AtomicInteger();

    private final Dispatcher dispatcher = new DirectDispatcher();

    private final int capacity = Math.max(1, Configuration
            .getDispatcherCapacity());
    private final int keepAliveTime = Math.max(0, Configuration
            .getDispatcherKeepAliveTime());
    private final int concurrent = Math.max(1, Configuration
            .getDispatcherConcurrent());

    private final Object mainLock = new Object();
    private final List idleWorkers = new LinkedList();
    private final Map sessionMap = new WeakHashMap();

    private final Worker[] activeWorkers = new Worker[concurrent];
    private int currentConcurrent = 0;

    private int indexOf(Worker worker) {
        for (int i = 0; i < concurrent; i++) {
            if (activeWorkers[i] == worker)
                return i;
        }
        return -1;
    }

    /**
     * Dispatch session events.
     *
     * @author <a href="chenrui@gmail.com">Roger Chen</a>
     * @version $id$
     */
    private class Worker extends Thread {

        private volatile BlockingQueue queue;
        private volatile boolean blocked;

        public Worker() {
            super(THREAD_GROUP, "Dispatcher-" + COUNTER.incrementAndGet());
        }

        private void setQueue(BlockingQueue queue) {
            this.queue = (queue == null ? new ArrayBlockingQueue(capacity)
                    : queue);
        }

        private Runnable getTask() {
            Runnable runnable = null;
            try {
                runnable = (Runnable) queue.poll(keepAliveTime,
                        TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
            }

            // similar double-checked locking
            if (runnable == null) {
                synchronized (mainLock) {
                    runnable = (Runnable) queue.poll();
                    if (runnable == null) {
                        // thus getWorker method will not use this thread
                        blocked = true;

                        activeWorkers[indexOf(this)] = null;
                        currentConcurrent--;
                    }
                }
            }
            return runnable;
        }

        public void run() {
            for (Runnable task = null; (task = getTask()) != null; task = null) {
                try {
                    task.run();
                } catch (Throwable e) { // protect catch
                    log.error(e, e);
                }

                if (blocked) {
                    synchronized (mainLock) {
                        queue = null;
                        idleWorkers.add(this);

                        synchronized (this) {
                            try {
                                wait(keepAliveTime);
                            } catch (InterruptedException e) {
                            }
                        }

                        if (idleWorkers.remove(this)) // timeout
                            break;
                        else
                            blocked = false; // have new task
                    }
                }
            }
        }

    }

    private int index = -1; // choose worker index

    private Worker getWorker(Session session) {
        // get last worker
        Worker worker = (Worker) sessionMap.get(session);
        if (worker != null && !worker.blocked)
            return worker;

        if (currentConcurrent >= concurrent) {
            // reach concurrent limit
            index = (index + 1) % concurrent;
            worker = activeWorkers[index];
        } else {
            // create new worker
            worker = newWorker(null);
            activeWorkers[indexOf(null)] = worker;
            currentConcurrent++;
        }

        sessionMap.put(session, worker);
        return worker;
    }

    private Worker newWorker(BlockingQueue queue) {
        Worker worker = null;
        if (idleWorkers.isEmpty()) {
            worker = new Worker();
            worker.setQueue(queue);
            worker.start();
        } else { // reuse idle worker
            worker = (Worker) idleWorkers.remove(idleWorkers.size() - 1);
            worker.setQueue(queue);
            synchronized (worker) {
                worker.notify();
            }
        }
        return worker;
    }

    private final ElapsedTime elapsedTime = new ElapsedTime();

    public void dispatch(Session session, Runnable event) {
        Worker worker = null;
        synchronized (mainLock) {
            worker = getWorker(session);
        }
        if (Thread.currentThread() == worker) {
            dispatcher.dispatch(session, event);
        } else {
            BlockingQueue queue = worker.queue;
            if (!queue.offer(event)) {
                // flow control
                if (elapsedTime.getElapsedTime() >= 10000) {
                    elapsedTime.reset();
                    log.warn("dispatcher flow control");
                }
                try {
                    queue.put(event);
                } catch (InterruptedException e) {
                }
            }
        }
    }

    public void block() {
        Thread currentThread = Thread.currentThread();
        if (currentThread instanceof Worker) {
            Worker worker = (Worker) currentThread;
            worker.blocked = true;

            synchronized (mainLock) {
                Worker newWorker = newWorker(worker.queue);
                activeWorkers[indexOf(worker)] = newWorker;
            }
        } else {
            dispatcher.block();
        }
    }

}
TOP

Related Classes of net.sf.cindy.session.dispatcher.DefaultDispatcher$Worker

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.