WIP
This commit is contained in:
parent
7b56928bca
commit
3ecb38b5b7
8 changed files with 322 additions and 212 deletions
|
|
@ -10,7 +10,131 @@ import atomic.{AtomicLong, AtomicInteger}
|
|||
import ThreadPoolExecutor.CallerRunsPolicy
|
||||
|
||||
import se.scalablesolutions.akka.actor.IllegalActorStateException
|
||||
import se.scalablesolutions.akka.util.{Logger, Logging}
|
||||
import se.scalablesolutions.akka.util. {Duration, Logger, Logging}
|
||||
|
||||
object ThreadPoolConfig {
|
||||
type Bounds = Int
|
||||
type FlowHandler = Either[RejectedExecutionHandler,Bounds]
|
||||
type QueueFactory = () => BlockingQueue[Runnable]
|
||||
|
||||
val defaultAllowCoreThreadTimeout: Boolean = false
|
||||
val defaultCorePoolSize: Int = 16
|
||||
val defaultMaxPoolSize: Int = 128
|
||||
val defaultTimeout: Duration = Duration(60000L,TimeUnit.MILLISECONDS)
|
||||
def defaultFlowHandler: FlowHandler = flowHandler(new CallerRunsPolicy)
|
||||
|
||||
def flowHandler(rejectionHandler: RejectedExecutionHandler): FlowHandler = Left(rejectionHandler)
|
||||
def flowHandler(bounds: Int): FlowHandler = Right(bounds)
|
||||
|
||||
def fixedPoolSize(size: Int): Int = size
|
||||
def scaledPoolSize(multiplier: Double): Int =
|
||||
(Runtime.getRuntime.availableProcessors * multiplier).ceil.toInt
|
||||
|
||||
def arrayBlockingQueue(capacity: Int, fair: Boolean): QueueFactory =
|
||||
() => new ArrayBlockingQueue[Runnable](capacity,fair)
|
||||
|
||||
def synchronousQueue(fair: Boolean): QueueFactory =
|
||||
() => new SynchronousQueue[Runnable](fair)
|
||||
|
||||
def linkedBlockingQueue(): QueueFactory =
|
||||
() => new LinkedBlockingQueue[Runnable]()
|
||||
|
||||
def linkedBlockingQueue(capacity: Int): QueueFactory =
|
||||
() => new LinkedBlockingQueue[Runnable](capacity)
|
||||
|
||||
def reusableQueue(queue: BlockingQueue[Runnable]): QueueFactory =
|
||||
() => queue
|
||||
|
||||
def reusableQueue(queueFactory: QueueFactory): QueueFactory = {
|
||||
val queue = queueFactory()
|
||||
() => queue
|
||||
}
|
||||
}
|
||||
|
||||
case class ThreadPoolConfig(allowCorePoolTimeout: Boolean = ThreadPoolConfig.defaultAllowCoreThreadTimeout,
|
||||
corePoolSize: Int = ThreadPoolConfig.defaultCorePoolSize,
|
||||
maxPoolSize: Int = ThreadPoolConfig.defaultMaxPoolSize,
|
||||
threadTimeout: Duration = ThreadPoolConfig.defaultTimeout,
|
||||
flowHandler: ThreadPoolConfig.FlowHandler = ThreadPoolConfig.defaultFlowHandler,
|
||||
queueFactory: ThreadPoolConfig.QueueFactory = ThreadPoolConfig.linkedBlockingQueue()) {
|
||||
|
||||
final def createExecutorService(threadFactory: ThreadFactory): ExecutorService = {
|
||||
flowHandler match {
|
||||
case Left(rejectHandler) =>
|
||||
val service = new ThreadPoolExecutor(corePoolSize, maxPoolSize, threadTimeout.length, threadTimeout.unit, queueFactory(), threadFactory, rejectHandler)
|
||||
service.allowCoreThreadTimeOut(allowCorePoolTimeout)
|
||||
service
|
||||
case Right(bounds) =>
|
||||
val service = new ThreadPoolExecutor(corePoolSize, maxPoolSize, threadTimeout.length, threadTimeout.unit, queueFactory(), threadFactory)
|
||||
service.allowCoreThreadTimeOut(allowCorePoolTimeout)
|
||||
new BoundedExecutorDecorator(service,bounds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait DispatcherBuilder {
|
||||
def build: MessageDispatcher
|
||||
}
|
||||
|
||||
case class ThreadPoolConfigDispatcherBuilder(dispatcherFactory: (ThreadPoolConfig) => MessageDispatcher, config: ThreadPoolConfig) extends DispatcherBuilder {
|
||||
import ThreadPoolConfig._
|
||||
def build = dispatcherFactory(config)
|
||||
|
||||
//TODO remove this, for backwards compat only
|
||||
def buildThreadPool = build
|
||||
|
||||
def withNewBoundedThreadPoolWithLinkedBlockingQueueWithUnboundedCapacity(bounds: Int): ThreadPoolConfigDispatcherBuilder =
|
||||
this.copy(config = config.copy(flowHandler = flowHandler(bounds), queueFactory = linkedBlockingQueue()))
|
||||
|
||||
def withNewThreadPoolWithCustomBlockingQueue(newQueueFactory: QueueFactory): ThreadPoolConfigDispatcherBuilder =
|
||||
this.copy(config = config.copy(flowHandler = defaultFlowHandler, queueFactory = newQueueFactory))
|
||||
|
||||
def withNewThreadPoolWithCustomBlockingQueue(queue: BlockingQueue[Runnable]): ThreadPoolConfigDispatcherBuilder =
|
||||
withNewThreadPoolWithCustomBlockingQueue(reusableQueue(queue))
|
||||
|
||||
def withNewThreadPoolWithLinkedBlockingQueueWithUnboundedCapacity: ThreadPoolConfigDispatcherBuilder =
|
||||
this.copy(config = config.copy(queueFactory = linkedBlockingQueue(), flowHandler = defaultFlowHandler))
|
||||
|
||||
def withNewThreadPoolWithLinkedBlockingQueueWithCapacity(capacity: Int): ThreadPoolConfigDispatcherBuilder =
|
||||
this.copy(config = config.copy(queueFactory = linkedBlockingQueue(capacity), flowHandler = defaultFlowHandler))
|
||||
|
||||
def withNewThreadPoolWithSynchronousQueueWithFairness(fair: Boolean): ThreadPoolConfigDispatcherBuilder =
|
||||
this.copy(config = config.copy(queueFactory = synchronousQueue(fair), flowHandler = defaultFlowHandler))
|
||||
|
||||
def withNewThreadPoolWithArrayBlockingQueueWithCapacityAndFairness(capacity: Int, fair: Boolean): ThreadPoolConfigDispatcherBuilder =
|
||||
this.copy(config = config.copy(queueFactory = arrayBlockingQueue(capacity,fair), flowHandler = defaultFlowHandler))
|
||||
|
||||
def setCorePoolSize(size: Int): ThreadPoolConfigDispatcherBuilder =
|
||||
this.copy(config = config.copy(corePoolSize = size))
|
||||
|
||||
def setMaxPoolSize(size: Int): ThreadPoolConfigDispatcherBuilder =
|
||||
this.copy(config = config.copy(maxPoolSize = size))
|
||||
|
||||
def setCorePoolSizeFromFactor(multiplier: Double): ThreadPoolConfigDispatcherBuilder =
|
||||
setCorePoolSize(scaledPoolSize(multiplier))
|
||||
|
||||
def setMaxPoolSizeFromFactor(multiplier: Double): ThreadPoolConfigDispatcherBuilder =
|
||||
setMaxPoolSize(scaledPoolSize(multiplier))
|
||||
|
||||
def setExecutorBounds(bounds: Int): ThreadPoolConfigDispatcherBuilder =
|
||||
this.copy(config = config.copy(flowHandler = flowHandler(bounds)))
|
||||
|
||||
def setKeepAliveTimeInMillis(time: Long): ThreadPoolConfigDispatcherBuilder =
|
||||
setKeepAliveTime(Duration(time,TimeUnit.MILLISECONDS))
|
||||
|
||||
def setKeepAliveTime(time: Duration): ThreadPoolConfigDispatcherBuilder =
|
||||
this.copy(config = config.copy(threadTimeout = time))
|
||||
|
||||
def setRejectionPolicy(policy: RejectedExecutionHandler): ThreadPoolConfigDispatcherBuilder =
|
||||
setFlowHandler(flowHandler(policy))
|
||||
|
||||
def setFlowHandler(newFlowHandler: FlowHandler): ThreadPoolConfigDispatcherBuilder =
|
||||
this.copy(config = config.copy(flowHandler = newFlowHandler))
|
||||
|
||||
def setAllowCoreThreadTimeout(allow: Boolean): ThreadPoolConfigDispatcherBuilder =
|
||||
this.copy(config = config.copy(allowCorePoolTimeout = allow))
|
||||
}
|
||||
|
||||
|
||||
trait ThreadPoolBuilder extends Logging {
|
||||
val name: String
|
||||
|
|
@ -26,13 +150,13 @@ trait ThreadPoolBuilder extends Logging {
|
|||
@volatile private var inProcessOfBuilding = false
|
||||
private var blockingQueue: BlockingQueue[Runnable] = _
|
||||
|
||||
private lazy val threadFactory = new MonitorableThreadFactory(name)
|
||||
protected lazy val threadFactory = new MonitorableThreadFactory(name)
|
||||
|
||||
protected var executor: ExecutorService = _
|
||||
@volatile var executor: ExecutorService = _
|
||||
|
||||
def isShutdown = executor.isShutdown
|
||||
|
||||
def buildThreadPool(): Unit = synchronized {
|
||||
def buildThreadPool(): ExecutorService = synchronized {
|
||||
ensureNotActive
|
||||
inProcessOfBuilding = false
|
||||
|
||||
|
|
@ -51,6 +175,7 @@ trait ThreadPoolBuilder extends Logging {
|
|||
} else {
|
||||
executor = threadPoolBuilder
|
||||
}
|
||||
executor
|
||||
}
|
||||
|
||||
def withNewThreadPoolWithCustomBlockingQueue(queue: BlockingQueue[Runnable]): ThreadPoolBuilder = synchronized {
|
||||
|
|
@ -204,104 +329,105 @@ trait ThreadPoolBuilder extends Logging {
|
|||
}
|
||||
|
||||
def ensureNotActive(): Unit
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class BoundedExecutorDecorator(val executor: ExecutorService, bound: Int) extends ExecutorService with Logging {
|
||||
protected val semaphore = new Semaphore(bound)
|
||||
|
||||
def execute(command: Runnable) = {
|
||||
semaphore.acquire
|
||||
try {
|
||||
executor.execute(new Runnable() {
|
||||
def run = {
|
||||
try {
|
||||
command.run
|
||||
} finally {
|
||||
semaphore.release
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch {
|
||||
case e: RejectedExecutionException =>
|
||||
semaphore.release
|
||||
case e =>
|
||||
log.error(e,"Unexpected exception")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class MonitorableThreadFactory(val name: String) extends ThreadFactory {
|
||||
protected val counter = new AtomicLong
|
||||
|
||||
// Delegating methods for the ExecutorService interface
|
||||
def shutdown = executor.shutdown
|
||||
def newThread(runnable: Runnable) =
|
||||
new MonitorableThread(runnable, name)
|
||||
// new Thread(runnable, name + "-" + counter.getAndIncrement)
|
||||
}
|
||||
|
||||
def shutdownNow = executor.shutdownNow
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object MonitorableThread {
|
||||
val DEFAULT_NAME = "MonitorableThread"
|
||||
val created = new AtomicInteger
|
||||
val alive = new AtomicInteger
|
||||
@volatile var debugLifecycle = false
|
||||
}
|
||||
|
||||
def isShutdown = executor.isShutdown
|
||||
// FIXME fix the issues with using the monitoring in MonitorableThread
|
||||
|
||||
def isTerminated = executor.isTerminated
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class MonitorableThread(runnable: Runnable, name: String)
|
||||
extends Thread(runnable, name + "-" + MonitorableThread.created.incrementAndGet) with Logging {
|
||||
|
||||
def awaitTermination(l: Long, timeUnit: TimeUnit) = executor.awaitTermination(l, timeUnit)
|
||||
setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||||
def uncaughtException(thread: Thread, cause: Throwable) =
|
||||
log.error(cause, "UNCAUGHT in thread [%s]", thread.getName)
|
||||
})
|
||||
|
||||
def submit[T](callable: Callable[T]) = executor.submit(callable)
|
||||
|
||||
def submit[T](runnable: Runnable, t: T) = executor.submit(runnable, t)
|
||||
|
||||
def submit(runnable: Runnable) = executor.submit(runnable)
|
||||
|
||||
def invokeAll[T](callables: Collection[_ <: Callable[T]]) = executor.invokeAll(callables)
|
||||
|
||||
def invokeAll[T](callables: Collection[_ <: Callable[T]], l: Long, timeUnit: TimeUnit) = executor.invokeAll(callables, l, timeUnit)
|
||||
|
||||
def invokeAny[T](callables: Collection[_ <: Callable[T]]) = executor.invokeAny(callables)
|
||||
|
||||
def invokeAny[T](callables: Collection[_ <: Callable[T]], l: Long, timeUnit: TimeUnit) = executor.invokeAny(callables, l, timeUnit)
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class MonitorableThreadFactory(val name: String) extends ThreadFactory {
|
||||
protected val counter = new AtomicLong
|
||||
|
||||
def newThread(runnable: Runnable) =
|
||||
new MonitorableThread(runnable, name)
|
||||
// new Thread(runnable, name + "-" + counter.getAndIncrement)
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object MonitorableThread {
|
||||
val DEFAULT_NAME = "MonitorableThread"
|
||||
val created = new AtomicInteger
|
||||
val alive = new AtomicInteger
|
||||
@volatile var debugLifecycle = false
|
||||
}
|
||||
|
||||
// FIXME fix the issues with using the monitoring in MonitorableThread
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class MonitorableThread(runnable: Runnable, name: String)
|
||||
extends Thread(runnable, name + "-" + MonitorableThread.created.incrementAndGet) with Logging {
|
||||
|
||||
setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||||
def uncaughtException(thread: Thread, cause: Throwable) =
|
||||
log.error(cause, "UNCAUGHT in thread [%s]", thread.getName)
|
||||
})
|
||||
|
||||
override def run = {
|
||||
val debug = MonitorableThread.debugLifecycle
|
||||
log.debug("Created thread %s", getName)
|
||||
try {
|
||||
MonitorableThread.alive.incrementAndGet
|
||||
super.run
|
||||
} finally {
|
||||
MonitorableThread.alive.decrementAndGet
|
||||
log.debug("Exiting thread %s", getName)
|
||||
}
|
||||
override def run = {
|
||||
val debug = MonitorableThread.debugLifecycle
|
||||
log.debug("Created thread %s", getName)
|
||||
try {
|
||||
MonitorableThread.alive.incrementAndGet
|
||||
super.run
|
||||
} finally {
|
||||
MonitorableThread.alive.decrementAndGet
|
||||
log.debug("Exiting thread %s", getName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class BoundedExecutorDecorator(val executor: ExecutorService, bound: Int) extends ExecutorService with Logging {
|
||||
protected val semaphore = new Semaphore(bound)
|
||||
|
||||
def execute(command: Runnable) = {
|
||||
semaphore.acquire
|
||||
try {
|
||||
executor.execute(new Runnable() {
|
||||
def run = {
|
||||
try {
|
||||
command.run
|
||||
} finally {
|
||||
semaphore.release
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch {
|
||||
case e: RejectedExecutionException =>
|
||||
semaphore.release
|
||||
case e =>
|
||||
log.error(e,"Unexpected exception")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
// Delegating methods for the ExecutorService interface
|
||||
def shutdown = executor.shutdown
|
||||
|
||||
def shutdownNow = executor.shutdownNow
|
||||
|
||||
def isShutdown = executor.isShutdown
|
||||
|
||||
def isTerminated = executor.isTerminated
|
||||
|
||||
def awaitTermination(l: Long, timeUnit: TimeUnit) = executor.awaitTermination(l, timeUnit)
|
||||
|
||||
def submit[T](callable: Callable[T]) = executor.submit(callable)
|
||||
|
||||
def submit[T](runnable: Runnable, t: T) = executor.submit(runnable, t)
|
||||
|
||||
def submit(runnable: Runnable) = executor.submit(runnable)
|
||||
|
||||
def invokeAll[T](callables: Collection[_ <: Callable[T]]) = executor.invokeAll(callables)
|
||||
|
||||
def invokeAll[T](callables: Collection[_ <: Callable[T]], l: Long, timeUnit: TimeUnit) = executor.invokeAll(callables, l, timeUnit)
|
||||
|
||||
def invokeAny[T](callables: Collection[_ <: Callable[T]]) = executor.invokeAny(callables)
|
||||
|
||||
def invokeAny[T](callables: Collection[_ <: Callable[T]], l: Long, timeUnit: TimeUnit) = executor.invokeAny(callables, l, timeUnit)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue