nextflow.util.Throttle.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nf-commons Show documentation
Show all versions of nf-commons Show documentation
A DSL modelled around the UNIX pipe concept, that simplifies writing parallel and scalable pipelines in a portable manner
The newest version!
/*
* Copyright 2013-2024, Seqera Labs
*
* 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 nextflow.util
import java.util.concurrent.ConcurrentHashMap
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
/**
* Limit the execution of a code block on a specified time basis
*
* @author Paolo Di Tommaso
*/
@CompileStatic
class Throttle {
static final Map throttleMap = new ConcurrentHashMap<>()
@EqualsAndHashCode
static class ThrottleObj {
Object result
long timestamp
ThrottleObj() {}
ThrottleObj( value, long timestamp ) {
this.result = value
this.timestamp = timestamp
}
}
/**
* Executes the specified code block and returns the resulting value. All following
* invocations within the specified time {@code period} are skipped and the cached
* result value is returned instead.
*
* @param period
* The time period in milliseconds, in which the throttler will allow at most to invoke
* the specified closure
* @param closure
* The code block to execute
* @return
* The value returned by the closure execution
*/
static Object every( long period, Closure closure ) {
throttle0( period, null, closure)
}
/**
* Executes the specified code block and returns the resulting value. All following
* invocations within the specified time {@code period} are skipped and the cached
* result value is returned instead.
*
* @param period
* The time duration, in which the throttler will allow at most to invoke
* the specified closure
* @param closure
* The code block to execute
* @return
* The value returned by the closure execution
*/
static Object every( Duration period, Closure closure ) {
throttle0( period.millis, null, closure)
}
/**
* Executes the specified code block and returns the resulting value. All following
* invocations within the specified time {@code period} are skipped and the cached
* result value is returned instead.
*
* @param period
* The duration string, in which the throttler will allow at most to invoke
* the specified closure
* @param closure
* The code block to execute
* @return
* The value returned by the closure execution
*/
static Object every( String period, Closure closure ) {
throttle0( Duration.of(period).millis, null, closure)
}
/**
* Allows at most one execution after the specified time {@code delay} returning
* {@code null} before the first execution, and the cached result value the
* following times
*
* @param delay
* @param closure
* @return
*/
static Object after( long delay, Closure closure ) {
final obj = new ThrottleObj( null, System.currentTimeMillis() )
throttle0( delay, obj, closure)
}
/**
* Allows at most one execution after the specified time {@code delay} returning
* {@code null} before the first execution, and the cached result value the
* following times
*
* @param delay
* @param closure
* @return
*/
static Object after( Duration delay, Closure closure ) {
def obj = new ThrottleObj( null, System.currentTimeMillis() )
throttle0( delay.millis, obj, closure)
}
/**
* Allows at most one execution after the specified time {@code delay} returning
* {@code null} before the first execution, and the cached result value the
* following times
*
* @param delay
* @param closure
* @return
*/
static Object after( String delay, Closure closure ) {
def obj = new ThrottleObj( null, System.currentTimeMillis() )
throttle0( Duration.of(delay).millis, obj, closure)
}
/**
* Allows at most one execution after the specified time {@code delay} returning
* {@code initialValue} before the first execution, and the cached result value the
* following times
*
* @param delay
* @param initialValue
* @param closure
* @return
*/
static Object after( long delay, initialValue, Closure closure ) {
final obj = new ThrottleObj( initialValue, System.currentTimeMillis() )
throttle0( delay, obj, closure)
}
/**
* Allows at most one execution after the specified time {@code delay} returning
* {@code initialValue} before the first execution, and the cached result value the
* following times
*
* @param delay
* @param initialValue
* @param closure
* @return
*/
static Object after( Duration delay, initialValue, Closure closure ) {
def obj = new ThrottleObj( initialValue, System.currentTimeMillis() )
throttle0( delay.millis, obj, closure)
}
/**
* Allows at most one execution after the specified time {@code delay} returning
* {@code initialValue} before the first execution, and the cached result value the
* following times
*
* @param delay
* @param initialValue
* @param closure
* @return
*/
static Object after( String delay, initialValue, Closure closure ) {
def obj = new ThrottleObj( initialValue, System.currentTimeMillis() )
throttle0( Duration.of(delay).millis, obj, closure)
}
private static throttle0( long timeout, ThrottleObj initialValue, Closure closure ) {
assert closure != null
def key = 17
key = 31 * key + closure.class.name.hashCode()
key = 31 * key + closure.owner.hashCode()
key = 31 * key + closure.delegate?.hashCode() ?: 0
ThrottleObj obj = throttleMap.get(key)
if( obj == null ) {
obj = initialValue ?: new ThrottleObj()
throttleMap.put(key,obj)
}
if( System.currentTimeMillis() - obj.timestamp > timeout ) {
obj.timestamp = System.currentTimeMillis()
obj.result = closure.call()
}
obj.result
}
static V cache( Object key, Duration eviction, Closure action ) {
cache(key, eviction.toMillis(), action)
}
static V cache( Object key, long eviction, Closure closure ) {
final int hash = key?.hashCode() ?: 0
ThrottleObj obj = throttleMap.get(hash)
if( obj == null ) {
obj = new ThrottleObj()
obj.result = closure.call()
obj.timestamp = System.currentTimeMillis()
throttleMap.put(hash,obj)
}
else if( System.currentTimeMillis() - obj.timestamp > eviction ) {
obj.timestamp = System.currentTimeMillis()
obj.result = closure.call()
}
(V)obj.result
}
}