GSource-based scheduling considered harmful

(Originally a reply to the message
[email protected], but I decided to break
the thread because it’s a new topic that deserves attention.)

To simplify part of a previous message about GSources for
scheduling Ruby threads:

Using a custom GSource to dispatch Ruby threads as in r2707 causes
the Ruby process to wake up every millisecond. This is bad.

Demonstrating this is simple, using strace:

strace -r ruby -r gtk2 -e Gtk.main

This process should sleep, because nothing is happening. It should
only wake up when something interesting happens.

  • When using the GSource, the process wakes up every millisecond.
  • When using the original poll function, the process wakes up every
    100 milliseconds to call the empty_timeout_func. I do not know
    why the empty_timeout_func exists, except for programs which are
    using Ruby/GTK+ wrongly.
  • When using the poll function and eliminating the empty_timeout_func,
    the process does not wake up. This is what should happen.

—> Drake W.

Hi,

In [email protected]
“[ruby-gnome2-devel-en] GSource-based scheduling considered harmful”
on Wed, 21 Nov 2007 06:02:55 -0600,
Drake W. [email protected] wrote:

This process should sleep, because nothing is happening. It should
only wake up when something interesting happens.

I confirmed that understand sleeping is better. But is there
a problem the current implementation? The current
implementation doesn’t cause busy loop. The process doesn’t
consume any CPU time on my machine. (I used ‘top -p PID’)

I know that the current implementation should be improved
but it will be done step by step.

And I don’t want to add a new API like main_thread_do_async.

Thanks,

kou

Quoth Kouhei S. [email protected], on 2007-11-23 12:36:50 +0900:

consume any CPU time on my machine. (I used ‘top -p PID’)
It’s bad semantics. Sleep should be sleep. But this has some
practical effects also:

  • Normally, power management systems can put the CPU into deeper
    sleep modes when it’s idle for long enough.

    With frequent polling, this becomes impossible, and the machine
    is forced to use more energy.

  • Normally, if a process is completely idle, its pages can be
    swapped out, thus freeing RAM until the process wakes up.

    With frequent polling, some pages cannot be swapped out. Other
    pages might alsonot be, because the kernel will think that this
    process is busy and try to keep it running.

  • Normally, if everything else on the system is idle enough, a
    process doing heavy work might expect to have almost all the CPU
    and cache and such.

    With frequent polling, you make it possible for apparently idle
    backgrounded tasks to degrade the performance of foreground
    tasks by interrupting them frequently.

Not all of these are true on every system. You can probably think of
other reasons.

But other software is not expecting processes to continually wake up
for no reason at all. Broken semantics have a way of propagating, and
that is why I think correct semantics is very important.

I know that the current implementation should be improved
but it will be done step by step.

Using a GSource makes things worse. The custom poll function can
avoid the continual waking entirely, if you’re careful; in fact, when
you remove the empty_timeout_func, it does exactly that. The design
of the custom GSource API makes this impossible.

And I don’t want to add a new API like main_thread_do_async.

I’ll respond to this in a second message.

kou

—> Drake W.

Quoth Kouhei S. [email protected], on 2007-11-23 12:36:50 +0900:

And I don’t want to add a new API like main_thread_do_async.

If adding sources to the main loop from Ruby code is safe, we can make
sure to wake up the main loop ourselves (preferably inside
Ruby-GNOME2, but not necessarily in C) after adding each source.

If this is true, that is good. That is the most compatible with old
Ruby-GNOME2 programs, and does not need a new function.

But adding sources to the main loop from Ruby code may not be safe,
as a result of GLib not knowing how to do appropriate locking.

If this is true, old Ruby-GNOME2 programs that use idle blocks that
way are in trouble no matter what we do.

Then either Ruby-GNOME2 needs to add a new function, or each program
needs to add that function, and it would be better to put it in
Ruby-GNOME2 core because it is very useful to have.

In all of these cases, you have to have some way to wake up the main
loop. That is why the pipe is there.

Now, when I tried making this in Ruby before (with an IOChannel), it
didn’t seem to work; I don’t know why. It looks like it’s possible,
though. Some Ruby code is attached showing multiple styles of this:

  • main-idle-wakeup just adds idle blocks from other threads, then
    uses a wakeup function to make sure the main loop wakes.

    This sometimes randomly segfaults. It sometimes randomly
    produces “broken pipe” errors. This is why I think adding
    idle blocks from other threads may not be safe.

  • main-iochannel-queue uses a queue and has one function from the
    queue called for each byte read from the pipe.

    This works fine, at least on GNU/Linux, except for the problem
    below.

In both of these, if you feed blocks too fast, the UI gets
unresponsive, but that’s hard to avoid anyway. These would still need
a few tweaks before being production-quality. (Checking for the main
thread hanging on its own pipe, for instance.)

Both of these function as described under a version of Ruby-GNOME2
equivalent to r2704, minus the empty_timeout_funcs (to make sure that
they do the wakeup correctly). In that case, they both sleep and do
not wake up until there is something to do.

They also function as described under the current version of
Ruby-GNOME2 in Debian unstable. In that case, of course, they do not
sleep, because they cannot get rid of the empty_timeout_func.

kou

—> Drake W.

Quoth Kouhei S. [email protected], on 2007-11-24 09:09:39 +0900:

Using a GSource makes things worse. The custom poll function can
avoid the continual waking entirely, if you’re careful; in fact, when
you remove the empty_timeout_func, it does exactly that. The design
of the custom GSource API makes this impossible.

The custom poll function doesn’t work on Windows.
Did you see the following codes in old
src/rbglib_mainloop.c?

I am aware of this. Let me try describing the effects with a table:

                       Windows         Unixy
                 +----------------------------------+
  GSource        |  periodic poll   periodic poll   |

custom poll func | doesn’t work sleeps properly |
weird idle thing | periodic poll? (don’t know) |
±---------------------------------+

(I am using “weird idle thing” to describe the previous behavior
on Windows.)

As far as I know, no one has proposed a way to make the main loop
sleep properly on Windows. That means that on Windows, we
periodically wake up. I have no objection to using a GSource on
Windows, because it’s not worse than the original way. When someone
invents a better main loop for Windows, we can remove the GSource and
use that instead.

Using a GSource on Unixy platforms makes things worse. The custom
poll function can sleep correctly. The GSource cannot. This means
that a GSource should not be used on Unixy systems. We can
distinguish between the types of systems using the GLib platform
symbols, just like before. The custom poll function will simply not
be used on Windows, just like before.

Do you consider it better to use a GSource rather than the custom poll
function on Unixy platforms for some reason? If so, why?

(I suspect that proper GLib main loop integration on Windows is very
tricky without changes to the Ruby interpreter code itself, by the
way.)

kou

—> Drake W.

Hi,

In [email protected]
“Re: [ruby-gnome2-devel-en] GSource-based scheduling considered
harmful” on Fri, 23 Nov 2007 14:57:57 -0600,
Drake W. [email protected] wrote:

I confirmed that understand sleeping is better. But is there
a problem the current implementation? The current
implementation doesn’t cause busy loop. The process doesn’t
consume any CPU time on my machine. (I used ‘top -p PID’)

But other software is not expecting processes to continually wake up
for no reason at all. Broken semantics have a way of propagating, and
that is why I think correct semantics is very important.

I know that. So I said ‘understand sleeping is better’ and

I know that the current implementation should be improved
but it will be done step by step.

.

Using a GSource makes things worse. The custom poll function can
avoid the continual waking entirely, if you’re careful; in fact, when
you remove the empty_timeout_func, it does exactly that. The design
of the custom GSource API makes this impossible.

The custom poll function doesn’t work on Windows.
Did you see the following codes in old
src/rbglib_mainloop.c?

#ifdef G_OS_WIN32

undef USE_POLL_FUNC

#endif

Thanks,

kou

In [email protected]
“Re: [ruby-gnome2-devel-en] GSource-based scheduling considered
harmful” on Fri, 23 Nov 2007 22:10:00 -0600,
Drake W. [email protected] wrote:

Do you consider it better to use a GSource rather than the custom poll
function on Unixy platforms for some reason? If so, why?

It’s simple.

I’ll improve this. Could you remember that?
For now, it’s enough that the current implementation
functionally works. Performance tunings and so on are the
next works.

I have an idea to avoid polling but don’t ask me the
detail. I’ll commit that I implement that.

Thanks,

kou

In [email protected]
“Re: [ruby-gnome2-devel-en] GSource-based scheduling considered
harmful” on Fri, 23 Nov 2007 22:38:46 -0600,
Drake W. [email protected] wrote:

I’ll improve this. Could you remember that?
I have an idea to avoid polling but don’t ask me the
detail. I’ll commit that I implement that.

Okay. I’ll wait for that, and then we’ll see what happens. Can you
reply to this thread when you commit it?

If you are interested in the repository, you can subscribe
ruby-gnome2-cvs ML.


kou

Quoth Kouhei S. [email protected], on 2007-11-24 13:21:18 +0900:

Do you consider it better to use a GSource rather than the custom poll
function on Unixy platforms for some reason? If so, why?

It’s simple.

Simple and wrong, yes.

(I happen to think the custom poll function is clearer anyway,
actually, but I don’t want to argue about that.)

I’ll improve this. Could you remember that?
I have an idea to avoid polling but don’t ask me the
detail. I’ll commit that I implement that.

Okay. I’ll wait for that, and then we’ll see what happens. Can you
reply to this thread when you commit it?

For now, it’s enough that the current implementation
functionally works. Performance tunings and so on are the
next works.

I don’t consider this to be only a performance tuning. I consider
failure to put the process to sleep to be semantically broken. Not
broken enough to make things unusable, but still broken.

(The other issue is waking the process up afterwards, but that’s
covered in the other subthread.)

kou

—> Drake W.