Negative Sleep (#87)

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

While 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

running 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 = 0
@possessor = nil
@mutex = Mutex.new
@cv = ConditionVariable.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 -= 1
        return unless @count.zero?
        @possessor = nil
        @cv.signal
    end
end

private
def possessing?
    Thread.current == @possessor
end

def try_get
    if @possessor.nil? then
        @possessor = Thread.current
        @count = 1
        return true
    end
    return false unless possessing?
    @count += 1
    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 = nil
### time the supervisor slept
@@time = 0
### keep track of the maximum prioriy
@@max_prio = Thread.current.priority + 1
### synchronizing management data
@@lock = BusyFlag.new

alias_method :orig_priority=, :priority=
def priority= np
    # 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 = np if np > @@max_prio
self.orig_priority = np
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 = Thread.current.priority
            Thread.current.orig_priority = @@max_prio + 1
            @@supervisor = Thread.new( Thread.current ) {
                |t|
                Thread.current.orig_priority = @@max_prio + 2
                @@time = orig_sleep n
                t.orig_priority = old_prio
            }
            @@supervisor = nil
        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 :slight_smile:
Thread.stop_insomnia if Thread.insomnia?
orig_sleep n
end
end
end

On 7/19/06, James Edward G. II [email protected] wrote:

The mailing list link requires a little work to extract it from,
yes. (I was to do that as a quiz someday.) However, I’m pretty
diligent. I have your code and it will be uploaded with the summary
on Thursday. No worries. :wink:

James Edward G. II

Well thank you, I will not do it again :wink:
Robert

On Jul 18, 2006, at 4:56 PM, Robert D. wrote:

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?

The mailing list link requires a little work to extract it from,
yes. (I was to do that as a quiz someday.) However, I’m pretty
diligent. I have your code and it will be uploaded with the summary
on Thursday. No worries. :wink:

James Edward G. II

On 7/18/06, Adam S. [email protected] wrote:

say good bye to your filesystems, databases etc. etc.

oops. It’s been about 10 years since I used Unix regularly, and I was
never the sysadmin.

I suppose this is offtopic, but why would it mess up the filesystem?
I realize if you leave the time set in the past you’ll run into
problems with file creation/modification times, but the testscript
restores the time to its original point - where’s the lasting harm in
that?

Actually I am not clever enough to know myself, I suppose you are pretty
safe on a client box of your own.
It all depends on the implementation of the file system.
My knowledge comes from an excellent NTP tutorial, quite normal for them
to
warn about it.
The NTP FAQ and HOWTO Warning: Totally Offtopic.

But I would not assume that all system software is robust against not
contigous time. (think about cronjobs e.g. who would write them
reentrant, I
would not !!)

I did not mean to criticise your Quiz submission BTW, quit nice and
original
but not safe I am afraid.

Cheers
Robert

I didn’t seem to suffer any permanent side effects after running it on

WinXP.

-Adam


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein