Issue with threads and socket accept

Hi,

I'm writing a multi-threaded server, and would like to stop it on a
signal (SIGINT on Ctrl-C in this case).
The problem is that after the first connection is accepted, and then
Ctrl-C is hit, the "while accept" loop doesn't break. If you hit Ctrl-
C before any connections, it just works fine. And the single-threaded
version, of course, works
as well.

Could somebody explain what am I doing wrong?

Below is a simple application that reproduces the problem. Change the
last line to "server.serve_seq" to run a single-threaded version.

Thanks in advance!

#!/usr/local/bin/ruby -w

require 'socket'

class MyServer
  def initialize
    @servsock = TCPServer.new("127.0.0.1", 1111)
  end

  def work(sock)
    begin
      lines = 0
      lines += 1 while sock.gets
      puts "Got #{lines} lines."
    rescue Exception => e
      print "exception: "; p e
    ensure
      sock.close
    end
  end

  def serve_mt
    while clisock = @servsock.accept
      Thread.new(clisock) { |sock| work(sock) }
    end
  end

  def serve_seq
    while sock = @servsock.accept
      work(sock)
    end
  end

  def shutdown
    return nil if @servsock.nil? or @servsock.closed?
    puts "Shutting down..."
    @servsock.close
  end
end

server = MyServer.new
Signal.trap("INT") { server.shutdown }
server.serve_mt

Errr..."not investigating deep and strange mysteries of the Ruby interpreter
signal handling that are not comprehensible even after reading the source?"

I hope I get a point for that answer.

But anyway, much as I'd like to know *why* the signal handling is set up
to ignore signals when waiting in accept(), a situation on which the
source code that does this is particularly silent, I just live with the
solution, which is:

Do a select() before your accept(), and wait until the socket in
question becomes readable.

You and I are not the only ones caught by this, as it happens. The native
Ruby FastCGI code has the same issue.

cjs

···

On 2007-12-12 01:18 +0900 (Wed), Vasyl Smirnov wrote:

I'm writing a multi-threaded server, and would like to stop it on a
signal (SIGINT on Ctrl-C in this case).
The problem is that after the first connection is accepted, and then
Ctrl-C is hit, the "while accept" loop doesn't break....
Could somebody explain what am I doing wrong?

--
Curt Sampson <cjs@starling-software.com> +81 90 7737 2974
Mobile sites and software consulting: http://www.starling-software.com

Without addressing your specific question, let me suggest that you
investigate the Ruby/Eventmachine library. It should make your application
easier to develop.

···

On Dec 11, 2007 11:18 AM, Vasyl Smirnov <vasyl.smirnov@gmail.com> wrote:

Hi,

I'm writing a multi-threaded server, and would like to stop it on a
signal (SIGINT on Ctrl-C in this case).
The problem is that after the first connection is accepted, and then
Ctrl-C is hit, the "while accept" loop doesn't break. If you hit Ctrl-
C before any connections, it just works fine. And the single-threaded
version, of course, works
as well.

Could somebody explain what am I doing wrong?

Thanks, Francis, I'm just reading the docs for your library!

···

On Dec 11, 7:53 pm, "Francis Cianfrocca" <garbageca...@gmail.com> wrote:

Without addressing your specific question, let me suggest that you
investigate the Ruby/Eventmachine library. It should make your application
easier to develop.

Errr..."not investigating deep and strange mysteries of the Ruby interpreter
signal handling that are not comprehensible even after reading the source?"

I hope I get a point for that answer.

You probably do :slight_smile:

But anyway, much as I'd like to know *why* the signal handling is set up
to ignore signals when waiting in accept(), a situation on which the
source code that does this is particularly silent, I just live with the
solution, which is:

Well, actually the signal handler gets called, it calls shutdown,
outputs a message
to the stdout and closes the server socket. And the main thread is
still inside accept.

Do a select() before your accept(), and wait until the socket in
question becomes readable.

I've tried this, with the same success:

  def serve_mt2
    until @servsock.closed?
      IO.select([@servsock])
      clisock = @servsock.accept
      Thread.new(clisock) { |sock| work(sock) }
    end
  end

···

On Dec 11, 7:43 pm, Curt Sampson <c...@cynic.net> wrote: