Module: Tins::Once

Includes:
File::Constants
Defined in:
lib/tins/once.rb

Overview

A module for ensuring exclusive execution of code blocks using file-based locking.

This module provides mechanisms to prevent multiple instances of a script from running simultaneously by using file system locks on the script itself or a specified lock file.

Examples:

Basic usage with automatic lock file detection

Tins::Once.only_once do
  # Critical section - only one instance runs at a time
  perform_backup()
end

Using custom lock file

Tins::Once.only_once("/var/run/myapp.lock") do
  # Custom lock file
  process_data()
end

Non-blocking attempt

begin
  Tins::Once.try_only_once do
    # Will raise if another instance holds the lock
    update_cache()
  end
rescue => e
  puts "Another instance is running"
end

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.only_once(lock_filename = nil, locking_constant = nil) {|void| ... } ⇒ Object

Executes a block of code exclusively, ensuring only one instance runs at a time.

Uses the script name (or specified lock file) as the locking mechanism. The first invocation will acquire an exclusive lock and execute the block, while subsequent invocations will block until the lock is released.

Parameters:

  • lock_filename (String) (defaults to: nil)

    Optional custom lock filename. Defaults to ‘$0` (the script name), which means the script itself is used as the lock file.

  • locking_constant (Integer) (defaults to: nil)

    File locking constant. Defaults to ‘LOCK_EX` for exclusive locking.

Yields:

  • (void)

    The block of code to execute exclusively

Returns:

  • (Object)

    The return value of the yielded block

Raises:

  • (Errno::ENOENT)

    If the lock file doesn’t exist

  • (SystemCallError)

    If file locking fails



50
51
52
53
54
55
56
57
58
59
60
# File 'lib/tins/once.rb', line 50

def only_once(lock_filename = nil, locking_constant = nil)
  lock_filename ||= $0
  locking_constant ||= LOCK_EX
  f = File.new(lock_filename, RDONLY)
  f.flock(locking_constant) and yield
ensure
  if f
    f.flock LOCK_UN
    f.close
  end
end

.try_only_once(lock_filename = nil, locking_constant = nil) {|void| ... } ⇒ Object

Attempts to execute a block of code exclusively, but fails immediately if another instance holds the lock.

This is a non-blocking version that will raise an exception if the lock cannot be acquired immediately.

Parameters:

  • lock_filename (String) (defaults to: nil)

    Optional custom lock filename. Defaults to ‘$0` (the script name).

  • locking_constant (Integer) (defaults to: nil)

    File locking constant. Defaults to ‘LOCK_EX | LOCK_NB` for non-blocking exclusive locking.

Yields:

  • (void)

    The block of code to execute exclusively

Returns:

  • (Object)

    The return value of the yielded block

Raises:

  • (Errno::EAGAIN)

    If another process holds the lock

  • (Errno::ENOENT)

    If the lock file doesn’t exist



76
77
78
# File 'lib/tins/once.rb', line 76

def try_only_once(lock_filename = nil, locking_constant = nil, &block)
  only_once(lock_filename, locking_constant || LOCK_EX | LOCK_NB, &block)
end

Instance Method Details

#only_once(lock_filename = nil, locking_constant = nil) {|void| ... } ⇒ Object (private)

Executes a block of code exclusively, ensuring only one instance runs at a time.

Uses the script name (or specified lock file) as the locking mechanism. The first invocation will acquire an exclusive lock and execute the block, while subsequent invocations will block until the lock is released.

Parameters:

  • lock_filename (String) (defaults to: nil)

    Optional custom lock filename. Defaults to ‘$0` (the script name), which means the script itself is used as the lock file.

  • locking_constant (Integer) (defaults to: nil)

    File locking constant. Defaults to ‘LOCK_EX` for exclusive locking.

Yields:

  • (void)

    The block of code to execute exclusively

Returns:

  • (Object)

    The return value of the yielded block

Raises:

  • (Errno::ENOENT)

    If the lock file doesn’t exist

  • (SystemCallError)

    If file locking fails



50
51
52
53
54
55
56
57
58
59
60
# File 'lib/tins/once.rb', line 50

def only_once(lock_filename = nil, locking_constant = nil)
  lock_filename ||= $0
  locking_constant ||= LOCK_EX
  f = File.new(lock_filename, RDONLY)
  f.flock(locking_constant) and yield
ensure
  if f
    f.flock LOCK_UN
    f.close
  end
end

#try_only_once(lock_filename = nil, locking_constant = nil) {|void| ... } ⇒ Object (private)

Attempts to execute a block of code exclusively, but fails immediately if another instance holds the lock.

This is a non-blocking version that will raise an exception if the lock cannot be acquired immediately.

Parameters:

  • lock_filename (String) (defaults to: nil)

    Optional custom lock filename. Defaults to ‘$0` (the script name).

  • locking_constant (Integer) (defaults to: nil)

    File locking constant. Defaults to ‘LOCK_EX | LOCK_NB` for non-blocking exclusive locking.

Yields:

  • (void)

    The block of code to execute exclusively

Returns:

  • (Object)

    The return value of the yielded block

Raises:

  • (Errno::EAGAIN)

    If another process holds the lock

  • (Errno::ENOENT)

    If the lock file doesn’t exist



76
77
78
# File 'lib/tins/once.rb', line 76

def try_only_once(lock_filename = nil, locking_constant = nil, &block)
  only_once(lock_filename, locking_constant || LOCK_EX | LOCK_NB, &block)
end