nextflow.file.FileMutex.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
/*
* 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.file
import java.nio.channels.FileLock
import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j
import nextflow.util.Duration
/**
* Implements a mutual exclusive file lock strategy to prevent
* two or more JVMs/process to access the same file or resource
*
* NOTE: it should NOT be used to synchronise concurrent from different
* threads in the same JVM
*
* @author Paolo Di Tommaso
*/
@Slf4j
@CompileStatic
class FileMutex {
String waitMessage
String errorMessage
File target
long delayMillis = 100
private long timeoutMillis
FileMutex() { }
FileMutex(File target) {
this.target = target
}
FileMutex setTimeout( value ) {
if( value instanceof Duration )
timeoutMillis = value.millis
else if( value instanceof CharSequence )
timeoutMillis = Duration.of(value.toString()).millis
else if( value instanceof Number )
timeoutMillis = value.longValue()
else if( value != null )
throw new IllegalArgumentException("Not a valid Duration value: $value [${value.class.name}]")
return this
}
FileMutex setWaitMessage( String str ) {
this.waitMessage = str
return this
}
FileMutex setErrorMessage( String str ) {
this.errorMessage = str
return this
}
def T lock(Closure closure) {
assert target
final file = new RandomAccessFile(target, "rw")
try {
/*
* wait to acquire a lock
*/
boolean showed=0
long max = timeoutMillis ? timeoutMillis : Long.MAX_VALUE
long begin = System.currentTimeMillis()
FileLock lock
while( !(lock=file.getChannel().tryLock()) ) {
if( waitMessage && !showed ) {
log.info waitMessage
showed = true
}
if( System.currentTimeMillis()-begin > max )
throw new InterruptedException("Cannot acquire FileLock on $target")
sleep delayMillis
}
/*
* now it can do the job
*/
try {
return closure.call()
}
finally {
lock.close()
}
}
finally {
file.close()
}
}
}