nio4r 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }