class SzymanskisMutex

Based on the Szymanski's Mutual Exclusion Algorithm

Constants

SZYMANSKIS_SLEEP

Larger seconds mean less cycles/second but may result in lower completion

Public Class Methods

mutual_exclusion(concern) { || ... } click to toggle source

Provide a MutEx lock Provide the critical section as a block to this method Different concerns will prevent unrelated code to wait on each other Returns the result of the critical code or nil if something fails

# File lib/szymanskis_mutex.rb, line 41
def mutual_exclusion(concern)
  @@counter[concern] ||= 0
  @@flags[concern] ||= {}

  # Suppose @@counter += 1 is an atomic function
  my_id = @@counter[concern] += 1

  entry_protocol(concern, my_id)
  begin
    result = yield
  ensure
    # If something fails in the critical section release the resource
    exit_protocol(concern, my_id)
    result
  end
end

Private Class Methods

entry_protocol(concern, id) click to toggle source

1: Standing outside waiting room 2: Waiting for other processes to enter 3: Standing in doorway 4: Closed entrance door id is the number of the process entering this section relative to concern

# File lib/szymanskis_mutex.rb, line 65
def entry_protocol(concern, id)
  # Standing outside waiting room
  @@flags[concern][id] = 1

  # Wait for open door
  sleep(SZYMANSKIS_SLEEP) while @@flags[concern].values.any? { |f| f > 2 }

  # Stand in doorway
  @@flags[concern][id] = 3

  # Is another process waiting to enter?
  if @@flags[concern].values.any? { |f| f == 1 }

    # Wait for other processes to enter
    @@flags[concern][id] = 2

    # Wait for a process to enter and close the door
    sleep(SZYMANSKIS_SLEEP) while @@flags[concern].values.all? { |f| f != 4 }
  end

  # Close the entrance door
  @@flags[concern][id] = 4

  # Wait for lower ids to finish exit protocol
  while @@flags[concern].select { |i| i < id }.values.any? { |f| f != 1 }
    sleep(SZYMANSKIS_SLEEP)
  end
end
exit_protocol(concern, id) click to toggle source

Exit the process and if it is the last one in that batch then reopens the door for the next batch id is the number of the process exiting this section relative to concern

# File lib/szymanskis_mutex.rb, line 97
def exit_protocol(concern, id)
  # Ensure everyone in the waiting room has realized that the door
  # is supposed to be closed
  while @@flags[concern].select { |i| i > id }.any? { |f| [2, 3].include?(f) }
    sleep(SZYMANSKIS_SLEEP)
  end

  # Leave. This will reopen door if nobody is still in the waiting room
  @@flags[concern].delete(id)

  # If there are processes still running this concern there is
  # nothing else to do
  return unless @@flags[concern].empty?

  # Prevent counter from increasing indefinitely
  @@counter.delete concern
  @@flags.delete concern
end