Skip to contents

Mutually exclusive (mutex) locks are used to control access to shared resources.

An exclusive lock grants permission to one process at a time, for example to update the contents of a database file. While an exclusive lock is active, no other exclusive or shared locks will be granted.

Multiple shared locks can be held by different processes at the same time, for example to read a database file. While a shared lock is active, no exclusive locks will be granted.

Usage

mutex(name = uid(), assert = NULL, cleanup = FALSE, file = NULL)

# S3 method for class 'mutex'
with(data, expr, alt_expr = NULL, shared = FALSE, timeout_ms = Inf, ...)

Arguments

name

Unique ID. Alphanumeric, starting with a letter.

assert

Apply an additional constraint.

  • 'create' - Error if the mutex already exists.

  • 'exists' - Error if the mutex doesn't exist.

  • NULL - No constraint; create the mutex if it doesn't exist.

cleanup

Remove the mutex when the R session exits. If FALSE, the mutex will persist until $remove() is called or the operating system is restarted.

file

Use a hash of this file/directory path as the mutex name. The file itself will not be read or modified, and does not need to exist.

data

A mutex object.

expr

Expression to evaluate if the mutex is acquired.

alt_expr

Expression to evaluate if timeout_ms is reached.

shared

If FALSE (the default) an exclusive lock is returned. If TRUE, a shared lock is returned instead. See description.

timeout_ms

Maximum time (in milliseconds) to block the process while waiting for the operation to succeed. Use 0 or Inf to return immediately or only when successful, respectively.

...

Not used.

Value

mutex() returns a mutex object with the following methods:

  • $name

    • Returns the mutex's name (scalar character).

  • $lock(shared = FALSE, timeout_ms = Inf)

    • Returns TRUE if the lock is acquired, or FALSE if the timeout is reached.

  • $unlock(warn = TRUE)

    • Returns TRUE if successful, or FALSE (with optional warning) if the mutex wasn't locked.

  • $remove()

    • Returns TRUE on success, or FALSE if the mutex wasn't found.

with() returns eval(expr) if the lock was acquired, or eval(alt_expr) if the timeout is reached.

Error Handling

The with() wrapper automatically unlocks the mutex if an error stops evaluation of expr. If you are directly calling lock(), be sure that unlock() is registered with error handlers or added to on.exit(). Otherwise, the lock will persist until the process terminates.

Duplicate Mutexes

Mutex locks are per-process. If a process already has a lock, it can not attempt to acquire a second lock on the same mutex.

Examples


tmp <- tempfile()
mut <- interprocess::mutex(file = tmp)

print(mut)
#> <mutex> "C8iOKsEEg8E"

# Exclusive lock to write the file
with(mut, writeLines('some data', tmp))

# Use a shared lock to read the file
with(mut,
  shared     = TRUE,
  timeout_ms = 0, 
  expr       = readLines(tmp), 
  alt_expr   = warning('Mutex was locked. Giving up.') )
#> [1] "some data"

# Directly lock/unlock with safeguards
if (mut$lock(timeout_ms = 0)) {
  local({
    on.exit(mut$unlock())
    writeLines('more data', tmp)
  })
} else {
  warning('Mutex was locked. Giving up.')
}

mut$remove()
unlink(tmp)