/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.env.thread;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ThreadRing {
private static final Logger log = Logger.getLogger(ThreadRing.class.getName());
private final static int RING_SIZE = 16 * 1024;
private final ThreadPool _threadPool;
private final Item [] _ring;
private final AtomicInteger _head = new AtomicInteger();
private final AtomicInteger _tail = new AtomicInteger();
private final int _mask;
private final AtomicInteger _queueCount = new AtomicInteger();
private final AtomicInteger _idleCount = new AtomicInteger();
private final ClassLoader _classLoader = ThreadRing.class.getClassLoader();
private final RingTask _task = new RingTask();
private final int _loopCount = 1 * 1000;
public ThreadRing(ThreadPool threadPool)
{
_threadPool = threadPool;
_ring = new Item[RING_SIZE];
_mask = RING_SIZE - 1;
for (int i = 0; i < _ring.length; i++) {
_ring[i] = new Item();
}
}
public void schedule(Runnable task)
{
_queueCount.incrementAndGet();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
int head;
int nextHead;
do {
head = _head.get();
nextHead = (head + 1) & _mask;
int tail = _tail.get();
if (nextHead == tail) {
// overflow handled by the base thread
_queueCount.decrementAndGet();
_threadPool.schedule(task);
return;
}
} while (! _head.compareAndSet(head, nextHead));
Item item = _ring[head];
item.init(task, loader);
launch();
}
private Item nextThread()
{
int tail;
int nextTail;
int head;
do {
tail = _tail.get();
nextTail = (tail + 1) & _mask;
head = _head.get();
if (head == tail)
return null;
} while (! _tail.compareAndSet(tail, nextTail));
_queueCount.decrementAndGet();
return _ring[tail];
}
private void launch()
{
int idleCount;
do {
idleCount = _idleCount.get();
int queueCount = _queueCount.get();
if (queueCount < idleCount)
return;
} while (! _idleCount.compareAndSet(idleCount, idleCount + 1));
_threadPool.schedule(_task, _classLoader);
}
final class RingTask implements Runnable {
@Override
public final void run()
{
try {
int countMax = _loopCount;
int count = countMax;
while (count-- >= 0) {
Item item = nextThread();
if (item != null) {
count = countMax;
try {
_idleCount.decrementAndGet();
launch();
item.runTask();
} finally {
_idleCount.incrementAndGet();
}
}
}
} finally {
_idleCount.decrementAndGet();
}
}
}
static class Item {
private final AtomicReference<Runnable> _task
= new AtomicReference<Runnable>();
private final AtomicReference<ClassLoader> _loader
= new AtomicReference<ClassLoader>();
public final void init(Runnable task, ClassLoader loader)
{
if (! _loader.compareAndSet(null, loader))
throw new IllegalStateException();
if (! _task.compareAndSet(null, task))
throw new IllegalStateException();
}
public final void runTask()
{
Runnable task;
while ((task = _task.getAndSet(null)) == null) {
}
ClassLoader loader = _loader.getAndSet(null);
Thread thread = Thread.currentThread();
String oldName = thread.getName();
ClassLoader oldLoader = thread.getContextClassLoader();
try {
thread.setContextClassLoader(loader);
try {
Thread.interrupted();
task.run();
} catch (Throwable e) {
log.log(Level.WARNING, e.toString(), e);
}
} finally {
thread.setName(oldName);
thread.setContextClassLoader(oldLoader);
}
}
}
}