nio4r 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,123 @@
1
+ // a single header file is required
2
+ #include <ev.h>
3
+ #include <stdio.h>
4
+ #include <io.h>
5
+
6
+ // every watcher type has its own typedef'd struct
7
+ // with the name ev_TYPE
8
+ ev_io stdin_watcher;
9
+ ev_timer timeout_watcher;
10
+
11
+ // all watcher callbacks have a similar signature
12
+ // this callback is called when data is readable on stdin
13
+ static void
14
+ stdin_cb (EV_P_ ev_io *w, int revents)
15
+ {
16
+ puts ("stdin ready or done or something");
17
+ // for one-shot events, one must manually stop the watcher
18
+ // with its corresponding stop function.
19
+ //ev_io_stop (EV_A_ w);
20
+
21
+ // this causes all nested ev_loop's to stop iterating
22
+ //ev_unloop (EV_A_ EVUNLOOP_ALL);
23
+ }
24
+
25
+ // another callback, this time for a time-out
26
+ static void
27
+ timeout_cb (EV_P_ ev_timer *w, int revents)
28
+ {
29
+ puts ("timeout");
30
+ // this causes the innermost ev_loop to stop iterating
31
+ ev_unloop (EV_A_ EVUNLOOP_ONE);
32
+ }
33
+
34
+
35
+
36
+ #include <winsock.h>
37
+
38
+ #include <stdlib.h>
39
+ #include <iostream>
40
+ int get_server_fd()
41
+ {
42
+
43
+ //----------------------
44
+ // Initialize Winsock.
45
+ WSADATA wsaData;
46
+ int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
47
+ if (iResult != NO_ERROR) {
48
+ printf("Error at WSAStartup()\n");
49
+ return 1;
50
+ }
51
+
52
+ //----------------------
53
+ // Create a SOCKET for listening for
54
+ // incoming connection requests.
55
+ SOCKET ListenSocket;
56
+ ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
57
+ if (ListenSocket == INVALID_SOCKET) {
58
+ printf("Error at socket(): %ld\n", WSAGetLastError());
59
+ WSACleanup();
60
+ return 1;
61
+ }
62
+ printf("socket returned %d\n", ListenSocket);
63
+
64
+ //----------------------
65
+ // The sockaddr_in structure specifies the address family,
66
+ // IP address, and port for the socket that is being bound.
67
+ sockaddr_in service;
68
+ service.sin_family = AF_INET;
69
+ service.sin_addr.s_addr = inet_addr("127.0.0.1");
70
+ service.sin_port = htons(4444);
71
+
72
+ if (bind( ListenSocket,
73
+ (SOCKADDR*) &service,
74
+ sizeof(service)) == SOCKET_ERROR) {
75
+ printf("bind() failed.\n");
76
+ closesocket(ListenSocket);
77
+ WSACleanup();
78
+ return 1;
79
+ }
80
+
81
+ //----------------------
82
+ // Listen for incoming connection requests.
83
+ // on the created socket
84
+ if (listen( ListenSocket, 1 ) == SOCKET_ERROR) {
85
+ printf("Error listening on socket.\n");
86
+ closesocket(ListenSocket);
87
+ WSACleanup();
88
+ return 1;
89
+ }
90
+
91
+
92
+ printf("sock and osf handle are %d %d, error is \n", ListenSocket, _get_osfhandle (ListenSocket)); // -1 is invalid file handle: http://msdn.microsoft.com/en-us/library/ks2530z6.aspx
93
+ printf("err was %d\n", WSAGetLastError());
94
+ //----------------------
95
+ return ListenSocket;
96
+ }
97
+
98
+
99
+ int
100
+ main (void)
101
+ {
102
+ struct ev_loop *loopy = ev_default_loop(0);
103
+ int fd = get_server_fd();
104
+ int fd_real = _open_osfhandle(fd, NULL);
105
+ int conv = _get_osfhandle(fd_real);
106
+ printf("got server fd %d, loop %d, fd_real %d, that converted %d\n", fd, loopy, fd_real, conv);
107
+ // accept(fd, NULL, NULL);
108
+ // initialise an io watcher, then start it
109
+ // this one will watch for stdin to become readable
110
+ ev_io_init (&stdin_watcher, stdin_cb, /*STDIN_FILENO*/ conv, EV_READ);
111
+ ev_io_start (loopy, &stdin_watcher);
112
+
113
+ // initialise a timer watcher, then start it
114
+ // simple non-repeating 5.5 second timeout
115
+ //ev_timer_init (&timeout_watcher, timeout_cb, 15.5, 0.);
116
+ //ev_timer_start (loopy, &timeout_watcher);
117
+ printf("starting loop\n");
118
+ // now wait for events to arrive
119
+ ev_loop (loopy, 0);
120
+
121
+ // unloop was called, so exit
122
+ return 0;
123
+ }
@@ -0,0 +1,44 @@
1
+ require 'mkmf'
2
+
3
+ if have_func('rb_thread_blocking_region')
4
+ $defs << '-DHAVE_RB_THREAD_BLOCKING_REGION'
5
+ end
6
+
7
+ if have_header('sys/select.h')
8
+ $defs << '-DEV_USE_SELECT'
9
+ end
10
+
11
+ if have_header('poll.h')
12
+ $defs << '-DEV_USE_POLL'
13
+ end
14
+
15
+ if have_header('sys/epoll.h')
16
+ $defs << '-DEV_USE_EPOLL'
17
+ end
18
+
19
+ if have_header('sys/event.h') and have_header('sys/queue.h')
20
+ $defs << '-DEV_USE_KQUEUE'
21
+ end
22
+
23
+ if have_header('port.h')
24
+ $defs << '-DEV_USE_PORT'
25
+ end
26
+
27
+ if have_header('sys/resource.h')
28
+ $defs << '-DHAVE_SYS_RESOURCE_H'
29
+ end
30
+
31
+ dir_config 'nio4r_ext'
32
+ create_makefile 'nio4r_ext'
33
+
34
+ # win32 needs to link in "just the right order" for some reason or ioctlsocket will be mapped to an [inverted] ruby specific version.
35
+ # See libev mailing list for (not so helpful discussion--true cause I'm not sure, but this overcomes the symptom)
36
+ if RUBY_PLATFORM =~ /mingw|win32/
37
+ makefile_contents = File.read 'Makefile'
38
+
39
+ # "Init_cool could not be found" when loading cool.io.so.
40
+ makefile_contents.gsub! 'DLDFLAGS = ', 'DLDFLAGS = -export-all '
41
+
42
+ makefile_contents.gsub! 'LIBS = $(LIBRUBYARG_SHARED)', 'LIBS = -lws2_32 $(LIBRUBYARG_SHARED)'
43
+ File.open('Makefile', 'w') { |f| f.write makefile_contents }
44
+ end
data/ext/nio4r/libev.h ADDED
@@ -0,0 +1,8 @@
1
+ #define EV_STANDALONE /* keeps ev from requiring config.h */
2
+
3
+ #ifdef _WIN32
4
+ #define EV_SELECT_IS_WINSOCKET 1
5
+ #define FD_SETSIZE 512
6
+ #endif
7
+
8
+ #include "../libev/ev.h"
@@ -0,0 +1,164 @@
1
+ /*
2
+ * Copyright (c) 2011 Tony Arcieri. Distributed under the MIT License. See
3
+ * LICENSE.txt for further details.
4
+ */
5
+
6
+ #include "nio4r.h"
7
+ #include <assert.h>
8
+
9
+ static VALUE mNIO = Qnil;
10
+ static VALUE cNIO_Monitor = Qnil;
11
+
12
+ /* Allocator/deallocator */
13
+ static VALUE NIO_Monitor_allocate(VALUE klass);
14
+ static void NIO_Monitor_mark(struct NIO_Monitor *monitor);
15
+ static void NIO_Monitor_free(struct NIO_Monitor *monitor);
16
+
17
+ /* Methods */
18
+ static VALUE NIO_Monitor_initialize(VALUE self, VALUE selector, VALUE io, VALUE interests);
19
+ static VALUE NIO_Monitor_close(VALUE self);
20
+ static VALUE NIO_Monitor_is_closed(VALUE self);
21
+ static VALUE NIO_Monitor_io(VALUE self);
22
+ static VALUE NIO_Monitor_interests(VALUE self);
23
+ static VALUE NIO_Monitor_value(VALUE self);
24
+ static VALUE NIO_Monitor_set_value(VALUE self, VALUE obj);
25
+
26
+ /* Internal functions */
27
+ static void NIO_Monitor_callback(struct ev_loop *ev_loop, struct ev_io *io, int revents);
28
+
29
+ #if HAVE_RB_IO_T
30
+ rb_io_t *fptr;
31
+ #else
32
+ OpenFile *fptr;
33
+ #endif
34
+
35
+ /* Monitor control how a channel is being waited for by a monitor */
36
+ void Init_NIO_Monitor()
37
+ {
38
+ mNIO = rb_define_module("NIO");
39
+ cNIO_Monitor = rb_define_class_under(mNIO, "Monitor", rb_cObject);
40
+ rb_define_alloc_func(cNIO_Monitor, NIO_Monitor_allocate);
41
+
42
+ rb_define_method(cNIO_Monitor, "initialize", NIO_Monitor_initialize, 3);
43
+ rb_define_method(cNIO_Monitor, "close", NIO_Monitor_close, 0);
44
+ rb_define_method(cNIO_Monitor, "closed?", NIO_Monitor_is_closed, 0);
45
+ rb_define_method(cNIO_Monitor, "io", NIO_Monitor_io, 0);
46
+ rb_define_method(cNIO_Monitor, "interests", NIO_Monitor_interests, 0);
47
+ rb_define_method(cNIO_Monitor, "value", NIO_Monitor_value, 0);
48
+ rb_define_method(cNIO_Monitor, "value=", NIO_Monitor_set_value, 1);
49
+ }
50
+
51
+ static VALUE NIO_Monitor_allocate(VALUE klass)
52
+ {
53
+ struct NIO_Monitor *monitor = (struct NIO_Monitor *)xmalloc(sizeof(struct NIO_Monitor));
54
+
55
+ return Data_Wrap_Struct(klass, NIO_Monitor_mark, NIO_Monitor_free, monitor);
56
+ }
57
+
58
+ static void NIO_Monitor_mark(struct NIO_Monitor *monitor)
59
+ {
60
+ }
61
+
62
+ static void NIO_Monitor_free(struct NIO_Monitor *monitor)
63
+ {
64
+ xfree(monitor);
65
+ }
66
+
67
+ static VALUE NIO_Monitor_initialize(VALUE self, VALUE selector_obj, VALUE io, VALUE interests)
68
+ {
69
+ struct NIO_Monitor *monitor;
70
+ struct NIO_Selector *selector;
71
+ int events;
72
+ ID interests_id;
73
+
74
+ #if HAVE_RB_IO_T
75
+ rb_io_t *fptr;
76
+ #else
77
+ OpenFile *fptr;
78
+ #endif
79
+
80
+ interests_id = SYM2ID(interests);
81
+
82
+ if(interests_id == rb_intern("r")) {
83
+ events = EV_READ;
84
+ } else if(interests_id == rb_intern("w")) {
85
+ events = EV_WRITE;
86
+ } else if(interests_id == rb_intern("rw")) {
87
+ events = EV_READ | EV_WRITE;
88
+ } else {
89
+ rb_raise(rb_eArgError, "invalid event type %s (must be :r, :w, or :rw)",
90
+ RSTRING_PTR(rb_funcall(interests, rb_intern("inspect"), 0, 0)));
91
+ }
92
+
93
+ Data_Get_Struct(self, struct NIO_Monitor, monitor);
94
+
95
+ GetOpenFile(rb_convert_type(io, T_FILE, "IO", "to_io"), fptr);
96
+ ev_io_init(&monitor->ev_io, NIO_Monitor_callback, FPTR_TO_FD(fptr), events);
97
+
98
+ rb_ivar_set(self, rb_intern("selector"), selector_obj);
99
+ rb_ivar_set(self, rb_intern("io"), io);
100
+ rb_ivar_set(self, rb_intern("interests"), interests);
101
+
102
+ Data_Get_Struct(selector_obj, struct NIO_Selector, selector);
103
+
104
+ monitor->self = self;
105
+ monitor->ev_io.data = (void *)monitor;
106
+
107
+ /* We can safely hang onto this as we also hang onto a reference to the
108
+ object where it originally came from */
109
+ monitor->selector = selector;
110
+
111
+ ev_io_start(selector->ev_loop, &monitor->ev_io);
112
+
113
+ return Qnil;
114
+ }
115
+
116
+ static VALUE NIO_Monitor_close(VALUE self)
117
+ {
118
+ struct NIO_Monitor *monitor;
119
+ Data_Get_Struct(self, struct NIO_Monitor, monitor);
120
+
121
+ if(monitor->selector) {
122
+ ev_io_stop(monitor->selector->ev_loop, &monitor->ev_io);
123
+ monitor->selector = 0;
124
+ }
125
+
126
+ return Qnil;
127
+ }
128
+
129
+ static VALUE NIO_Monitor_is_closed(VALUE self)
130
+ {
131
+ struct NIO_Monitor *monitor;
132
+ Data_Get_Struct(self, struct NIO_Monitor, monitor);
133
+
134
+ return !monitor->selector;
135
+ }
136
+
137
+ static VALUE NIO_Monitor_io(VALUE self)
138
+ {
139
+ return rb_ivar_get(self, rb_intern("io"));
140
+ }
141
+
142
+ static VALUE NIO_Monitor_interests(VALUE self)
143
+ {
144
+ return rb_ivar_get(self, rb_intern("interests"));
145
+ }
146
+
147
+ static VALUE NIO_Monitor_value(VALUE self)
148
+ {
149
+ return rb_ivar_get(self, rb_intern("value"));
150
+ }
151
+
152
+ static VALUE NIO_Monitor_set_value(VALUE self, VALUE obj)
153
+ {
154
+ return rb_ivar_set(self, rb_intern("value"), obj);
155
+ }
156
+
157
+ /* libev callback fired whenever this monitor gets events */
158
+ static void NIO_Monitor_callback(struct ev_loop *ev_loop, struct ev_io *io, int revents)
159
+ {
160
+ struct NIO_Monitor *monitor = (struct NIO_Monitor *)io->data;
161
+
162
+ assert(monitor->selector != 0);
163
+ NIO_Selector_handle_event(monitor->selector, monitor->self, revents);
164
+ }
data/ext/nio4r/nio4r.h ADDED
@@ -0,0 +1,53 @@
1
+ /*
2
+ * Copyright (c) 2011 Tony Arcieri. Distributed under the MIT License. See
3
+ * LICENSE.txt for further details.
4
+ */
5
+
6
+ #ifndef NIO4R_H
7
+ #define NIO4R_H
8
+
9
+ #include "ruby.h"
10
+ #include "rubyio.h"
11
+ #include "libev.h"
12
+
13
+ struct NIO_Selector
14
+ {
15
+ struct ev_loop *ev_loop;
16
+ struct ev_timer timer; /* for timeouts */
17
+ struct ev_async wakeup;
18
+
19
+ int closed, selecting;
20
+ int ready_count;
21
+ int ready_buffer_size;
22
+ VALUE *ready_buffer;
23
+ };
24
+
25
+ struct NIO_callback_data
26
+ {
27
+ VALUE *monitor;
28
+ struct NIO_Selector *selector;
29
+ };
30
+
31
+ struct NIO_Monitor
32
+ {
33
+ VALUE self;
34
+ struct ev_io ev_io;
35
+ struct NIO_Selector *selector;
36
+ };
37
+
38
+ #ifdef GetReadFile
39
+ # define FPTR_TO_FD(fptr) (fileno(GetReadFile(fptr)))
40
+ #else
41
+
42
+ #if !HAVE_RB_IO_T || (RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8)
43
+ # define FPTR_TO_FD(fptr) fileno(fptr->f)
44
+ #else
45
+ # define FPTR_TO_FD(fptr) fptr->fd
46
+ #endif /* !HAVE_RB_IO_T */
47
+
48
+ #endif /* GetReadFile */
49
+
50
+ /* Thunk between libev callbacks in NIO::Monitors and NIO::Selectors */
51
+ void NIO_Selector_handle_event(struct NIO_Selector *selector, VALUE monitor, int revents);
52
+
53
+ #endif /* NIO4R_H */
@@ -0,0 +1,370 @@
1
+ /*
2
+ * Copyright (c) 2011 Tony Arcieri. Distributed under the MIT License. See
3
+ * LICENSE.txt for further details.
4
+ */
5
+
6
+ #include "nio4r.h"
7
+ #include "rubysig.h"
8
+
9
+ static VALUE mNIO = Qnil;
10
+ static VALUE cNIO_Channel = Qnil;
11
+ static VALUE cNIO_Monitor = Qnil;
12
+ static VALUE cNIO_Selector = Qnil;
13
+
14
+ /* Allocator/deallocator */
15
+ static VALUE NIO_Selector_allocate(VALUE klass);
16
+ static void NIO_Selector_mark(struct NIO_Selector *loop);
17
+ static void NIO_Selector_shutdown(struct NIO_Selector *selector);
18
+ static void NIO_Selector_free(struct NIO_Selector *loop);
19
+
20
+ /* Methods */
21
+ static VALUE NIO_Selector_initialize(VALUE self);
22
+ static VALUE NIO_Selector_register(VALUE self, VALUE selectable, VALUE interest);
23
+ static VALUE NIO_Selector_deregister(VALUE self, VALUE io);
24
+ static VALUE NIO_Selector_is_registered(VALUE self, VALUE io);
25
+ static VALUE NIO_Selector_select(int argc, VALUE *argv, VALUE self);
26
+ static VALUE NIO_Selector_wakeup(VALUE self);
27
+ static VALUE NIO_Selector_close(VALUE self);
28
+ static VALUE NIO_Selector_closed(VALUE self);
29
+
30
+ /* Internal functions */
31
+ static VALUE NIO_Selector_synchronize(VALUE self, VALUE (*func)(VALUE *args), VALUE *args);
32
+ static VALUE NIO_Selector_unlock(VALUE lock);
33
+ static VALUE NIO_Selector_register_synchronized(VALUE *args);
34
+ static VALUE NIO_Selector_deregister_synchronized(VALUE *args);
35
+ static VALUE NIO_Selector_select_synchronized(VALUE *args);
36
+ static VALUE NIO_Selector_run_evloop(void *ptr);
37
+ static void NIO_Selector_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents);
38
+ static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_async *async, int revents);
39
+
40
+ /* Default number of slots in the buffer for selected monitors */
41
+ #define INITIAL_READY_BUFFER 32
42
+
43
+ /* Ruby 1.8 needs us to busy wait and run the green threads scheduler every 10ms */
44
+ #define BUSYWAIT_INTERVAL 0.01
45
+
46
+ /* Selectors wait for events */
47
+ void Init_NIO_Selector()
48
+ {
49
+ mNIO = rb_define_module("NIO");
50
+ cNIO_Channel = rb_define_class_under(mNIO, "Channel", rb_cObject);
51
+ cNIO_Monitor = rb_define_class_under(mNIO, "Monitor", rb_cObject);
52
+ cNIO_Selector = rb_define_class_under(mNIO, "Selector", rb_cObject);
53
+ rb_define_alloc_func(cNIO_Selector, NIO_Selector_allocate);
54
+
55
+ rb_define_method(cNIO_Selector, "initialize", NIO_Selector_initialize, 0);
56
+ rb_define_method(cNIO_Selector, "register", NIO_Selector_register, 2);
57
+ rb_define_method(cNIO_Selector, "deregister", NIO_Selector_deregister, 1);
58
+ rb_define_method(cNIO_Selector, "registered?", NIO_Selector_is_registered, 1);
59
+ rb_define_method(cNIO_Selector, "select", NIO_Selector_select, -1);
60
+ rb_define_method(cNIO_Selector, "wakeup", NIO_Selector_wakeup, 0);
61
+ rb_define_method(cNIO_Selector, "close", NIO_Selector_close, 0);
62
+ rb_define_method(cNIO_Selector, "closed?", NIO_Selector_closed, 0);
63
+ }
64
+
65
+ /* Create the libev event loop and incoming event buffer */
66
+ static VALUE NIO_Selector_allocate(VALUE klass)
67
+ {
68
+ struct NIO_Selector *selector = (struct NIO_Selector *)xmalloc(sizeof(struct NIO_Selector));
69
+
70
+ selector->ev_loop = ev_loop_new(0);
71
+ ev_init(&selector->timer, NIO_Selector_timeout_callback);
72
+
73
+ ev_async_init(&selector->wakeup, NIO_Selector_wakeup_callback);
74
+ selector->wakeup.data = (void *)selector;
75
+
76
+ ev_async_start(selector->ev_loop, &selector->wakeup);
77
+
78
+ selector->closed = selector->selecting = selector->ready_count = 0;
79
+ selector->ready_buffer_size = INITIAL_READY_BUFFER;
80
+ selector->ready_buffer = (VALUE *)xmalloc(sizeof(VALUE) * INITIAL_READY_BUFFER);
81
+
82
+ return Data_Wrap_Struct(klass, NIO_Selector_mark, NIO_Selector_free, selector);
83
+ }
84
+
85
+ /* NIO selectors store all Ruby objects in instance variables so mark is a stub */
86
+ static void NIO_Selector_mark(struct NIO_Selector *selector)
87
+ {
88
+ }
89
+
90
+ /* Free a Selector's system resources.
91
+ Called by both NIO::Selector#close and the finalizer below */
92
+ static void NIO_Selector_shutdown(struct NIO_Selector *selector)
93
+ {
94
+ if(selector->ev_loop) {
95
+ ev_loop_destroy(selector->ev_loop);
96
+ selector->ev_loop = 0;
97
+ }
98
+
99
+ if(selector->closed) {
100
+ return;
101
+ }
102
+
103
+ selector->closed = 1;
104
+ }
105
+
106
+ /* Ruby finalizer for selector objects */
107
+ static void NIO_Selector_free(struct NIO_Selector *selector)
108
+ {
109
+ NIO_Selector_shutdown(selector);
110
+
111
+ xfree(selector->ready_buffer);
112
+ xfree(selector);
113
+ }
114
+
115
+ /* Create a new selector. This is more or less the pure Ruby version
116
+ translated into an MRI cext */
117
+ static VALUE NIO_Selector_initialize(VALUE self)
118
+ {
119
+ VALUE lock;
120
+
121
+ rb_ivar_set(self, rb_intern("selectables"), rb_hash_new());
122
+
123
+ lock = rb_class_new_instance(0, 0, rb_const_get(rb_cObject, rb_intern("Mutex")));
124
+ rb_ivar_set(self, rb_intern("lock"), lock);
125
+
126
+ return Qnil;
127
+ }
128
+
129
+ /* Synchronize the given function with the selector mutex */
130
+ static VALUE NIO_Selector_synchronize(VALUE self, VALUE (*func)(VALUE *args), VALUE *args)
131
+ {
132
+ VALUE lock;
133
+
134
+ lock = rb_ivar_get(self, rb_intern("lock"));
135
+ rb_funcall(lock, rb_intern("lock"), 0, 0);
136
+ return rb_ensure(func, (VALUE)args, NIO_Selector_unlock, lock);
137
+ }
138
+
139
+ /* Unlock the selector mutex */
140
+ static VALUE NIO_Selector_unlock(VALUE lock)
141
+ {
142
+ rb_funcall(lock, rb_intern("unlock"), 0, 0);
143
+ }
144
+
145
+ /* Register an IO object with the selector for the given interests */
146
+ static VALUE NIO_Selector_register(VALUE self, VALUE io, VALUE interests)
147
+ {
148
+ VALUE args[3] = {self, io, interests};
149
+ return NIO_Selector_synchronize(self, NIO_Selector_register_synchronized, args);
150
+ }
151
+
152
+ /* Internal implementation of register after acquiring mutex */
153
+ static VALUE NIO_Selector_register_synchronized(VALUE *args)
154
+ {
155
+ VALUE self, io, interests, selectables, monitor;
156
+ VALUE monitor_args[3];
157
+
158
+ self = args[0];
159
+ io = args[1];
160
+ interests = args[2];
161
+
162
+ selectables = rb_ivar_get(self, rb_intern("selectables"));
163
+ monitor = rb_hash_lookup(selectables, io);
164
+
165
+ if(monitor != Qnil)
166
+ rb_raise(rb_eArgError, "this IO is already registered with selector");
167
+
168
+ /* Create a new NIO::Monitor */
169
+ monitor_args[0] = self;
170
+ monitor_args[1] = io;
171
+ monitor_args[2] = interests;
172
+
173
+ monitor = rb_class_new_instance(3, monitor_args, cNIO_Monitor);
174
+ rb_hash_aset(selectables, io, monitor);
175
+
176
+ return monitor;
177
+ }
178
+
179
+ /* Deregister an IO object from the selector */
180
+ static VALUE NIO_Selector_deregister(VALUE self, VALUE io)
181
+ {
182
+ VALUE args[2] = {self, io};
183
+ return NIO_Selector_synchronize(self, NIO_Selector_deregister_synchronized, args);
184
+ }
185
+
186
+ /* Internal implementation of register after acquiring mutex */
187
+ static VALUE NIO_Selector_deregister_synchronized(VALUE *args)
188
+ {
189
+ VALUE self, io, interests, selectables, monitor;
190
+ VALUE monitor_args[3];
191
+
192
+ self = args[0];
193
+ io = args[1];
194
+
195
+ selectables = rb_ivar_get(self, rb_intern("selectables"));
196
+ monitor = rb_hash_delete(selectables, io);
197
+
198
+ if(monitor != Qnil) {
199
+ rb_funcall(monitor, rb_intern("close"), 0, 0);
200
+ }
201
+
202
+ return monitor;
203
+ }
204
+
205
+ /* Is the given IO object registered with the selector */
206
+ static VALUE NIO_Selector_is_registered(VALUE self, VALUE io)
207
+ {
208
+ VALUE selectables = rb_ivar_get(self, rb_intern("selectables"));
209
+
210
+ /* Perhaps this should be holding the mutex? */
211
+ return rb_funcall(selectables, rb_intern("has_key?"), 1, io);
212
+ }
213
+
214
+ /* Select from all registered IO objects */
215
+ static VALUE NIO_Selector_select(int argc, VALUE *argv, VALUE self)
216
+ {
217
+ VALUE timeout, array;
218
+ VALUE args[2];
219
+
220
+ rb_scan_args(argc, argv, "01", &timeout);
221
+
222
+ args[0] = self;
223
+ args[1] = timeout;
224
+
225
+ return NIO_Selector_synchronize(self, NIO_Selector_select_synchronized, args);
226
+ }
227
+
228
+ /* Internal implementation of select with the selector lock held */
229
+ static VALUE NIO_Selector_select_synchronized(VALUE *args)
230
+ {
231
+ VALUE self, timeout, result;
232
+ struct NIO_Selector *selector;
233
+
234
+ self = args[0];
235
+ timeout = args[1];
236
+
237
+ Data_Get_Struct(self, struct NIO_Selector, selector);
238
+ selector->selecting = 1;
239
+
240
+ #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_ALONE)
241
+ /* Implement the optional timeout (if any) as a ev_timer */
242
+ if(timeout != Qnil) {
243
+ selector->timer.repeat = NUM2DBL(timeout);
244
+ ev_timer_again(selector->ev_loop, &selector->timer);
245
+ } else {
246
+ ev_timer_stop(selector->ev_loop, &selector->timer);
247
+ }
248
+ #else
249
+ /* Store when we started the loop so we can calculate the timeout */
250
+ ev_tstamp started_at = ev_now(selector->ev_loop);
251
+ #endif
252
+
253
+ #if defined(HAVE_RB_THREAD_BLOCKING_REGION)
254
+ /* Ruby 1.9 lets us release the GIL and make a blocking I/O call */
255
+ rb_thread_blocking_region(NIO_Selector_run_evloop, selector, RUBY_UBF_IO, 0);
256
+ #elif defined(HAVE_RB_THREAD_ALONE)
257
+ /* If we're the only thread we can make a blocking system call */
258
+ if(rb_thread_alone()) {
259
+ #else
260
+ /* If we don't have rb_thread_alone() we can't block */
261
+ if(0) {
262
+ #endif /* defined(HAVE_RB_THREAD_BLOCKING_REGION) */
263
+
264
+ #if !defined(HAVE_RB_THREAD_BLOCKING_REGION)
265
+ TRAP_BEG;
266
+ NIO_Selector_run_evloop(selector);
267
+ TRAP_END;
268
+ } else {
269
+ /* We need to busy wait as not to stall the green thread scheduler
270
+ Ruby 1.8: just say no! :( */
271
+ ev_timer_init(&selector->timer, NIO_Selector_timeout_callback, BUSYWAIT_INTERVAL, BUSYWAIT_INTERVAL);
272
+ ev_timer_start(selector->ev_loop, &selector->timer);
273
+
274
+ /* Loop until we receive events */
275
+ while(selector->selecting && !selector->ready_count) {
276
+ TRAP_BEG;
277
+ NIO_Selector_run_evloop(selector);
278
+ TRAP_END;
279
+
280
+ /* Run the next green thread */
281
+ rb_thread_schedule();
282
+
283
+ /* Break if the timeout has elapsed */
284
+ if(timeout != Qnil && ev_now(selector->ev_loop) - started_at >= NUM2DBL(timeout))
285
+ break;
286
+ }
287
+
288
+ ev_timer_stop(selector->ev_loop, &selector->timer);
289
+ }
290
+ #endif /* defined(HAVE_RB_THREAD_BLOCKING_REGION) */
291
+
292
+ if(!selector->ready_count) {
293
+ return Qnil;
294
+ }
295
+
296
+ result = rb_ary_new4(selector->ready_count, selector->ready_buffer);
297
+ selector->selecting = selector->ready_count = 0;
298
+
299
+ return result;
300
+ }
301
+
302
+ /* Run the libev event loop */
303
+ static VALUE NIO_Selector_run_evloop(void *ptr)
304
+ {
305
+ struct NIO_Selector *selector = (struct NIO_Selector *)ptr;
306
+
307
+ ev_loop(selector->ev_loop, EVLOOP_ONESHOT);
308
+
309
+ return Qnil;
310
+ }
311
+
312
+ /* Wake the selector up from another thread */
313
+ static VALUE NIO_Selector_wakeup(VALUE self)
314
+ {
315
+ struct NIO_Selector *selector;
316
+ Data_Get_Struct(self, struct NIO_Selector, selector);
317
+
318
+ ev_async_send(selector->ev_loop, &selector->wakeup);
319
+
320
+ return Qnil;
321
+ }
322
+
323
+ /* Close the selector and free system resources */
324
+ static VALUE NIO_Selector_close(VALUE self)
325
+ {
326
+ struct NIO_Selector *selector;
327
+ Data_Get_Struct(self, struct NIO_Selector, selector);
328
+
329
+ NIO_Selector_shutdown(selector);
330
+
331
+ return Qnil;
332
+ }
333
+
334
+ /* Is the selector closed? */
335
+ static VALUE NIO_Selector_closed(VALUE self)
336
+ {
337
+ struct NIO_Selector *selector;
338
+ Data_Get_Struct(self, struct NIO_Selector, selector);
339
+
340
+ return selector->closed ? Qtrue : Qfalse;
341
+ }
342
+
343
+ /* Called whenever a timeout fires on the event loop */
344
+ static void NIO_Selector_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents)
345
+ {
346
+ /* We don't actually need to do anything here, the mere firing of the
347
+ timer is sufficient to interrupt the selector. However, libev still wants a callback */
348
+ }
349
+
350
+ /* Called whenever a wakeup request is sent to a selector */
351
+ static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_async *async, int revents)
352
+ {
353
+ struct NIO_Selector *selector = (struct NIO_Selector *)async->data;
354
+ selector->selecting = 0;
355
+ }
356
+
357
+ /* This gets called from individual monitors. We must be careful here because
358
+ the GIL isn't held, so we must rely only on standard C and can't touch
359
+ anything Ruby-related */
360
+ void NIO_Selector_handle_event(struct NIO_Selector *selector, VALUE monitor, int revents)
361
+ {
362
+ /* Grow the ready buffer if it's too small */
363
+ if(selector->ready_count >= selector->ready_buffer_size) {
364
+ selector->ready_buffer_size *= 2;
365
+ selector->ready_buffer = (VALUE *)xrealloc(selector->ready_buffer, sizeof(VALUE) * selector->ready_buffer_size);
366
+ }
367
+
368
+ selector->ready_buffer[selector->ready_count] = monitor;
369
+ selector->ready_count++;
370
+ }