/**
* Copyright 2024 Beijing Baolande Software Corporation
*
* 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.
*
* Runtime Library Exception to the Apache 2.0 License:
*
* As an exception, if you use this Software to compile your source code and
* portions of this Software are embedded into the binary product as a result,
* you may redistribute such product without providing attribution as would
* otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.
*/
package hyperion.objectpool
/**
* 非独占资源的对象池。非独占资源可以被多个线程同时借出使用。
*
* @author yangfuping
*/
public class NonExclusiveObjectPool<T> <: ObjectPool<T> where T <: Hashable & Equatable<T> {
protected static let logger = LoggerFactory.getLogger("objectpool")
private let poolConfig: PoolConfig
private let objectFactory: PooledObjectFactory<T>
/**
* 共享的非独占资源列表
*/
private let sharedObjects: Array<AtomicOptionReference<PooledObject<T>>>
private let allObjects = ConcurrentHashMap<T, PooledObject<T>>()
private let nextIndexGenerator = AtomicUInt64(0)
private let removeObjects = ConcurrentHashMap<PooledObject<T>, T>()
/**
* 正在创建的对象数和活跃对象数的总和
*/
private let createAndActiveCount = AtomicInt64(0)
/**
* 活跃状态的对象数
*/
private let activeCount = AtomicInt64(0)
private let initialized = AtomicBool(false)
private let poolMutex = Mutex()
private let notEmptyMonitor = synchronized(poolMutex) {
poolMutex.condition()
}
private let closed = AtomicBool(false)
private let evictorThread = AtomicOptionReference<NonExclusiveEvictorThread<T>>()
public init(poolConfig: PoolConfig, objectFactory: PooledObjectFactory<T>) {
this.poolConfig = poolConfig
this.objectFactory = objectFactory
sharedObjects = Array<AtomicOptionReference<PooledObject<T>>>(
poolConfig.maxActiveSize,
{
// 需要确保每个元素使用单独的AtomicOptionReference
i => AtomicOptionReference<PooledObject<T>>()
}
)
}
/*
* 初始化对象池
*
* @throws PoolException
*/
public func initialize() {
if (initialized.compareAndSwap(false, true)) {
ensureMinActiveObjects()
var evictionIntervalsInSeconds = -1
if (let Some(evictionIntervals) <- poolConfig.evictionIntervals) {
evictionIntervalsInSeconds = evictionIntervals.toSeconds()
} else if (let Some(idleTimeout) <- poolConfig.idleTimeout) {
evictionIntervalsInSeconds = idleTimeout.toSeconds() / 3
}
if (evictionIntervalsInSeconds > 0) {
if (evictorThread.load().isSome()) {
return
}
let evictor = NonExclusiveEvictorThread<T>(this, evictionIntervalsInSeconds)
if (evictorThread.compareAndSwap(None, evictor)) {
// 启动检测线程
spawn {
Thread.currentThread.name = "NonExclusiveObjectPool-Evictor"
evictor.run()
}
}
}
}
}
/*
* 保证最小对象实例数
*/
private func ensureMinActiveObjects() {
if (poolConfig.minActiveSize > 0) {
while (createAndActiveCount.load() < poolConfig.minActiveSize) {
if (let Some(pooledObj) <- create()) {
addShareObject(pooledObj)
}
}
}
}
/*
* 创建对象
*
*/
protected func create(): ?PooledObject<T> {
var created = false
try {
let count = createAndActiveCount.fetchAdd(1)
if (count + 1 <= poolConfig.maxActiveSize) {
let pooledObj = objectFactory.createObject()
if (logger.isDebugEnabled()) {
logger.log(LogLevel.DEBUG, "Create object ${stringiferPooledObject(pooledObj)}")
}
pooledObj.config(poolConfig)
allObjects.add(pooledObj.value, pooledObj)
created = true
logPoolAction(pooledObj, PoolActions.CREATE_ACTION)
return pooledObj
}
return None
} catch (poolEx: PoolException) {
if (logger.isDebugEnabled()) {
logger.debug("Failure to create PooledObject", poolEx)
}
throw poolEx
} finally {
if (!created) {
createAndActiveCount.fetchSub(1)
}
}
}
private func addShareObject(pooledObj: PooledObject<T>): Unit {
if (logger.isDebugEnabled()) {
logger.log(LogLevel.DEBUG, "Add object ${stringiferPooledObject(pooledObj)}")
}
var retry = false
var retryCount = 0
do {
retryCount++
let nextIndex = nextIndex()
for (i in 0..sharedObjects.size) {
let realIndex = (nextIndex + i) % sharedObjects.size
let storedObj = sharedObjects[realIndex]
if (let Some(storeObject) <- storedObj.load()) {
if (logger.isTraceEnabled()) {
logger.log(LogLevel.TRACE, "The share[${realIndex}] is ${stringiferPooledObject(storeObject)}")
}
continue
}
if (logger.isDebugEnabled()) {
logger.log(LogLevel.DEBUG, "The sharedObjects[${realIndex}] is None")
}
if (storedObj.compareAndSwap(None, pooledObj)) {
if (logger.isDebugEnabled()) {
logger.log(
LogLevel.DEBUG,
"Set sharedObjects[${realIndex}] to ${stringiferPooledObject(pooledObj)}"
)
}
// 增加活跃对象数
activeCount.fetchAdd(1)
synchronized(poolMutex) {
notEmptyMonitor.notifyAll()
}
return
} else {
retry = true
if (logger.isTraceEnabled()) {
logger.log(
LogLevel.TRACE,
"Failure to set sharedObjects[${realIndex}] to ${stringiferPooledObject(pooledObj)}, continue"
)
}
}
}
} while (retry && retryCount < 3)
// 抛出异常前,应减少createAndActiveCount的数目
createAndActiveCount.fetchSub(1)
throw PoolException("Failure to add object ${stringiferPooledObject(pooledObj)}")
}
private func stringiferPooledObject(pooledObj: PooledObject<T>): String {
if (let Some(toString) <- (pooledObj.value as ToString)) {
return toString.toString()
} else {
return "PooledObject, id: ${pooledObj.id}"
}
}
/*
* 往对象池中添加实例
* @throws PoolException
*/
public func addObject(): Unit {
checkOpen()
if (let Some(pooledObj) <- create()) {
addShareObject(pooledObj)
}
}
/*
* 往对象池中添加指定数量的实例
*
* @param count
* @throws PoolException
*/
public func addObject(count: Int64): Unit {
checkOpen()
for (i in 0..count) {
if (createAndActiveCount.load() >= poolConfig.maxActiveSize) {
break
}
if (let Some(pooledObj) <- create()) {
addShareObject(pooledObj)
}
}
}
/*
* 借出对象
*
* @throws NoSuchElementException
*/
public func borrowObject(): T {
checkOpen()
if (!initialized.load()) {
spawn {
Thread.currentThread.name = "NonExclusiveObjectPool-Initializer"
initialize()
}
}
if (createAndActiveCount.load() < poolConfig.maxActiveSize) {
// 尝试创建对象
var createdObj = create()
if (let Some(pooledObj) <- createdObj) {
addShareObject(pooledObj)
pooledObj.allocate()
logPoolAction(pooledObj, PoolActions.BORROW_ACTION)
return pooledObj.value
}
}
var currentTime = DateTime.now().nanosecond
let expireTime = currentTime + poolConfig.borrowTimeout.toNanoseconds()
while (currentTime < expireTime) {
var loopCount = 0
var hasDestroyed = false
while (loopCount < sharedObjects.size) {
loopCount++
let index = nextIndex()
var valid = true
if (let Some(pooledObj) <- sharedObjects[index].load()) {
var valid = true
if (poolConfig.testOnBorrow) {
valid = objectFactory.validObject(pooledObj)
}
if (valid) {
pooledObj.allocate()
logPoolAction(pooledObj, PoolActions.BORROW_ACTION)
return pooledObj.value
} else {
hasDestroyed = true
sharedObjects[index].store(None)
destoryObject(pooledObj, PoolActions.TEST_ON_BORROW)
}
}
}
if (hasDestroyed && createAndActiveCount.load() < poolConfig.maxActiveSize) {
// 尝试创建对象
var createdObj = create()
if (let Some(pooledObj) <- createdObj) {
addShareObject(pooledObj)
pooledObj.allocate()
logPoolAction(pooledObj, PoolActions.BORROW_ACTION)
return pooledObj.value
}
}
let waitTime = expireTime - currentTime
synchronized(poolMutex) {
if (activeCount.load() == 0) {
notEmptyMonitor.wait(timeout: waitTime * Duration.nanosecond)
}
}
currentTime = DateTime.now().nanosecond
}
throw NoSuchElementException(
"Timeout waiting for idle object, maxWaitTime: ${poolConfig.borrowTimeout.toMilliseconds()} millseconds")
}
@OverflowWrapping
private func nextIndex(): Int64 {
let index = nextIndexGenerator.fetchAdd(1) % UInt64(sharedObjects.size)
return Int64(index)
}
/*
* 归还对象到池中
*/
public func returnObject(obj: T) {
if (let Some(pooledObj) <- allObjects.get(obj)) {
pooledObj.deallocate()
if (closed.load()) {
destoryObject(pooledObj, PoolActions.POOL_CLOSED)
return
}
if (poolConfig.testOnReturn) {
if (!objectFactory.validObject(pooledObj)) {
for (i in 0..sharedObjects.size) {
if (let Some(storeObj) <- sharedObjects[i].load()) {
if (storeObj == pooledObj) {
sharedObjects[i].store(None)
break
}
}
}
destoryObject(pooledObj, PoolActions.TEST_ON_RETURN)
}
}
} else {
if (logger.isDebugEnabled()) {
if (let Some(toString) <- (obj as ToString)) {
logger.debug("Returned object ${toString} not belongs to this pool")
} else {
logger.debug("Returned object not belongs to this pool")
}
}
}
}
/*
* 从对象池中销毁指定实例
*
* @param obj
* @throws PoolException
*/
public func invalidateObject(obj: T): Unit {
if (let Some(pooledObj) <- allObjects.get(obj)) {
for (i in 0..sharedObjects.size) {
if (let Some(storeObj) <- sharedObjects[i].load()) {
if (storeObj == pooledObj) {
sharedObjects[i].store(None)
break
}
}
}
destoryObject(pooledObj, PoolActions.INVALIDATION)
if (createAndActiveCount.load() < poolConfig.minActiveSize) {
addObject()
}
}
}
/*
* 从对象池中销毁所有实例
*
* @throws PoolException
*/
public func invalidateAll(): Unit {
let removeEntries = ArrayList<PooledObject<T>>()
for (i in 0..sharedObjects.size) {
if (let Some(pooledObj) <- sharedObjects[i].load()) {
removeEntries.add(pooledObj)
sharedObjects[i].store(None)
destoryObject(pooledObj, PoolActions.INVALIDATION)
}
}
for (pooledObj in removeEntries) {
destoryObject(pooledObj, PoolActions.INVALIDATION)
}
}
/*
* 销毁对象
*/
private func destoryObject(pooledObj: PooledObject<T>, cause: String) {
if (let Some(pooledObj) <- allObjects.remove(pooledObj.value)) {
objectFactory.destoryObject(pooledObj)
activeCount.fetchSub(1)
createAndActiveCount.fetchSub(1)
logPoolAction(pooledObj, PoolActions.DESTORY_ACTION, cause)
}
}
/**
* 清理过期或者失效对象
*/
public func evict() {
let invalidEntries = ArrayList<PooledObject<T>>()
let expireEntries = ArrayList<PooledObject<T>>()
for (i in 0..sharedObjects.size) {
if (let Some(sharedObject) <- sharedObjects[i].load()) {
let idle = sharedObject.checkIdleAndSetExpireTime()
if (poolConfig.testWhileIdle && !objectFactory.validObject(sharedObject)) {
sharedObjects[i].store(None)
invalidEntries.add(sharedObject)
continue
}
if ((idle && sharedObject.isExpired()) && createAndActiveCount.load() - expireEntries.size > poolConfig.
minActiveSize) {
sharedObjects[i].store(None)
expireEntries.add(sharedObject)
continue
}
}
}
for (removeObj in invalidEntries) {
destoryObject(removeObj, PoolActions.TEST_WHILTE_IDLE)
}
for (removeObj in expireEntries) {
destoryObject(removeObj, PoolActions.EXPIRATION)
}
// 保证最小活跃连接数
ensureMinActiveObjects()
}
/**
* 检查对象池是否为未关闭状态
*/
protected func checkOpen() {
if (closed.load()) {
throw PoolException("Pool is closed");
}
}
private func logPoolAction(pooledObj: PooledObject<T>, action: String) {
if (logger.isLoggable(LogLevel.DEBUG)) {
if (let Some(toString) <- (pooledObj.value as ToString)) {
logger.log(LogLevel.DEBUG, "${action} PooledObject ${toString}")
} else {
logger.log(LogLevel.DEBUG, "${action} PooledObject, id: ${pooledObj.id}")
}
}
if (logger.isLoggable(LogLevel.TRACE)) {
logger.log(LogLevel.TRACE, getObjectPoolStatus())
}
}
private func logPoolAction(pooledObj: PooledObject<T>, action: String, cause: String) {
if (logger.isLoggable(LogLevel.DEBUG)) {
if (let Some(toString) <- (pooledObj.value as ToString)) {
logger.log(
LogLevel.DEBUG,
"${action} PooledObject ${toString}, due to ${cause}"
)
} else {
logger.log(
LogLevel.DEBUG,
"${action} PooledObject, id: ${pooledObj.id}, due to ${cause}"
)
}
}
if (logger.isLoggable(LogLevel.TRACE)) {
logger.log(LogLevel.TRACE, getObjectPoolStatus())
}
}
private func clear(): Unit {
invalidateAll()
}
public func getObjectPoolStatus(): String {
let builder = StringBuilder()
builder.append("NonExclusiveObjectPool{")
builder.append("createAndActive objects: ${createAndActiveCount.load()}, ")
builder.append("active objects: ${activeCount.load()}, ")
builder.append("share objects: ${sharedObjects.size}, ")
builder.append("all objects: ${allObjects.size}")
builder.append("}")
return builder.toString()
}
public func isClosed() {
return closed.load()
}
/*
* @throws PoolException
*/
public func close(): Unit {
if (closed.load()) {
return
}
if (closed.compareAndSwap(false, true)) {
clear()
}
}
}
/**
* 清理失效对象和空闲超时对象的线程
*/
class NonExclusiveEvictorThread<T> where T <: Hashable & Equatable<T> {
private static let logger = LoggerFactory.getLogger("objectpool")
private let pool: NonExclusiveObjectPool<T>
private let evictionIntervalsInSeconds: Int64
public init(pool: NonExclusiveObjectPool<T>, evictionIntervalsInSeconds: Int64) {
this.pool = pool
this.evictionIntervalsInSeconds = evictionIntervalsInSeconds
}
public func run() {
while (!pool.isClosed()) {
try {
sleep(evictionIntervalsInSeconds * Duration.second)
pool.evict()
} catch (ex: Exception) {
logger.log(LogLevel.WARN, ex.message, ex)
}
}
}
}