Thread safety: Serializing access to ruby interpreter- again

I recently asked about this and got answers, but here I go again:

How can I efficiently serialize access to the ruby interpreter. I have to
make sure all access to the ruby interpreter happens on a particular thread
(not a ruby thread, mind you), so when a different thread attempts to access
the ruby interpreter I need it to have it post the job to a queue and then
wait for a ruby worker thread on the ruby-thread (:-)) to execute whatever
it needs done. Btw, in this mail I use the term ruby-thread, with a dash in
the middle, as a reference to the heavy-weight thread in which the ruby
interpreter executes.

The thing is that if I have a ruby thread on the ruby-thread block on a
heavyweight thread lock the entire ruby-thread is blocked. This is
demonstrated by this bit below (which requires my dotnet module to work). It
simply starts a thread that writes to the console continuously and then the
main thread waits for a signal on anObject. However that wait() call blocks
the entire ruby-thread not just the “green” ruby main thread, so I never get
to see a steady stream of hello worlds, and that is what ruins my day.

require ‘dotnet’

t = Thread.new { while true; sleep 0.1; puts “hello world”; end }

anObject = System::EventArgs.new

System::Threading::Monitor.enter(anObject)
System::Threading::Monitor.wait(anObject) # This blocks the entire
ruby-thread
System::Threading::Monitor.exit(anObject)

t.join

So if I am not allowed to touch the ruby interpreter from other threads than
the ruby-thread how can I wake up a sleeping worker thread when there is
something for it to do?

Is there a heavyweight-thread-safe way to wake up a ruby thread? Shouldn’t
there be?

  • Thomas

Hi,

So if I am not allowed to touch the ruby interpreter from other threads than
the ruby-thread how can I wake up a sleeping worker thread when there is
something for it to do?

Using a pipe (or any IO) as the trigger which wakes Monitor up.

Is there a heavyweight-thread-safe way to wake up a ruby thread? Shouldn’t
there be?

Agree, but wait until 1.9 or perhaps 2.0.

···

At Thu, 18 Sep 2003 09:32:21 +0900, Thomas Sondergaard wrote:


Nobu Nakada

Hi Nobu,

Using a pipe (or any IO) as the trigger which wakes Monitor up.

That doesn’t work. Blocking on a pipe (or any IO) in Windows blocks the
entire ruby process. This sample will just block and get no where in Windows
after writing “hello from main” once, while in Linux it happily prints
“hello from main” once a second.

readPipe, writePipe = IO.pipe

t = Thread.new { while true; sleep 1; puts “got #{readPipe.readline}”; end }

while true
sleep 1
puts “hello from main”
end

t.join

Out of curiousity, why does this happen on Windows? Is it not fixable?

Tom

Hi,

Using a pipe (or any IO) as the trigger which wakes Monitor up.

That doesn’t work. Blocking on a pipe (or any IO) in Windows blocks the
entire ruby process. This sample will just block and get no where in Windows
after writing “hello from main” once, while in Linux it happily prints
“hello from main” once a second.

Socket might work.

Out of curiousity, why does this happen on Windows? Is it not fixable?

Because winsock’s select() supports only socket handles.

···

At Fri, 19 Sep 2003 17:37:09 +0900, Thomas Sondergaard wrote:


Nobu Nakada

Socket might work.

If you say so, I’ll give it a try.

Out of curiousity, why does this happen on Windows? Is it not fixable?

Because winsock’s select() supports only socket handles.

Is all ruby IO implemented in terms of select to avoid blocking the process?
Why doesn’t select block the process, btw?

Thomas

Well, I’m a newbie to Ruby, but unfortunately old enough to remember
writing C code before real threads existed.

I’m guessing Ruby threads work in the old “cooperative” manner, as
follows:

What you would do is replace blocking calls to read operations with
calls to functions implemented like this:

read_from_socket:

for(;:wink: {
check for data available
if data is available
read data and return it
else
schedule some other thread to run // aka “yield”
}

The check for data is done with a NON-BLOCKING select call, ie one that
returns immediately even if no data is currently available. In that
case, the current “thread” stops running and some other “thread” gets
the CPU. Eventually the current “thread” is rescheduled, goes back to
the top of the loop, and checks for data again.

It was always a pain to code this way, because you had to call these
“yielding” versions of standard library calls rather than the real ones,
for every library function that might block.

However given that languages like Ruby always have “glue” code for
calling native operations like read & write, it works quite tidily for
them.

The disadvantage, of course, is that because all threading is
implemented within a single real OS thread, the app can’t take advantage
of systems which really have multiple CPUs. And thread “pre-emption” is
a bit flaky.

I am guessing that Windows doesn’t provide a way to check a socket for
the presence of data without blocking, meaning that this just can’t be
implemented.

Cheers,

Simon

···

On Wed, 2003-09-24 at 21:38, Thomas Sondergaard wrote:

Socket might work.

If you say so, I’ll give it a try.

Out of curiousity, why does this happen on Windows? Is it not fixable?

Because winsock’s select() supports only socket handles.

Is all ruby IO implemented in terms of select to avoid blocking the process?
Why doesn’t select block the process, btw?

Thomas