with_flock(filename, expr, envir = parent.frame(), delay = 0.01, max_delay = 0.1, timeout = Inf, error = TRUE, verbose = FALSE)with_flock_(filename, expr, envir = parent.frame(), delay = 0.01, max_delay = 0.1, timeout = Inf, error = TRUE, verbose = FALSE)
NULL
, no
lockfile is used and the action always runs.filename
in any
way (see warnings in the package README).expr
. The
default is usually reasonable.fcntl
I see delays around the 0.2s mark when
accessing files over SMB so small values there are likely
aspirational. This time is also additional to the
fcntl
call (i.e., the pattern is try to lock, then sleep).delay
to max_delay
over subsequent calls.expr
(see Details).TRUE
then an error is thrown or the value if expr
is returned. If FALSE
the return value is a list with
the first element indicating success or not and the second
element being either a condition or the return value. See
Details.Evaluate an expression after acquiring, and while holding, a file
lock. The with_flock_
version uses standard evaluation and
is suitable for programming.
The behaviour on error is controlled by the error
argument.
If TRUE
(the default) then if a lock cannot be established
then with_flock
will throw an error and not return. If
there is no error the return value is whatever expr
evaluates to. (If expr
itself throws an error the lock
will always be cleaned up, though this may fail if the lockfile is
removed by the code in expr
or another process -- try to
avoid this!)
If error=FALSE
the return value is always a list of length
2. The first element is a logical scalar TRUE
or
FALSE
indicating if the lock was acquired and the
expression evaluated. The second element is the value of
expr
if the lock was acquired or a condition object
describing why the lock was not acquired. If expr
throws
an error, that error will still not be caught (use
tryCatch
).
In either case, if a lock cannot be established the code in
expr
is not evaluated.
It is not safe to use the file for anything, including locking it
a second time (e.g. with_flock(filename,
with_flock(filename, ...))
). Simply opening or closing a
handle to a file will break the lock on non-Windows systems
(this is a limitation of the underlying system calls).
## Demonstrating this is difficult because for a lock to fail ## another process needs to hold a lock on the file. But the ## basic approach for using it is below. ## First, we have a file that we want to modify; say path: path <- tempfile() writeLines(c("a", "b", "c"), path) ## Then we have another file that we'll use as a lock. We can't ## safely write to this file (see notes above) so it's simplest to ## have a separate file here. lock <- paste0(path, ".lock") ## Suppose we want to take the first element of the data in 'path'. ## This involves a read and a write operation so is not atomic - ## another process could read the file in the meantime and we'd ## both pull the same element out. But if we advertise that we're ## using it by using a lock the other process can wait until we ## release the lock: res <- with_flock(lock, { txt <- readLines(path) writeLines(txt[-1], path) txt[[1]] }) res[1] "a"