Background jobs with #fork

Eric Wong normalperson at yhbt.net
Thu Apr 12 23:04:57 UTC 2012


Patrik Wenger <paddor at gmail.com> wrote:
> Thanks for the answers.

No problem.

> Isn't there another way to retrieve the right socket?

Actually, I think my proposed patch (in reply to Hongli) at
http://mid.gmane.org/20120412221022.GA20640@dcvr.yhbt.net
should fix your issue.

> > I can probably writeup a better explanation (perhaps on the usp.ruby
> > (Unix Systems Programming for Ruby mailing list) if this doesn't make
> > sense.

> Yeah I don't really understand this part. The "hanging" Unicorn worker
> can read another request because the client socket wasn't closed
> because it's still open in the child process? I would appreciate a
> better explanation, thank you.

Basically, fork() has a similar effect as dup() in that it creates
multiple references to the same kernel object (the client socket).

close() basically lowers the refcount of a kernel object, when the
refcount is zero, resources inside the kernel are freed.  When
the refcount of a kernel object reaches zero, a shutdown(SHUT_RDWR)
is implied.

This works for 99% of Rack apps since they don't fork() nor call dup()
on the client socket, so refcount==1 when unicorn calls close(), leading
to unicorn setting refcount=0 upon close() => everything is freed.

However, since the client socket increments refcount via fork(),
close() in the parent (unicorn worker) no longer implies
shutdown(SHUT_RDWR).



  parent timeline                  | child timeline
  ------------------------------------------------------------------
                                   |
  accept() -> sockfd created       | (child doesn't exist, yet)
  sockfd.refcount == 1             |
                                   |
  fork()                           | child exists, now
                                   |

    sockfd is shared by both processes now: sockfd.refcount == 2
    if either the child or parent forks again: sockfd.recount += 1

                                   |
  close() => sockfd.recount -= 1   | child continues running

    since sockfd.refcount == 1 at this point, the socket is still
    considerd "alive" by the kernel.  If the child calls close()
    (or exits), sockfd.refcount is decremented again (and now
    reaches zero).

Now, to write this as a commit message :>


More information about the mongrel-unicorn mailing list