Merge commit '3c60a40b796843e62ba2b56f4878a569c86a9d6e'
[god.git] / ext / god / kqueue_handler.c
blob6802c67361eb53120905b7a35c50d9dc3bd05560
1 #if defined(__FreeBSD__) || defined(__APPLE__)
3 #include <ruby.h>
4 #include <sys/event.h>
5 #include <sys/time.h>
6 #include <errno.h>
8 static VALUE mGod;
9 static VALUE cKQueueHandler;
10 static VALUE cEventHandler;
12 static ID proc_exit;
13 static ID proc_fork;
14 static ID m_call;
15 static ID m_size;
16 static ID m_deregister;
18 static int kq;
19 int num_events;
21 #define NUM_EVENTS FIX2INT(rb_funcall(rb_cv_get(cEventHandler, "@@actions"), m_size, 0))
23 VALUE
24 kqh_event_mask(VALUE klass, VALUE sym)
26 ID id = SYM2ID(sym);
27 if (proc_exit == id) {
28 return UINT2NUM(NOTE_EXIT);
29 } else if (proc_fork == id) {
30 return UINT2NUM(NOTE_FORK);
31 } else {
32 rb_raise(rb_eNotImpError, "Event `%s` not implemented", rb_id2name(id));
35 return Qnil;
39 VALUE
40 kqh_monitor_process(VALUE klass, VALUE pid, VALUE mask)
42 struct kevent new_event;
43 ID event;
45 u_int fflags = NUM2UINT(mask);
47 EV_SET(&new_event, FIX2UINT(pid), EVFILT_PROC,
48 EV_ADD | EV_ENABLE, fflags, 0, 0);
50 if (-1 == kevent(kq, &new_event, 1, NULL, 0, NULL)) {
51 rb_raise(rb_eStandardError, strerror(errno));
54 num_events = NUM_EVENTS;
56 return Qnil;
59 VALUE
60 kqh_handle_events()
62 int nevents, i, num_to_fetch;
63 struct kevent *events;
64 fd_set read_set;
66 FD_ZERO(&read_set);
67 FD_SET(kq, &read_set);
69 // Don't actually run this method until we've got an event
70 rb_thread_select(kq + 1, &read_set, NULL, NULL, NULL);
72 // Grabbing num_events once for thread safety
73 num_to_fetch = num_events;
74 events = (struct kevent*)malloc(num_to_fetch * sizeof(struct kevent));
76 if (NULL == events) {
77 rb_raise(rb_eStandardError, strerror(errno));
80 nevents = kevent(kq, NULL, 0, events, num_to_fetch, NULL);
82 if (-1 == nevents) {
83 rb_raise(rb_eStandardError, strerror(errno));
84 } else {
85 for (i = 0; i < nevents; i++) {
86 if (events[i].fflags & NOTE_EXIT) {
87 rb_funcall(cEventHandler, m_call, 2, INT2NUM(events[i].ident), ID2SYM(proc_exit));
88 } else if (events[i].fflags & NOTE_FORK) {
89 rb_funcall(cEventHandler, m_call, 2, INT2NUM(events[i].ident), ID2SYM(proc_fork));
94 free(events);
96 return INT2FIX(nevents);
99 void
100 Init_kqueue_handler_ext()
102 kq = kqueue();
104 if (kq == -1) {
105 rb_raise(rb_eStandardError, "kqueue initilization failed");
108 proc_exit = rb_intern("proc_exit");
109 proc_fork = rb_intern("proc_fork");
110 m_call = rb_intern("call");
111 m_size = rb_intern("size");
112 m_deregister = rb_intern("deregister");
114 mGod = rb_const_get(rb_cObject, rb_intern("God"));
115 cEventHandler = rb_const_get(mGod, rb_intern("EventHandler"));
116 cKQueueHandler = rb_define_class_under(mGod, "KQueueHandler", rb_cObject);
117 rb_define_singleton_method(cKQueueHandler, "monitor_process", kqh_monitor_process, 2);
118 rb_define_singleton_method(cKQueueHandler, "handle_events", kqh_handle_events, 0);
119 rb_define_singleton_method(cKQueueHandler, "event_mask", kqh_event_mask, 1);
122 #endif