------art_75357_3799978.1153259766438
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

I just checked rubyquiz.com for some submissions and realized that sending
my submission as tar.bz2 was, errr, not the best idea.
Maybe it would be a good idea to add a warning about it somewhere or did I
overlook it?
Anyway here is my submission again. I was doing some rather havy
synchronisation stuff which is probably unneccessary, I would appreciate any
suggestions. I have the feeling that just using Thread.critical might be
enough,otoh I would interfere with threading libraries which want to use my
sleep-.

Here ya go

Definition
hile sleep(n) [n > 0] assures that the calling Thread sleeps for at least n
seconds,
unless awoken by #run, sleep(m) [m < 0] assures that the calling Thread will
suffer from
insomnia for at least -m seconds, unless resting with #stop or #sleep(n) [n
> 0]

As we know sleep(n) [n > 0] returns the time really slept, sleep(m) [m < 0]
cannot easily do
such a thing as it returns immediately. But when we put ourselves out of
insomnia with Thread#stop
we can return the time we were in insomnia. Thread#stop will still return
nil if the calling thread
was not in insomnia.

Implementation
Instead of rewriting the scheduler I will try to give an implementation by
using Thread
priorities.
A thread that calls sleep(m) [m < 0] will be assigned the highest priority
of all threads. It also starts
another thread, called supervisor with even higher priority. But the
supervisor just sleeps for -m
seconds before reassigning the normal priority to the calling thread.

The Thread  is in "insomnia" mode during this time span. As mentioned above
there are only two
ways to get out of this "insomnia" mode. By the timeout -m, or if the
calling thread calls sleep(n)
[n > 0] or stop.

In order to do that we have to intercept all calls to Kernel#sleep and to
methods
adjusting a Thread's priority, which are:
    * Thread#new
    * Thread#fork
    * Thread#priority* Thread#stop
to get out of insomnia ourselves.

I will ignore Thread#new and Thread#fork assuming that they initialize a
Thread with priority 0.

Testing
unning test.rb with a parameter of >> 10**5 should give a first insight of
how it works, depending
on the speed of your machine.


Shortcomings

* The naming is quite trivial and might conflict with subclasses of Thread.
* The implementation is minimal, an insomnia Thread does not get back into
insomnia mode after
    a sleep(n) [n > 0] as one might assume. An insomnia thread cannot call
sleep(m) [m < 0] again
    as might be useful.
* When calling sleep before synchronizing the Thread might be interrupted
and thus the effect
    of sleep(m) [m < 0] might not be immideate (as can be seen in test2.rb).
*Synchronization is done with big guns and without optimization, the former
because
    I do not know ruby Threads well, the later because of clarity of code.

* And hopefully *you* tell me about the other ones ;).

----------------------- 8< --------------------------------------
#!/usr/bin/ruby

require 'thread'

class IllegalMonitorState < RuntimeError ; end

class BusyFlag # taken from Scott Oak's and Henry Wong's "Java Threads"
    # allows to be called again for a Thread
    # already in possession of the Flag
    def initialize
        @count      
        @possessor  il
        @mutex      utex.new
        @cv         onditionVariable.new
    end

    def get
        @mutex.synchronize do
            while not try_get do
                @cv.wait( @mutex )
            end
        end
    end

    def free
        @mutex.synchronize do
            return unless possessing?
            @count - 
            return unless @count.zero?
            @possessor  il
            @cv.signal
        end
    end

    private
    def possessing?
        Thread.current @possessor
    end

    def try_get
        if @possessor.nil? then
            @possessor  hread.current
            @count  
            return true
        end
        return false unless possessing?
        @count + 
        true
    end

end


class Thread
    ### keep track if there is a thread in insomnia by means of a supervisor
    ### control the insomnia with a supervisor
    @@supervisor  il
    ### time the supervisor slept
    @@time        
    ### keep track of the maximum prioriy
    @@max_prio  hread.current.priority + 1
    ### synchronizing management data
    @@lock  usyFlag.new

    alias_method :orig_priority:prioritydef priority p
        # I do not think this needs to be protected, if a thread is
interrupted while setting the priority
        # it cannot yet run with that priority and well not interfere with
the insomnia process.
        @@max_prio  p if np > @@max_prio
        self.orig_priority  p
    end

    class << self

        alias_method :orig_stop, :stop

        def insomnia?; @@supervisor end
        def max_prio; @@max_prio end
        def last_time; @@time end

        def lock; @@lock; end

        def set_insomnia n
            synchronize do
                old_prio  hread.current.priority
                Thread.current.orig_priority  @max_prio + 1
                @@supervisor  hread.new( Thread.current ) {
                    |t|
                    Thread.current.orig_priority  @max_prio + 2
                    @@time  rig_sleep n
                    t.orig_priority  ld_prio
                }
                @@supervisor  il
            end
        end

        def stop_insomnia
            @@supervisor.run if @@supervisor
        end

        def stop
            synchronize do
                stop_insomnia
                orig_stop
                @@time
            end
        end

        def synchronize( &block )
            begin
                @@lock.get
                block.call
            ensure
                @@lock.free
            end
        end
    end
end

module Kernel
    alias_method :orig_sleep, :sleep

    def sleep n
        Thread.synchronize do
            if n < 0 then
                raise IllegalMonitorState, "Already in insommnia mode" if
Thread.insomnia?
                return Thread.set_insomnia( -n )
            end
            Thread.lock.free
            ### if we are in Thread insomnia we might be interrupted by the
supervisor which gets us out of it
            ### but even if we call Thread.stop_insomnia then that has no
effect. So no sync needed :)
            Thread.stop_insomnia if Thread.insomnia?
            orig_sleep n
        end
    end
end

------art_75357_3799978.1153259766438--