Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Add EventMachine::attach_server #271

Closed
wants to merge 1 commit into from

6 participants

@ramonmaruko

EventMachine::attach_Server is for attaching an arbitrary server IO
object or file descriptor to the event loop.

Tries to fix #93.

Signed-off-by: Ramon Marco L. Navarro ramonmaruko@gmail.com

@FooBarWidget FooBarWidget Add EventMachine::attach_server
EventMachine::attach_Server is for attaching an arbitrary server IO
object or file descriptor to the event loop.

Tries to fix #93.

Signed-off-by: Ramon Marco L. Navarro <ramonmaruko@gmail.com>
919b644
@macournoyer

What are the chances of this getting in 1.0?

I'm working on v2 of Thin and relying on this: https://github.com/macournoyer/thin/blob/v2/lib/thin/server.rb#L182.

Also it seems to me this would allow moving all of the socket creation code to Ruby. Thus making implementing IPv6 support and such trivial.

@ibc

This would be a really cool enhacement to EventMachine. Hopefully EM developers could accept this pull request.

@ibc

One question: would EventMachine::attach_server also allow adding a Ruby Socket (i.e. TCPSocket or OpenSSL::Socket) for acting as a connection client? this is, for establishing a connection with another server, rather than accepting incoming connections?

If not, would it be very difficult to add a EventMachine::attach_client? :)

@macournoyer
@ibc

Sorry, you are right. Let me just a question:

Should it already be possible to use OpenSSL::Socket within EM via EM.attach and establish a SSL connection from EM to an external server?

@macournoyer
@ibc

Yes, but I don't want to use EM's SSL, it leaks, there is an open issue showing it.

@ibc
ibc commented

Hi, this pull request (along with others) has been included in EventMachine-LE: https://github.com/ibc/EventMachine-LE

@marcoow

What's the state of this? I'm trying to run Goliath servers with stripe's einhorn and thus would need sth. like this.

This was referenced
@ramonmaruko

I think it's safe to close this now that #465 has been merged.

@sodabrew sodabrew commented on the diff
lib/eventmachine.rb
((6 lines not shown))
+ # an already existing file descriptor instead of having EventMachine create
+ # one. The descriptor must be accept()able and will be set as non-blocking.
+ #
+ # Unlike start_server however, the file descriptor is not closed when
+ # EventMachine is released, so you will have to do any cleanups manually.
+ # If +io+ is an IO object then a reference to it will be kept until
+ # EventMachine is released, so that the file descriptor isn't accidentally
+ # closed by the garbage collector.
+ def EventMachine::attach_server io, handler = nil, *args, &block
+ klass = klass_from_handler(Connection, handler, *args)
+ if io.respond_to?(:fileno)
+ fd = defined?(JRuby) ? JRuby.runtime.getDescriptorByFileno(io.fileno).getChannel : io.fileno
+ else
+ fd = io
+ end
+ s = attach_server_fd(fd)
@sodabrew Collaborator
sodabrew added a note

Although this was superseded by #465, I am looking into JRuby test failures in #551 and I don't see where attach_server_fd is defined for the JRuby implementation. So even though you're making sure to use JRuby.runtime.getDescriptorByFileno, it's not getting passed into a valid function, AFAICT.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 12, 2011
  1. @FooBarWidget @ramonmaruko

    Add EventMachine::attach_server

    FooBarWidget authored ramonmaruko committed
    EventMachine::attach_Server is for attaching an arbitrary server IO
    object or file descriptor to the event loop.
    
    Tries to fix #93.
    
    Signed-off-by: Ramon Marco L. Navarro <ramonmaruko@gmail.com>
This page is out of date. Refresh to see the latest.
View
10 ext/cmain.cpp
@@ -149,6 +149,16 @@ extern "C" int evma_detach_fd (const unsigned long binding)
return -1;
}
+/*********************
+evma_attach_server_fd
+**********************/
+
+extern "C" const unsigned long evma_attach_server_fd (int file_descriptor)
+{
+ ensure_eventmachine("evma_attach_server_fd");
+ return EventMachine->AttachServerFD (file_descriptor);
+}
+
/************************
evma_get_file_descriptor
************************/
View
11 ext/ed.cpp
@@ -50,7 +50,8 @@ bool SetSocketNonblocking (SOCKET sd)
EventableDescriptor::EventableDescriptor
****************************************/
-EventableDescriptor::EventableDescriptor (int sd, EventMachine_t *em):
+EventableDescriptor::EventableDescriptor (int sd, EventMachine_t *em, bool autoclose):
+ bAutoClose (autoclose),
bCloseNow (false),
bCloseAfterWriting (false),
MySocket (sd),
@@ -118,7 +119,9 @@ EventableDescriptor::~EventableDescriptor()
}
MyEventMachine->NumCloseScheduled--;
StopProxy();
- Close();
+ if (bAutoClose) {
+ Close();
+ }
}
@@ -1348,8 +1351,8 @@ void LoopbreakDescriptor::Write()
AcceptorDescriptor::AcceptorDescriptor
**************************************/
-AcceptorDescriptor::AcceptorDescriptor (int sd, EventMachine_t *parent_em):
- EventableDescriptor (sd, parent_em)
+AcceptorDescriptor::AcceptorDescriptor (int sd, EventMachine_t *parent_em, bool autoclose):
+ EventableDescriptor (sd, parent_em, autoclose)
{
#ifdef HAVE_EPOLL
EpollEvent.events = EPOLLIN;
View
5 ext/ed.h
@@ -36,7 +36,7 @@ class EventableDescriptor
class EventableDescriptor: public Bindable_t
{
public:
- EventableDescriptor (int, EventMachine_t*);
+ EventableDescriptor (int, EventMachine_t*, bool = true);
virtual ~EventableDescriptor();
int GetSocket() {return MySocket;}
@@ -98,6 +98,7 @@ class EventableDescriptor: public Bindable_t
virtual uint64_t GetNextHeartbeat();
private:
+ bool bAutoClose;
bool bCloseNow;
bool bCloseAfterWriting;
@@ -310,7 +311,7 @@ class AcceptorDescriptor
class AcceptorDescriptor: public EventableDescriptor
{
public:
- AcceptorDescriptor (int, EventMachine_t*);
+ AcceptorDescriptor (int, EventMachine_t*, bool = true);
virtual ~AcceptorDescriptor();
virtual void Read();
View
56 ext/em.cpp
@@ -1426,6 +1426,62 @@ int EventMachine_t::DetachFD (EventableDescriptor *ed)
return fd;
}
+/******************************
+EventMachine_t::AttachServerFD
+*******************************/
+
+const unsigned long EventMachine_t::AttachServerFD (int sd_accept)
+{
+ unsigned long output_binding = 0;
+
+ { // set reuseaddr to improve performance on restarts.
+ int oval = 1;
+ if (setsockopt (sd_accept, SOL_SOCKET, SO_REUSEADDR, (char*)&oval, sizeof(oval)) < 0) {
+ //__warning ("setsockopt failed while creating listener","");
+ goto fail;
+ }
+ }
+
+ { // set CLOEXEC. Only makes sense on Unix
+ #ifdef OS_UNIX
+ int cloexec = fcntl (sd_accept, F_GETFD, 0);
+ assert (cloexec >= 0);
+ cloexec |= FD_CLOEXEC;
+ fcntl (sd_accept, F_SETFD, cloexec);
+ #endif
+ }
+
+ if (listen (sd_accept, 100)) {
+ //__warning ("listen failed");
+ goto fail;
+ }
+
+ {
+ // Set the acceptor non-blocking.
+ // THIS IS CRUCIALLY IMPORTANT because we read it in a select loop.
+ if (!SetSocketNonblocking (sd_accept)) {
+ //int val = fcntl (sd_accept, F_GETFL, 0);
+ //if (fcntl (sd_accept, F_SETFL, val | O_NONBLOCK) == -1) {
+ goto fail;
+ }
+ }
+
+ { // Looking good.
+ AcceptorDescriptor *ad = new AcceptorDescriptor (sd_accept, this, false);
+ if (!ad)
+ throw std::runtime_error ("unable to allocate acceptor");
+ Add (ad);
+ output_binding = ad->GetBinding();
+ }
+
+ return output_binding;
+
+ fail:
+ if (sd_accept != INVALID_SOCKET)
+ close (sd_accept);
+ return 0;
+}
+
/************
name2address
************/
View
1  ext/em.h
@@ -94,6 +94,7 @@ class EventMachine_t
const unsigned long AttachFD (int, bool);
int DetachFD (EventableDescriptor*);
+ const unsigned long AttachServerFD (int);
void ArmKqueueWriter (EventableDescriptor*);
void ArmKqueueReader (EventableDescriptor*);
View
1  ext/eventmachine.h
@@ -49,6 +49,7 @@ extern "C" {
const unsigned long evma_attach_fd (int file_descriptor, int watch_mode);
int evma_detach_fd (const unsigned long binding);
+ const unsigned long evma_attach_server_fd (int file_descriptor);
int evma_get_file_descriptor (const unsigned long binding);
int evma_is_notify_readable (const unsigned long binding);
void evma_set_notify_readable (const unsigned long binding, int mode);
View
13 ext/rubymain.cpp
@@ -566,6 +566,18 @@ static VALUE t_detach_fd (VALUE self, VALUE signature)
return INT2NUM(evma_detach_fd (NUM2ULONG (signature)));
}
+/***********
+ t_attach_server_fd
+***********/
+
+static VALUE t_attach_server_fd (VALUE self, VALUE file_descriptor, VALUE watch_mode)
+{
+ const unsigned long f = evma_attach_server_fd (NUM2INT(file_descriptor));
+ if (!f)
+ rb_raise (rb_eRuntimeError, "no connection");
+ return ULONG2NUM (f);
+}
+
/**************
t_get_sock_opt
**************/
@@ -1179,6 +1191,7 @@ extern "C" void Init_rubyeventmachine()
rb_define_module_function (EmModule, "attach_fd", (VALUE (*)(...))t_attach_fd, 2);
rb_define_module_function (EmModule, "detach_fd", (VALUE (*)(...))t_detach_fd, 1);
+ rb_define_module_function (EmModule, "attach_server_fd", (VALUE (*)(...))t_attach_server_fd, 1);
rb_define_module_function (EmModule, "get_sock_opt", (VALUE (*)(...))t_get_sock_opt, 3);
rb_define_module_function (EmModule, "set_sock_opt", (VALUE (*)(...))t_set_sock_opt, 4);
rb_define_module_function (EmModule, "set_notify_readable", (VALUE (*)(...))t_set_notify_readable, 2);
View
22 lib/eventmachine.rb
@@ -751,6 +751,28 @@ def EventMachine::attach_io io, watch_mode, handler=nil, *args
c
end
+ # Attaches a server IO object or file descriptor to the eventloop.
+ # This function behaves just like start_server but allows you to reuse
+ # an already existing file descriptor instead of having EventMachine create
+ # one. The descriptor must be accept()able and will be set as non-blocking.
+ #
+ # Unlike start_server however, the file descriptor is not closed when
+ # EventMachine is released, so you will have to do any cleanups manually.
+ # If +io+ is an IO object then a reference to it will be kept until
+ # EventMachine is released, so that the file descriptor isn't accidentally
+ # closed by the garbage collector.
+ def EventMachine::attach_server io, handler = nil, *args, &block
+ klass = klass_from_handler(Connection, handler, *args)
+ if io.respond_to?(:fileno)
+ fd = defined?(JRuby) ? JRuby.runtime.getDescriptorByFileno(io.fileno).getChannel : io.fileno
+ else
+ fd = io
+ end
+ s = attach_server_fd(fd)
@sodabrew Collaborator
sodabrew added a note

Although this was superseded by #465, I am looking into JRuby test failures in #551 and I don't see where attach_server_fd is defined for the JRuby implementation. So even though you're making sure to use JRuby.runtime.getDescriptorByFileno, it's not getting passed into a valid function, AFAICT.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ @acceptors[s] = [klass,args,block,io]
+ s
+ end
+
# Connect to a given host/port and re-use the provided {EventMachine::Connection} instance.
# Consider also {EventMachine::Connection#reconnect}.
View
22 tests/test_attach.rb
@@ -4,6 +4,7 @@
class TestAttach < Test::Unit::TestCase
class EchoServer < EM::Connection
def receive_data data
+ $received_data << data
send_data data
end
end
@@ -31,12 +32,14 @@ def unbind
def setup
@port = next_port
$read, $r, $w, $fd = nil
+ $received_data = ""
end
def teardown
[$r, $w].each do |io|
io.close rescue nil
end
+ $received_data = nil
end
def test_attach
@@ -56,6 +59,25 @@ def test_attach
assert_equal socket.readline, "def\n"
end
+ def test_attach_server
+ $before = TCPServer.new("127.0.0.1", @port)
+ EM.run {
+ EM.attach_server $before, EchoServer
+
+ handler = Class.new(EM::Connection) do
+ def initialize
+ send_data "hello world"
+ close_connection_after_writing
+ EM.add_timer(0.1) { EM.stop }
+ end
+ end
+ EM.connect("127.0.0.1", @port, handler)
+ }
+
+ assert_equal false, $before.closed?
+ assert_equal "hello world", $received_data
+ end
+
module PipeWatch
def notify_readable
$read = $r.readline
Something went wrong with that request. Please try again.