Ruby threads? the point?

Saw an archived message yesterday that
said, "when one thread blocks, they all
block".

That matches my experience. I had a timer
thread and an interactive-controller
thread. But no display updates occur
in the timer thread until the user takes
an action in the controller thread.

What is the point of the having "threads"
if they don't allow to use the time that
would otherwise be spent waiting?

Game programs, interactive timers like
the one I was working on, and virtually
any program that really /needs/ threads
requires non-blocking behavior.

On *nix, I can fork a subsidiary process,
and that gives me sufficient control for
a timer app. But on windows, the fork call
says it is "not implemented".

So what is a windows app supposed to do?

Eric Armstrong wrote:

Saw an archived message yesterday that
said, "when one thread blocks, they all
block".

That matches my experience. I had a timer
thread and an interactive-controller
thread. But no display updates occur
in the timer thread until the user takes
an action in the controller thread.

What is the point of the having "threads"
if they don't allow to use the time that
would otherwise be spent waiting?

Game programs, interactive timers like
the one I was working on, and virtually
any program that really /needs/ threads
requires non-blocking behavior.

On *nix, I can fork a subsidiary process,
and that gives me sufficient control for
a timer app. But on windows, the fork call
says it is "not implemented".

So what is a windows app supposed to do?

This hasn't been my experience at all. Do you have a simple example that shows this behavior?
I have a program which involves quite a few threads, some waiting on (multiple) user input, and it works as I would expect (when waiting on input, it does other stuff). I even have a Timer object that works as expected. I also use some threads just to get asynchronous function call behavior.

So I'm a bit confused about your message :slight_smile:

-Justin

Justin Collins wrote:

Eric Armstrong wrote:

Saw an archived message yesterday that
said, "when one thread blocks, they all
block".

This hasn't been my experience at all.

>
Yay! I'd rather be wrong!

Do you have a simple example that shows this behavior?

>
Below. And now that I'm at work, rather than
at home, I've been able to determine that it
works as expected on my Solaris box, but fails
on Windows XP. (There, the display only updates
after pressing spacebar or some other key.)

Perhaps the problem is Curses on Windows??

···

---------------------------------
#!/usr/bin/env ruby

require 'curses'
include Curses

addstr "Press spacebar to start, x to exit: "
refresh

timer_thread = Thread.new do
   100.downto(1) do |i|
     addstr(".." + i.to_s)
     refresh
     sleep 1
   end
end

begin
   while true
     c = getch
     case c
     when 32 # Space
       #timer_thread.run

     when ?x, ?X
       exit
     end
   end
ensure
   Thread.kill(timer_thread)
end

Eric Armstrong wrote:

Justin Collins wrote:

Eric Armstrong wrote:

Saw an archived message yesterday that
said, "when one thread blocks, they all
block".

This hasn't been my experience at all.

>
Yay! I'd rather be wrong!

Do you have a simple example that shows this behavior?

>
Below. And now that I'm at work, rather than
at home, I've been able to determine that it
works as expected on my Solaris box, but fails
on Windows XP. (There, the display only updates
after pressing spacebar or some other key.)

Perhaps the problem is Curses on Windows??

<snip code>

Yep, works for me under Linux. I see the countdown without pressing anything and can exit by pressing 'x'.

Sounds like a Windows issue to me. However, I don't have a way of testing that :slight_smile:

-Justin

Here is an improvisation to your code. This automatically exits at the
end of the set timer, rather than wait for a 'q/Q' from the user.

Greetings,
JS

···

On Sat, 2006-07-29 at 06:18 +0900, Eric Armstrong wrote:

Below. And now that I'm at work, rather than
at home, I've been able to determine that it
works as expected on my Solaris box, but fails
on Windows XP. (There, the display only updates
after pressing spacebar or some other key.)

----
require 'curses'
require 'thwait'

count = ARGV[0] ? ARGV[0].to_i : 100

Curses.addstr "I am going to count #{count}. Press 'q' or 'Q' to
exit.\n"
Curses.refresh

threads = ThreadsWait.new

timer_thread = Thread.new do
  count.downto(1) do |i|
    Curses.addstr(".")
    Curses.refresh
    sleep 1
  end
end
threads.join_nowait timer_thread

wait_thread = Thread.new do
  while true
    c = Curses.getch
    case c
    when ?q, ?Q
      return true
    end
  end
end
threads.join_nowait wait_thread

begin
  threads.next_wait
ensure
  Thread.kill(timer_thread)
  Thread.kill(wait_thread)
end

Tested it with cygwin under Windows XP, and it worked fine. Again,
didn't have to hit spacebar for the thread to start up. Tried with
One-Click Ruby on the same box, and it didn't work.

Windows lack of standard fork is probably the problem here.

N Okia wrote:

Tested it with cygwin under Windows XP, and it worked fine. Again,
didn't have to hit spacebar for the thread to start up. Tried with
One-Click Ruby on the same box, and it didn't work.

Windows lack of standard fork is probably the problem here.

Ok, thanks much. One-click Ruby is /so/ convenient.
It's a real pity that threads don't work in it.

I have cygwin running on my machine at home. What's
the best way to get ruby running on it?

That works for me, at least. But the situation pretty
much kills my chances of distributing my timer app to
the world at large. Bummer.

(It's a simple little app. All it does is remind you
periodically to get off your chair and get some
exercise! A compiled executable would be ideal for
distribution, but I wouldn't have minded pointing
people to the one-click installer...)

Threads do work on it. Try this:

···

On Sat, 29 Jul 2006, Eric Armstrong wrote:

Ok, thanks much. One-click Ruby is /so/ convenient.
It's a real pity that threads don't work in it.

-----

def alpha
   Thread.new do
     'a'.upto('z') {|a| puts a; sleep 2}
   end
end

def numeric
   Thread.new do
     1.upto(55) {|n| puts n; sleep 1}
   end
end

alpha
numeric

Thread.list.each {|t| t.join}

-----

My guess would be that the way curses is implented on Windows is causing the thread blocking behavior.

Kirk Haines

I have cygwin running on my machine at home. What's
the best way to get ruby running on it?

Startup the cygwin setup.exe program. Select Ruby from the list of
packages to install. Finish the installation.

khaines@enigo.com wrote:

Threads do work on (one-click Windows ruby).

>

Try this:

-----

def alpha
    Thread.new do
        'a'.upto('z') {|a| puts a; sleep 2}
    end
end

def numeric
    Thread.new do
        1.upto(55) {|n| puts n; sleep 1}
    end
end

alpha
numeric

Thread.list.each {|t| t.join}

-----

My guess would be that the way curses is implented on Windows is causing the thread blocking behavior.

Interesting. I suppose it could be considered a
Curses bug, then.

So how would you implement a simple keyboard-controller
for threads on Windows?

Or are my options restricted to full-blown GUI
or nothing at all?

N Okia wrote:

I have cygwin running on my machine at home. What's
the best way to get ruby running on it?

Startup the cygwin setup.exe program. Select Ruby from the list of
packages to install. Finish the installation.

Spectacular. Didn't know about the program.
Thanks much.

You could always just implement your own text-based captive interface,
if that's what you really want.

···

On Sat, Jul 29, 2006 at 08:07:10AM +0900, Eric Armstrong wrote:

So how would you implement a simple keyboard-controller
for threads on Windows?

Or are my options restricted to full-blown GUI
or nothing at all?

--
CCD CopyWrite Chad Perrin [ http://ccd.apotheon.org ]
"The first rule of magic is simple. Don't waste your time waving your
hands and hopping when a rock or a club will do." - McCloctnick the Lucid

Eric Armstrong wrote:

Interesting. I suppose it could be considered a
Curses bug, then.

My experience has been that IO is blocking in Ruby/Windows. Just try
creating a background thread to insert into an MSSQL server through
WIN32OLE+ADO. It's an exercise in futility. :slight_smile:

I even had a seperate process that exposed a ThreadQueue in the DRb
server for the client to push onto, but while the server was busy, the
client was blocked. _In a completely different process, only connected
through DRb!_

I can appreciate the general Ruby community attitude that OS-Threading
isn't absolutely vital when you're on Linux and have _fork_ at the
ready, but under Windows it's very painful and really limits the types
of applications you can build with Ruby (with a reasonable amount of
effort) IME.

Maybe you can use kbhit() and getch() from msvcrt.dll

kbhit returns whether there are any keystrokes in the buffer
getc returns them.

So, what I do is (or something similar, I don't have the exact code here):

kbhit = Win32API.new('msvcrt','kbhit',...)
getch = ...

def wait_for_keypress
  while true
    if kbhit.Call
      ch = getch.Call while kbhit.Call # to flush the buffer
      return
    end
    sleep 0.1 # this should allow other threads to run
  end
end

J.

···

On 7/29/06, Eric Armstrong <Eric.Armstrong@sun.com> wrote:

So how would you implement a simple keyboard-controller
for threads on Windows?

Chad Perrin wrote:

So how would you implement a simple keyboard-controller
for threads on Windows?

Or are my options restricted to full-blown GUI
or nothing at all?

You could always just implement your own text-based captive interface,
if that's what you really want.

Riiigghttt....

Given infinite time and energy, I could build everything
I ever wanted or needed, and then some.

I was kind of looking for a simple solution that "just
works" until I can get around to adding the GUI I
eventually plan to add anyway.

It sounds so seductively simple, too: "just implement
my own text-based captive interface". (What the heck
is that, anyway?)
:_)

···

On Sat, Jul 29, 2006 at 08:07:10AM +0900, Eric Armstrong wrote:

This has been brought up at previous Ruby Conferences. It is my understanding that it is being worked on. So, relax and watch the blinking lights.

-- Matt
It's not what I know that counts. It's what I can remember in time to use.

···

On Sat, 29 Jul 2006, Sam Smoot wrote:

I can appreciate the general Ruby community attitude that OS-Threading
isn't absolutely vital when you're on Linux and have _fork_ at the
ready, but under Windows it's very painful and really limits the types
of applications you can build with Ruby (with a reasonable amount of
effort) IME.

Sam Smoot wrote:

My experience has been that IO is blocking in Ruby/Windows. Just try
creating a background thread to insert into an MSSQL server through
WIN32OLE+ADO. It's an exercise in futility. :slight_smile:

Ah. That explains why the two counter threads
worked. There was no I/O going on. But outside
of computational supercomputers, there is zero
point in having threads if they don't let you
keep doing things while waiting for I/O.

...under Windows it's very painful and really limits the types
of applications you can build with Ruby...

I'm forced to agree. My little application is dead in the
water. Windows users would have been a major market for it.
It would have helped drive ruby installs, which would have
helped to drive awareness. Similarly with game programs,
music programs, and a wide variety of other interesting
applications.

This limitation is a severe shot in the foot with respect
to platform independence.

Jan Svitok wrote:

kbhit returns whether there are any keystrokes in the buffer
getc returns them.

Nice! Didn't know about that kbhit.
That has promise. I wonder if there is a
multi-platform version of that API?

With that capability, I can dispense with
threads altogether, sleep for 1 second
intervals, and look for keyboard input
every time I wake up.

A brain-dead implementation is good enough
for this particular beast...
:_)

What is what -- a captive interface? It's an interface that forces you
to do everything you want to do with a program without letting you do
other things between minor operations. For instance, a non-captive CLI
is something like apt-get on Debian GNU/Linux or ipconfig on Windows,
while a captive interface is something like an ncurses-based program
that starts, takes input, gives output, and doesn't go away until you're
all done with it.

···

On Sat, Jul 29, 2006 at 08:42:58AM +0900, Eric Armstrong wrote:

Chad Perrin wrote:
>On Sat, Jul 29, 2006 at 08:07:10AM +0900, Eric Armstrong wrote:
>>So how would you implement a simple keyboard-controller
>>for threads on Windows?
>>
>>Or are my options restricted to full-blown GUI
>>or nothing at all?
>
>You could always just implement your own text-based captive interface,
>if that's what you really want.
>
Riiigghttt....

Given infinite time and energy, I could build everything
I ever wanted or needed, and then some.

I was kind of looking for a simple solution that "just
works" until I can get around to adding the GUI I
eventually plan to add anyway.

It sounds so seductively simple, too: "just implement
my own text-based captive interface". (What the heck
is that, anyway?)

--
CCD CopyWrite Chad Perrin [ http://ccd.apotheon.org ]
unix virus: If you're using a unixlike OS, please forward
this to 20 others and erase your system partition.

Eric-

  Have a look at the highline gem. Its a great library for console based question and menu systems. And it works with threading.

-Ezra

···

On Jul 28, 2006, at 4:42 PM, Eric Armstrong wrote:

Chad Perrin wrote:

On Sat, Jul 29, 2006 at 08:07:10AM +0900, Eric Armstrong wrote:

So how would you implement a simple keyboard-controller
for threads on Windows?

Or are my options restricted to full-blown GUI
or nothing at all?

You could always just implement your own text-based captive interface,
if that's what you really want.

Riiigghttt....

Given infinite time and energy, I could build everything
I ever wanted or needed, and then some.

I was kind of looking for a simple solution that "just
works" until I can get around to adding the GUI I
eventually plan to add anyway.

It sounds so seductively simple, too: "just implement
my own text-based captive interface". (What the heck
is that, anyway?)
:_)