Ruby BUG when using PStore and fork

PStore does not appear to play well with fork. This script

#!/usr/bin/env ruby

require 'pstore’
store = PStore.new("/tmp/pstore")
store.transaction do
fork do
store.abort
end
Process.wait
exit
end

… results in:

…/pstore_fork_bug.rb:7: [BUG] Unknown longjmp status 7
ruby 1.6.7 (2002-03-01) [i586-linux]

The motivation for doing this is a little GUI applicaiton I wrote that
keeps URLs in a PStore archive and forks a browser when I click a
button. The forked process maintains a lock on the PStore archive,
which is annoying, so I tried to get it to release its lock and hit
this bug message.

Regards,

Jeremy Henty

Hi,

···

In message “Ruby BUG when using PStore and fork” on 02/12/10, Jeremy Henty jeremy@chaos.org.uk writes:

PStore does not appear to play well with fork. This script

#!/usr/bin/env ruby

require ‘pstore’
store = PStore.new(“/tmp/pstore”)
store.transaction do
fork do
store.abort
end
Process.wait
exit
end

… results in:

…/pstore_fork_bug.rb:7: [BUG] Unknown longjmp status 7
ruby 1.6.7 (2002-03-01) [i586-linux]

Ah, you can’t. “fork” creates copy of the process. Copied child
process cannot affect the parent process. How can we abort
transaction from the child process.

Current pstore does not work well with threads either.

						matz.

i have gotten around this problem by spawning another process using the unix
at command with a time of ‘now’

in otherwords, instead of forking spawn a process which does the same thing to
the pstore now. pstores do work transactionally between processes.
additionally, you may want to copy the ‘being updated’ pstore to a temnam’d
file so any update transaction will not block the original process.

-a

···

On Tue, 10 Dec 2002, Yukihiro Matsumoto wrote:

In message “Ruby BUG when using PStore and fork” > on 02/12/10, Jeremy Henty jeremy@chaos.org.uk writes:

PStore does not appear to play well with fork. This script

#!/usr/bin/env ruby

require ‘pstore’
store = PStore.new(“/tmp/pstore”)
store.transaction do
fork do
store.abort
end
Process.wait
exit
end

… results in:

…/pstore_fork_bug.rb:7: [BUG] Unknown longjmp status 7
ruby 1.6.7 (2002-03-01) [i586-linux]

Ah, you can’t. “fork” creates copy of the process. Copied child
process cannot affect the parent process. How can we abort
transaction from the child process.

Current pstore does not work well with threads either.

====================================

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ahoward@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================
SIGSIG – signature too long (core dumped)

In article 1039475291.072573.1121.nullmailer@picachu.netlab.jp,
Yukihiro Matsumoto wrote:

PStore does not appear to play well with fork.

Update: it is nothing to do with PStore really. The problem is throw
(which PStore uses to implement abort).

#!/usr/bin/env ruby

catch :foo do
fork do
throw :foo
end
Process.wait
exit
end

foo.rb:5: [BUG] Unknown longjmp s\tatus 7
ruby 1.6.7 (2002-03-01) [i586-linux]

Ah, you can’t. “fork” creates copy of the process. Copied child
process cannot affect the parent process. How can we abort
transaction from the child process.

Well, I didn’t really want to abort the transaction. I just wanted
the child to lose the lock on the pstore file. I was sort of
relying on the isolation of the child from the parent to get away
with calling abort in the child without anything happening in the
parent. Perhaps this is my just reward for trying such a dirty trick!
:slight_smile:

If I comment out “file.flock(File::LOCK_EX)” in pstore.rb then things
work as I would like. But I don’t want to lose the protection of
locking for all pstore applications. I suspect the right thing to do
is to fork before opening the pstore and use IPC to tell the child to
spawn browsers. I had hoped to avoid such complications, but maybe I
should bite the bullet this time. Unless someone has a better
solution?

Regards,

Jeremy Henty

···

In message “Ruby BUG when using PStore and fork” > on 02/12/10, Jeremy Henty jeremy@chaos.org.uk writes:

Hi,

PStore does not appear to play well with fork.

Update: it is nothing to do with PStore really. The problem is throw
(which PStore uses to implement abort).

Update: PStore does not work well with fork, because “throw” does not
work with fork.

If I comment out “file.flock(File::LOCK_EX)” in pstore.rb then things
work as I would like. But I don’t want to lose the protection of
locking for all pstore applications. I suspect the right thing to do
is to fork before opening the pstore and use IPC to tell the child to
spawn browsers. I had hoped to avoid such complications, but maybe I
should bite the bullet this time. Unless someone has a better
solution?

Can you fork before starting transaction?

						matz.
···

In message “Re: Ruby BUG when using PStore and fork” on 02/12/13, Jeremy Henty jeremy@chaos.org.uk writes:

In article Pine.LNX.4.33.0212092317410.8611-100000@eli.fsl.noaa.gov,
ahoward wrote:

i have gotten around this problem by spawning another process using
the unix at command with a time of ‘now’

Neat trick! OK, I have implemented it and it will do for now. But
it’s not ideal: I would really like the spawned process’s stdin,
stdout and stderr to be the same as the original. Which AFAIK means I
would have to fork. But thanks anyway.

Jeremy Henty

ahoward ahoward@fsl.noaa.gov writes:

i have gotten around this problem by spawning another process using
the unix at command with a time of ‘now’

I would try

system("thecommand &")

instead.

This doesn’t solve the problem, which was that the child process
inherits a lock on the pstore archive. This stops anything else using
the archive even though no process is currently doing anything with
it. The “at now” trick works because the child process simply
schedules a job to do the business and terminates. The job itself is
forked from a daemon, so it does not get the lock.

I now have a better solution which preforks a child before opening
the archive. The parent then tells the child when it needs a new
process created and the child does the work. So the forking process
has no lock, hence there is no proliferation of unnecessary locks.

Regards,

Jeremy Henty

···

In article 873cp128ap.fsf@squeaker.lickey.com, Matt Armstrong wrote:

ahoward ahoward@fsl.noaa.gov writes:

i have gotten around this problem by spawning another process using
the unix at command with a time of ‘now’

system("thecommand &")

In article 1039771961.918542.10979.nullmailer@picachu.netlab.jp,
Yukihiro Matsumoto wrote:

Update: PStore does not work well with fork, because “throw” does
not work with fork.

So is this a bug or a feature? It is certainly alarming that you can
get a BUG message with a few lines of pure Ruby. And why does the
child lose the ability to throw?

Can you fork before starting transaction?

I can’t fork the browser beforehand because the pstore holds the list
of URLs. But I realised I could prefork a browser-spawning process
with IO.popen(“-”,“w”) and send it URLs from the parent. Now the
browsers don’t get the lock and it works very nicely.

Thanks

Jeremy Henty

Hi,

Update: PStore does not work well with fork, because “throw” does
not work with fork.

So is this a bug or a feature? It is certainly alarming that you can
get a BUG message with a few lines of pure Ruby. And why does the
child lose the ability to throw?

It’s a feature. UNIX fork creates a copy of the process. The parent
process and the child process no longer shares memory space, so that
there’s “almost” no way to send a exception (or a throw) from a child
to a parent.

Parpahs the message is misleading. It can be changed. But the
behavior will remain unchanged.

						matz.
···

In message “Re: Ruby BUG when using PStore and fork” on 02/12/14, Jeremy Henty jeremy@chaos.org.uk writes:

Jeremy Henty jeremy@chaos.org.uk writes:

···

In article 873cp128ap.fsf@squeaker.lickey.com, Matt Armstrong wrote:

ahoward ahoward@fsl.noaa.gov writes:

i have gotten around this problem by spawning another process
using the unix at command with a time of ‘now’

system("thecommand &")

This doesn’t solve the problem, which was that the child process
inherits a lock on the pstore archive.

You are right. Question is, should system() pass on any file
descriptors beyond stdin/stdout/stderr to the child process?
Probably. Forking child processes within a PStore transaction is
probably just bad practice.

Jeremy Henty jeremy@chaos.org.uk writes:

ahoward ahoward@fsl.noaa.gov writes:

i have gotten around this problem by spawning another process using
the unix at command with a time of ‘now’

system("thecommand &")

This doesn’t solve the problem, which was that the child process
inherits a lock on the pstore archive.

After experimenting, there is a way to close the PStore file in the
child process in ruby 1.7.

This program demonstrates it. In a PStore transaction, it forks a
child that just loops 5 times printing a message, sleeping between.
Without the call to close_extra_files, the child blocks the parent’s
2nd attempt to start a transaction. With the call, it ends up closing
the PStore file it inherits from its parent and allows the parent to
open the PStore file again.

Unfortunately, I don’t know a way to close file descriptors 3-1024 in
ruby 1.6.

require ‘pstore’
store = PStore.new(‘pstore.test’)

def close_extra_files
3.upto(1024) { |i|
begin
File.open(i).close
rescue Errno::EINVAL
end
}
end

store.transaction { |ps|
puts “parent forking child”
fork {
close_extra_files
5.times {
sleep(1)
puts “child is alive”
}
exit!
}
}

puts “parent trying for second transaction”
store.transaction { |ps|
puts “parent got second transaction”
}

Process.wait

···

In article 873cp128ap.fsf@squeaker.lickey.com, Matt Armstrong wrote:

In article 1039877646.586263.1663.nullmailer@picachu.netlab.jp,
Yukihiro Matsumoto wrote:

Update: PStore does not work well with fork, because “throw” does
not work with fork.

So is this a bug or a feature? It is certainly alarming that you can
get a BUG message with a few lines of pure Ruby. And why does the
child lose the ability to throw?

It’s a feature. UNIX fork creates a copy of the process. The
parent process and the child process no longer shares memory space,
so that there’s “almost” no way to send a exception (or a throw)
from a child to a parent.

Perhaps I haven’t made my question very clear. I understand how Unix
fork works (as far as this discussion goes anyway) and I am not
expecting the child to somehow throw to the parent. What I don’t
understand is why the child can’t do a throw in its own memory space.
After all, it contains its own copy of the stack. It should behave in
every respect like its parent (unless it chooses to examine the return
value of fork() or get_ppid() …). Why can’t it unwind its own
stack (as copied from the parent) to the catch block?

I tried tweaking my test program as follows:

#!/usr/bin/env ruby

catch :foo do
fork
throw :foo
puts “You should not see this”
end
puts “Hi from #{$$}”

and got:

Hi from 20572
Hi from 20571

It works! So it seems that doing a throw after forking is actually
OK. But if that is so, why is there a problem with “fork do throw
:foo end”? Is it because fork assumes that the flow of control in any
supplied block must terminate normally at the end of block rather than
jumping out in the middle? I guess that is reasonable, in which case
the solution is just a more helpful error message. Otherwise I am
still confused and would appreciate any further explanations you can
give.

Regards,

Jeremy Henty

···

In message “Re: Ruby BUG when using PStore and fork” > on 02/12/14, Jeremy Henty jeremy@chaos.org.uk writes:

Jeremy Henty jeremy@chaos.org.uk writes:

system("thecommand &")

This doesn’t solve the problem, which was that the child process
inherits a lock on the pstore archive.

You are right. Question is, should system() pass on any file
descriptors beyond stdin/stdout/stderr to the child process?

??? Is this relevant? The problem is the child inheriting locks, not
file descriptors. They are not the same. (At least, I thought they
weren’t.)

Forking child processes within a PStore transaction is probably just
bad practice.

I agree! In fact I have learned that forking when in the middle of
doing anything is perilous!

Jeremy Henty

···

In article 87y96sy1u8.fsf@squeaker.lickey.com, Matt Armstrong wrote:

In article 873cp128ap.fsf@squeaker.lickey.com, Matt Armstrong wrote:

Aha! Presumably because (according to the close() man page) closing
the file descriptor releases any locks on the file. Maybe PStore
could do with a new method that closes its file?

Regards,

Jeremy Henty

···

In article 87vg1wxu1x.fsf@squeaker.lickey.com, Matt Armstrong wrote:

After experimenting, there is a way to close the PStore file in the
child process in ruby 1.7.

This program demonstrates it. In a PStore transaction, it forks a
child that just loops 5 times printing a message, sleeping between.
Without the call to close_extra_files, the child blocks the parent’s
2nd attempt to start a transaction. With the call, it ends up closing
the PStore file it inherits from its parent and allows the parent to
open the PStore file again.

Hi,

···

In message “Re: Ruby BUG when using PStore and fork” on 02/12/16, Jeremy Henty jeremy@chaos.org.uk writes:

It works! So it seems that doing a throw after forking is actually
OK. But if that is so, why is there a problem with “fork do throw
:foo end”? Is it because fork assumes that the flow of control in any
supplied block must terminate normally at the end of block rather than
jumping out in the middle? I guess that is reasonable, in which case
the solution is just a more helpful error message. Otherwise I am
still confused and would appreciate any further explanations you can
give.

Your guess is right, and I admit I’ve forgot about this bahavior.
So do you recommend any better message?

						matz.

In article 1040027420.953407.30050.nullmailer@picachu.netlab.jp,
Yukihiro Matsumoto wrote:

… why is there a problem with “fork do throw :foo end”? Is it
because fork assumes that the flow of control in any supplied block
must terminate normally at the end of block rather than jumping out
in the middle?

So do you recommend any better message?

Off the top of my head, something like “fork: supplied block must
terminate normally”, assuming that “normally” is the appropriate word
here. The message should make it clear this requirement is specific
to fork: we don’t want people to get the impression that all blocks
are so restricted.

I am not really familiar with the Ruby house style for error messages,
maybe others can suggest a better wording.

Regards,

Jeremy Henty

···

In message “Re: Ruby BUG when using PStore and fork” > on 02/12/16, Jeremy Henty jeremy@chaos.org.uk writes:

Hi,

···

In message “Re: Ruby BUG when using PStore and fork” on 02/12/17, Jeremy Henty jeremy@chaos.org.uk writes:

So do you recommend any better message?

Off the top of my head, something like “fork: supplied block must
terminate normally”, assuming that “normally” is the appropriate word
here. The message should make it clear this requirement is specific
to fork: we don’t want people to get the impression that all blocks
are so restricted.

I am not really familiar with the Ruby house style for error messages,
maybe others can suggest a better wording.

For your information: the latest 1.6.8 snapshot says “uncaught throw”.

						matz.

In article 1040187828.294964.16002.nullmailer@picachu.netlab.jp,
Yukihiro Matsumoto wrote:

So do you recommend any better message?

… something like “fork: supplied block must terminate normally”,
assuming that “normally” is the appropriate word here.

For your information: the latest 1.6.8 snapshot says “uncaught
throw”.

That is certainly accurate, but it might be confusing to someone who
wrote “catch :foo do fork do throw :foo end end”. It looks as though
the throw should be caught! It would be nice if the message could
indicate that fork does not allow the block to transfer control
outside of itself. I have no idea whether this is feasible.

By the way, are there any other cases where the flow of control in a
block is restricted in this way, or is fork a special case?

Regards,

Jeremy Henty

···

In message “Re: Ruby BUG when using PStore and fork” > on 02/12/17, Jeremy Henty jeremy@chaos.org.uk writes:

Hi,

···

In message “Re: Ruby BUG when using PStore and fork” on 02/12/20, Jeremy Henty jeremy@chaos.org.uk writes:

By the way, are there any other cases where the flow of control in a
block is restricted in this way, or is fork a special case?

fork is a special case, unless you (or others) intentionally do so.

						matz.