I wrote some code that deliberately uses continuations that cross a
fork boundary. When I went to test it, I was surprised by the error:
“continuation called across trap”. I can think of a few reasons why
people might not want to use continuations across fork, but not
knowing Ruby internals particularly well, I couldn’t see a reason why
it should be absolutely prohibited.
Curious person that I am, I removed the test at the beginning of
rb_cont_call in eval.c, and my program ran like it was supposed to.
If I were more familiar with Ruby’s internals, I’d be asking my
question on ruby-core, but I figure that if there’s a super obvious
reason why continuations aren’t allowed across fork, someone here
would be able to point it out.
My reason for experimenting with this isn’t purely theoretical.
Ruby’s IO system (at least under Linux) uses fd_set, which limits the
number of open file descriptors to 1024. Using send_io and recv_io, I
can trivially write a forking server that uses a pool of children to
collectively have more than 1024 connections open at once. The parent
does the accept, then sends the fd down to a child. However, I want
my connections to be DRb connections on top of certificate-verifying
SSL, and I want my certificate password protected.
When I’ve done this before in C, I’ve used
That allows me to read in the password once, and then distribute that
password multiple times before destroying it. Ruby’s OpenSSL
implementation doesn’t use or expose SSL_CTX_set_default_passwd_cb, so
I had the idea of “chopping” DRb’s start_service so that the password
would be done once in the parent. By using continuations, I could then
fork my children and have each of them finish start_service, with the
certificate already unlocked.
I realize there are several other things I could do other than the
above. However, I coded up my solution without realizing that you
can’t call a continuation across fork. Now I’m curious why it’s
Cliff M. [email protected]
P.S., if anyone wants to see my proof-of-concept server, I don’t mind
sharing the code. The trick is to redefine
DRb::DRbTCPSocket.open_server_inaddr_any to save its context before
going to the context to do the forks. However, since this is DRb
implementation specific and since continuations across forks aren’t
actually allowed, my code is more of a curiosity to be poked with ten