class Splib::Monitor

Public Class Methods

new() click to toggle source

Create a new Monitor

# File lib/woolen_common/splib/Monitor.rb, line 10
def initialize
    @threads = []
    @locks = []
    @lock_owner = nil
    @timers = {}
    @timer = start_timer
    @stop = false
    Kernel.at_exit do
        if(@timer)
            @stop = true
            @timer.raise Wakeup.new
        end
    end
end

Public Instance Methods

broadcast() click to toggle source

Wake up all threads

# File lib/woolen_common/splib/Monitor.rb, line 69
def broadcast
    synchronize do
        @threads.dup.each do |t|
            t.wakeup if t.alive? && t.stop?
        end
    end
end
lock() click to toggle source

Lock the monitor

# File lib/woolen_common/splib/Monitor.rb, line 81
def lock
    Thread.exclusive{ do_lock }
    until(owner?(Thread.current)) do
        Thread.stop
    end
end
locked?(cln=true) click to toggle source
cln

Clean dead threads

Is monitor locked

# File lib/woolen_common/splib/Monitor.rb, line 107
def locked?(cln=true)
    Thread.exclusive{clean} if cln
    @locks.size > 0 || @lock_owner
end
signal() click to toggle source

Wake up earliest thread

# File lib/woolen_common/splib/Monitor.rb, line 56
def signal
    synchronize do
        while(t = @threads.shift)
            if(t && t.alive? && t.stop?)
                t.wakeup
                break
            else
                next
            end
        end
    end
end
synchronize() { || ... } click to toggle source

Lock the monitor, execute block and unlock the monitor

# File lib/woolen_common/splib/Monitor.rb, line 112
def synchronize
    result = nil
    lock
    result = yield
    do_unlock
    result
end
try_lock() click to toggle source

Attempt to lock. Returns true if lock is aquired and false if not.

# File lib/woolen_common/splib/Monitor.rb, line 92
def try_lock
    locked = false
    Thread.exclusive do
        clean
        unless(locked?(false))
            do_lock
            locked = true
        else
            locked = owner?(Thread.current)
        end
    end
    locked
end
unlock() click to toggle source

Unlock the monitor

# File lib/woolen_common/splib/Monitor.rb, line 88
def unlock
    do_unlock
end
wait(timeout=nil) click to toggle source
timeout

Wait for given amount of time

Park a thread here

# File lib/woolen_common/splib/Monitor.rb, line 27
def wait(timeout=nil)
    raise 'This thread is already a registered sleeper' if @threads.include?(Thread.current)
    Thread.exclusive{ @threads << Thread.current }
    if(timeout)
        timeout = timeout.to_f
        Thread.exclusive{ @timers[Thread.current] = timeout }
        @timer.raise Wakeup.new
    end
    Thread.stop
    Thread.exclusive{ @threads.delete(Thread.current) }
    if(timeout && @timers.has_key?(Thread.current))
        Thread.exclusive{ @timers.delete(Thread.current) }
        @timer.raise Wakeup.new
    end
    true
end
wait_until() click to toggle source

Park thread until block is true

# File lib/woolen_common/splib/Monitor.rb, line 50
def wait_until
    until yield
        wait
    end
end
wait_while() click to toggle source

Park thread while block is true

# File lib/woolen_common/splib/Monitor.rb, line 44
def wait_while
    while yield
        wait
    end
end
waiters() click to toggle source

Number of threads waiting

# File lib/woolen_common/splib/Monitor.rb, line 77
def waiters
    @threads.size
end

Private Instance Methods

clean() click to toggle source

This is a simple helper method to help keep threads from ending up stuck waiting for a lock when a thread locks the monitor and then decides to die without unlocking. It is only called when new locks are attempted or a check is made if the monitor is currently locked.

# File lib/woolen_common/splib/Monitor.rb, line 128
def clean
    @locks.delete_if{|t|!t.alive?}
    if(@lock_owner && !@lock_owner.alive?)
        @lock_owner = @locks.empty? ? nil : @locks.shift
        @lock_owner.wakeup if @lock_owner && !owner?(Thread.current)
    end
end
do_lock() click to toggle source

Aquire monitor lock or be queued for lock NOTE: To make this method more generic and useful, it does not perform a Thread.exclusive, and as such this method should only be called from within a Thread.exclusive{}

# File lib/woolen_common/splib/Monitor.rb, line 145
def do_lock
    clean
    if(@lock_owner)
        if(owner?(Thread.current))
            @locks.unshift(Thread.current)
        else
            @locks << Thread.current
        end
    else
        @lock_owner = Thread.current
    end
    true
end
do_unlock() click to toggle source

Unlock the monitor

# File lib/woolen_common/splib/Monitor.rb, line 160
def do_unlock
    unless(owner?(Thread.current))
        raise ThreadError.new("Thread #{Thread.current} is not the current owner: #{@lock_owner}")
    end
    Thread.exclusive do
        @locks.delete_if{|t|!t.alive?}
        unless(@locks.empty?)
            old_owner = @lock_owner
            @lock_owner = @locks.shift
            @lock_owner.wakeup unless old_owner == @lock_owner
        else
            @lock_owner = nil
        end
    end
end
owner?(t) click to toggle source

Check if the givin thread is the current owner

# File lib/woolen_common/splib/Monitor.rb, line 137
def owner?(t)
    @lock_owner == t
end
start_timer() click to toggle source

Starts the timer for waiting threads with a timeout

# File lib/woolen_common/splib/Monitor.rb, line 177
def start_timer
    @timer = Thread.new do
        begin
            until(@stop) do
                cur = []
                t = 0
                Thread.exclusive do
                    t = @timers.values.min
                    cur = @timers.dup
                end
                t = 0 if !t.nil? && t < 0
                a = 0
                begin
                    a = Splib.sleep(t)
                rescue Wakeup
                    # do nothing of importance
                ensure
                    next if t.nil?
                    Thread.exclusive do
                        cur.each_pair do |thread, value|
                            value -= a
                            if(value <= 0)
                                thread.wakeup
                                @timers.delete(thread)
                            else
                                @timers[thread] = value
                            end
                        end
                    end
                end
            end
        rescue
            retry
        end
    end
end