Sha256: 5b041a317ac3c5a13b0118b0650d188ab33fcadb84299f8b1e9b05389aa1c119

Contents?: true

Size: 1.93 KB

Versions: 1

Compression:

Stored size: 1.93 KB

Contents

require 'logging'
require 'memcache'

module MegaMutex
  class TimeoutError < Exception; end

  class CrossProcessMutex

    class Configuration
      attr_accessor :memcache_servers

      def initialize
        @memcache_servers = 'localhost'
      end
    end

    class << self
      def configure
        yield configuration
      end

      def configuration
        @configuration ||= Configuration.new
      end
    end
    
    def initialize(key, timeout = nil)
      @key = key
      @timeout = timeout
    end

    def logger
      Logging::Logger[self]
    end

    def run(&block)
      @start_time = Time.now
      log "Attempting to lock cross-process mutex..."
      lock!
      log "Locked. Running critical section..."
      yield
      log "Critical section complete. Unlocking..."
    ensure
      unlock!
      log "Unlocking Mutex."
    end
    
  private
  
    def timeout?
      return false unless @timeout
      Time.now > @start_time + @timeout
    end
  
    def log(message)
      logger.debug do
        "(key:#{@key}) (lock_id:#{my_lock_id}) #{message}"
      end
    end

    def lock!
      until timeout?
        return if attempt_to_lock == my_lock_id
        sleep 0.1
      end
      raise TimeoutError.new("Failed to obtain a lock within #{@timeout} seconds.")
    end
    
    def attempt_to_lock
      if current_lock.nil?
        set_current_lock my_lock_id
      end
      current_lock
    end
    
    def unlock!
      cache.delete(@key) if locked_by_me?
    end
    
    def locked_by_me?
      current_lock == my_lock_id
    end
    
    def current_lock
      cache.get(@key)
    end
    
    def set_current_lock(new_lock)
      cache.add(@key, my_lock_id)      
    end
    
    def my_lock_id
      @my_lock_id ||= "#{Process.pid.to_s}.#{self.object_id.to_s}.#{Time.now.to_i.to_s}"
    end

    def cache
      @cache ||= MemCache.new self.class.configuration.memcache_servers, :namespace => 'mega_mutex'
    end
  end
end

Version data entries

1 entries across 1 versions & 1 rubygems

Version Path
mega_mutex-0.1.0 lib/mega_mutex/cross_process_mutex.rb