Race-free server startup and shutdown can be a tricky task. The following example illustrates, how a TCP server can be started and interrupted properly.

require "eventbox"
require "socket"

class MyServer < Eventbox
  yield_call def init(bind, port, result)
    @count = 0
    @server = start_serving(bind, port, result)
  end

  action def start_serving(bind, port, init_done)
    serv = TCPServer.new(bind, port)
  rescue => err
    init_done.raise err
  else
    init_done.yield

    loop do
      begin
        conn = Thread.handle_interrupt(Stop => :on_blocking) do
          serv.accept
        end
      rescue Stop => st
        serv.close
        st.stopped.yield
        break
      else
        MyConnection.new(conn, self)
      end
    end
  end

  sync_call def count
    @count += 1
  end

  yield_call def stop(result)
    @server.raise(Stop.new(result))
  end

  class Stop < RuntimeError
    def initialize(stopped)
      @stopped = stopped
    end
    attr_reader :stopped
  end
end

class MyConnection < Eventbox
  action def init(conn, server)
    conn.write "Hello #{server.count}"
  ensure
    conn.close
  end
end

The server can now be started like so.

s = MyServer.new('localhost', 12345)

10.times.map do
  Thread.new do
    TCPSocket.new('localhost', 12345).read
  end
end.each { |th| p th.value }

s.stop

It prints some output like this:

"Hello 2"
"Hello 1"
"Hello 7"
"Hello 8"
"Hello 3"
"Hello 9"
"Hello 5"
"Hello 6"
"Hello 4"
"Hello 10"