Pulling my hair out, why won't Kernel.sleep(0) sleep?

Can anybody give me any hints as to what I should be looking for? What
can cause sleep(0) to wake up?

I've got a thread that does

  loop do
    sleep(delay)

    ... do stuff, then set delay to 0 because there is no more work ...
    
    delay = 0
  end

And my loop is busy looping, sleep just won't!

Whether it does or not is triggered by changes in what appear to me to
be completely unrelated code. I'm mystified.

Thanks,
Sam

From the docs - SIGALRM or another thread calling run. If these cases
arent relevant, could you post an example of the unrelated code?

···

On Fri, 18 Feb 2005 12:16:41 +0900, Sam Roberts <sroberts@uniserve.com> wrote:

Can anybody give me any hints as to what I should be looking for? What
can cause sleep(0) to wake up?

I've got a thread that does

  loop do
    sleep(delay)

    ... do stuff, then set delay to 0 because there is no more work ...

    delay = 0
  end

And my loop is busy looping, sleep just won't!

Whether it does or not is triggered by changes in what appear to me to
be completely unrelated code. I'm mystified.

Thanks,
Sam

--
spooq

Sam Roberts wrote:

Can anybody give me any hints as to what I should be looking for?

What

can cause sleep(0) to wake up?

I've got a thread that does

  loop do
    sleep(delay)

    ... do stuff, then set delay to 0 because there is no more work

....

    delay = 0
  end

And my loop is busy looping, sleep just won't!

Whether it does or not is triggered by changes in what appear to me

to

be completely unrelated code. I'm mystified.

Silly question: Is there another thread active at the same time? I
don't think a thread can sleep if it's the only one...

The documentation is wrong.

sleep(0) returns immediately. sleep with *zero* arguments sleeps
forever.

static VALUE
rb_f_sleep(argc, argv)
    int argc;
    VALUE *argv;
{
    int beg, end;

    beg = time(0);
    if (argc == 0) {
        rb_thread_sleep_forever();
    }
    else if (argc == 1) {
        rb_thread_wait_for(rb_time_interval(argv[0]));
    }
    else {
        rb_raise(rb_eArgError, "wrong number of arguments");
    }

    end = time(0) - beg;

    return INT2FIX(end);
}

Cheers,
Navin.

···

Sam Roberts <sroberts@uniserve.com> wrote:

Can anybody give me any hints as to what I should be looking for? What
can cause sleep(0) to wake up?

Assaph Mehr wrote:

Silly question: Is there another thread active at the same time? I
don't think a thread can sleep if it's the only one...

Huh? You mean the main thread cannot sleep? Or have I mis-read your answer?

-- shanko

Quoteing navindra@cs.mcgill.ca, on Fri, Feb 18, 2005 at 12:33:13PM +0900:

> Can anybody give me any hints as to what I should be looking for? What
> can cause sleep(0) to wake up?

The documentation is wrong.

sleep(0) returns immediately. sleep with *zero* arguments sleeps
forever.

Thank you, thank you, thank you!

I had started to notice that sometimes the code busy loops, and
sometimes it doesn't, it doesn't matter whether I make any code changes,
that turned out to be unrelated.

Do you have any idea why this should be? What does Kernel.sleep(0)
actually do?

Sam

···

Sam Roberts <sroberts@uniserve.com> wrote:

static VALUE
rb_f_sleep(argc, argv)
    int argc;
    VALUE *argv;
{
    int beg, end;

    beg = time(0);
    if (argc == 0) {
        rb_thread_sleep_forever();
    }
    else if (argc == 1) {
        rb_thread_wait_for(rb_time_interval(argv[0]));
    }
    else {
        rb_raise(rb_eArgError, "wrong number of arguments");
    }

    end = time(0) - beg;

    return INT2FIX(end);
}

Cheers,
Navin.

Navindra Umanee wrote:

> Can anybody give me any hints as to what I should be looking for?

What

> can cause sleep(0) to wake up?

The documentation is wrong.

sleep(0) returns immediately. sleep with *zero* arguments sleeps
forever.

static VALUE
rb_f_sleep(argc, argv)
    int argc;
    VALUE *argv;
{
    int beg, end;

    beg = time(0);
    if (argc == 0) {
        rb_thread_sleep_forever();
    }
    else if (argc == 1) {
        rb_thread_wait_for(rb_time_interval(argv[0]));
    }
    else {
        rb_raise(rb_eArgError, "wrong number of arguments");
    }

    end = time(0) - beg;

    return INT2FIX(end);
}

Cheers,
Navin.

Why are arguments being counted manually? It's less code, less error
prone, and doesn't force you to explicitly raise an ArgumentError if
the argument count is wrong.

Refactored:

/*
* :call-seq:
* sleep(numeric=nil)

···

Sam Roberts <sroberts@uniserve.com> wrote:

*
* Suspends the current thread for +numeric+ seconds (which may be a
* Float with fractional seconds. Returns the actual number of seconds
* slept, rounded, which may be less than what was asked for if the
* thread was interrupted by a SIGALARM or if another thread calls
* Thread#run.
*
* If no argument is provided then it will sleep forever.
*/
static VALUE
rb_f_sleep(argc, argv)
    int argc;
    VALUE *argv;
{
    int beg, end;
    VALUE rbNum;

    beg = time(0);

    rb_scan_args(argc,argv,"01",&rbNum);

    if(NIL_P(rbNum)){
       rb_thread_sleep_forever();
    }
    else{
       rb_thread_wait_for(rb_time_interval(rbNum));
    }

    end = time(0) - beg;

    return INT2FIX(end);
}

Shashank Date wrote:

Assaph Mehr wrote:

> Silly question: Is there another thread active at the same time? I
> don't think a thread can sleep if it's the only one...

Huh? You mean the main thread cannot sleep? Or have I mis-read your

answer?

Perhaps I wasn't clear. The main thread can of course sleep for a
specified period. But if you do a sleep 0, it means sleep until woken
up. Ruby will check, see that there is no other thread waiting and will
thus wake the main thread again.
So if you only have one thread, you can't sleep indefinitely.

Assaph

I would guess that the sleep is triggering the thread scheduler to
check the queue for eligible threads to run. Probably sometimes the
same thread is chosen and sometimes another thread.

Not 100% sure.

Cheers,
Navin.

···

Sam Roberts <sroberts@uniserve.com> wrote:

I had started to notice that sometimes the code busy loops, and
sometimes it doesn't, it doesn't matter whether I make any code changes,
that turned out to be unrelated.

Do you have any idea why this should be? What does Kernel.sleep(0)
actually do?

Hi,

The documentation is wrong.

Oops. I will fix.

I had started to notice that sometimes the code busy loops, and
sometimes it doesn't, it doesn't matter whether I make any code changes,
that turned out to be unrelated.

Do you have any idea why this should be? What does Kernel.sleep(0)
actually do?

I guess it was busy looping, but there were other threads to work
sometimes. sleep(0) does not sleep but causes context switch.

              matz.

···

In message "Re: pulling my hair out, why won't Kernel.sleep(0) sleep?" on Fri, 18 Feb 2005 13:07:28 +0900, Sam Roberts <sroberts@uniserve.com> writes:

Sam Roberts schrieb:

I had started to notice that sometimes the code busy loops, and
sometimes it doesn't, it doesn't matter whether I make any code changes,
that turned out to be unrelated.

Do you have any idea why this should be?

Could it be that the delay variable is shared between the threads?

Regards,
Pit

Why are arguments being counted manually?

it's faster :slight_smile:

    if(NIL_P(rbNum)){
       rb_thread_sleep_forever();
    }

it don't do the same thing :slight_smile:

uln% ruby -e 'sleep(nil)'
-e:1:in `sleep': can't convert NilClass into time interval (TypeError)
        from -e:1
uln%

Guy Decoux

Assaph Mehr wrote:

Shashank Date wrote:
> Assaph Mehr wrote:
>
> > Silly question: Is there another thread active at the same time?

I

> > don't think a thread can sleep if it's the only one...
>
> Huh? You mean the main thread cannot sleep? Or have I mis-read your
answer?

Perhaps I wasn't clear. The main thread can of course sleep for a
specified period. But if you do a sleep 0, it means sleep until woken
up. Ruby will check, see that there is no other thread waiting and

will

thus wake the main thread again.
So if you only have one thread, you can't sleep indefinitely.

Oops, looks like I was wrong. See Navindra's clarification.

Quoteing matz@ruby-lang.org, on Fri, Feb 18, 2005 at 01:50:26PM +0900:

Hi,

>Do you have any idea why this should be? What does Kernel.sleep(0)
>actually do?

I guess it was busy looping, but there were other threads to work
sometimes. sleep(0) does not sleep but causes context switch.

That describes what I saw, kindof (race conditions are inherently
weird).

Thanks,
Sam

Here's a start to the docs, but I don't know if my description is
correct!

/*
  * call-seq:
- * sleep(duration=0) => fixnum
+ * sleep(duration) => fixnum
+ * sleep() => fixnum

···

*
- * Suspends the current thread for _duration_ seconds (which may be
- * any number, including a +Float+ with fractional seconds). Returns the actual
- * number of seconds slept (rounded), which may be less than that asked
- * for if the thread was interrupted by a +SIGALRM+, or if
- * another thread calls <code>Thread#run</code>. An argument of zero
- * causes +sleep+ to sleep forever.
- *
+ * Suspends the current thread for _duration_ seconds (which may be any
+ * number, including a +Float+ with fractional seconds). Calling sleep with
+ * no arguments causes +sleep+ to sleep "forever" - where "forever" means
+ * until it is woken up, see below. Calling sleep with an argument of
+ * +0+ causes a context switch, if another thread is ready to be
+ * scheduled.
+ *
+ * Returns the actual number of seconds slept (rounded), which may be less
+ * than that asked for if the thread was interrupted by a +SIGALRM+, or if
+ * another thread calls <code>Thread#run</code>.
+ *
+ * WHAT ABOUT OTHER SIGNALS?
+ *
  * Time.new #=> Wed Apr 09 08:56:32 CDT 2003
  * sleep 1.2 #=> 1
  * Time.new #=> Wed Apr 09 08:56:33 CDT 2003

ts wrote:

> Why are arguments being counted manually?

it's faster :slight_smile:

> if(NIL_P(rbNum)){
> rb_thread_sleep_forever();
> }

it don't do the same thing :slight_smile:

uln% ruby -e 'sleep(nil)'
-e:1:in `sleep': can't convert NilClass into time interval

(TypeError)

        from -e:1
uln%

I'm confused - what conversion is it trying to do? I thought these two
were equivalent:

if(NIL_P(rbNum))

if(Qnil == rbNum)

No?

Regards,

Dan

I'm confused - what conversion is it trying to do? I thought these two
were equivalent:

if(NIL_P(rbNum))

if(Qnil == rbNum)

if you want to write the same with rb_scan_args(), you must write

    if (rb_scan_args(argc, argv, "01", &rbNum)) {
        rb_thread_wait_for(rb_time_interval(rbNum));
    }
    else {
        rb_thread_sleep_forever();
    }

and you see that the ruby version is faster :slight_smile:

Guy Decoux

ts wrote:

> I'm confused - what conversion is it trying to do? I thought

these two

> were equivalent:

> if(NIL_P(rbNum))

> if(Qnil == rbNum)

if you want to write the same with rb_scan_args(), you must write

    if (rb_scan_args(argc, argv, "01", &rbNum)) {
        rb_thread_wait_for(rb_time_interval(rbNum));
    }
    else {
        rb_thread_sleep_forever();
    }

and you see that the ruby version is faster :slight_smile:

Guy Decoux

First things first. You're skirting the issue. It SHOULD work. There
is some sort of side effect happening, because the error is NOT coming
from process.c. I added some debug print statements directly into
process.c and rebuilt. I can't even get to that point before the error
is raised. This smells like a bug and/or some kind of global setting
being used some where.

Second, counting argc is not significantly faster than using
rb_scan_args(). Using the following code, counting argc was faster by
by .01 to .05 seconds over *ONE MILLION* iterations. That's 5.0e-008
per iteration. Rather insignificant don't you think?

/* foo.c

···

*
* The purpose of this class is to test the speed of manual counting
* of argc vs using rb_scan_args().
*/
#include "ruby.h"
#include <stdlib.h>

VALUE some_func(int num);
int other_func(VALUE rbNum);

VALUE some_func(int num){
   VALUE rbNum = INT2FIX(num);
   return rbNum;
}

int other_func(VALUE rbNum){
   int y = FIX2INT(rbNum);
   return y;
}

static VALUE test_scan(int argc, VALUE* argv){
   VALUE rbNum;

   rb_scan_args(argc,argv,"01",&rbNum);

   if(NIL_P(rbNum)){
      some_func(55);
   }
   else{
      other_func(argv[0]);
   }

   return rbNum;
}

static VALUE test_count(int argc, VALUE* argv){
   if(argc == 0){
      some_func(55);
   }
   else if(argc == 1){
      other_func(argv[0]);
   }
   else{
      rb_raise(rb_eStandardError,"Wrong number of arguments");
   }

   return Qnil;
}

void Init_foo(){
   VALUE cFoo = rb_define_class("Foo",rb_cObject);
   rb_define_singleton_method(cFoo,"test_scan",test_scan,-1);
   rb_define_singleton_method(cFoo,"test_count",test_count,-1);
}

# extconf.rb
require "mkmf"
create_makefile("foo")

# test_basic.rb
# Proof that you ought to be able to send 'nil'
# Adding debug print statements to the C code proves that it
# does what I intend.

$:.unshift Dir.pwd

require "foo"

Foo.test_scan(5) # works
Foo.test_scan # works
Foo.test_scan(nil) # works (!)

# test_speed.rb
$LOAD_PATH.unshift Dir.pwd

require "foo"
require "benchmark"
include Benchmark

bm do |x|
   x.report("scan"){
      1000000.times{ Foo.test_scan }
   }
   x.report("count"){
      1000000.times{ Foo.test_count }
   }
end

Results of benchmark using on Solaris 9, gcc 3.4.2, 500Mhz Sparc Ultra
IIe, 512 MB RAM:

       user system total real
scan 3.220000 0.000000 3.220000 (3.224927)
count 3.170000 0.000000 3.170000 (3.179761)

Regards,

Dan