| CODENOTIFIER | HelpYou are not signed inSign in |
Project: rev
Revision: 236
Author: rogerdpack
Date: 28 Jul 2008 14:02:14
Changes:keep the array code around for comparison
Files:| ... | ...@@ -0,0 +1,264 @@ | |
| 1 | /* | |
| 2 | * Copyright (C) 2007 Tony Arcieri | |
| 3 | * You may redistribute this under the terms of the Ruby license. | |
| 4 | * See LICENSE for details | |
| 5 | */ | |
| 6 | ||
| 7 | #include "ruby.h" | |
| 8 | ||
| 9 | #define EV_STANDALONE 1 | |
| 10 | #include "../libev/ev.h" | |
| 11 | ||
| 12 | #include "rev.h" | |
| 13 | ||
| 14 | static VALUE mRev = Qnil; | |
| 15 | static VALUE cRev_Watcher = Qnil; | |
| 16 | ||
| 17 | static VALUE Rev_Watcher_allocate(VALUE klass); | |
| 18 | static void Rev_Watcher_mark(struct Rev_Watcher *watcher); | |
| 19 | static void Rev_Watcher_free(struct Rev_Watcher *watcher); | |
| 20 | ||
| 21 | static VALUE Rev_Watcher_initialize(VALUE self); | |
| 22 | static VALUE Rev_Watcher_attach(VALUE self, VALUE loop); | |
| 23 | static VALUE Rev_Watcher_detach(VALUE self); | |
| 24 | static VALUE Rev_Watcher_enable(VALUE self); | |
| 25 | static VALUE Rev_Watcher_disable(VALUE self); | |
| 26 | static VALUE Rev_Watcher_evloop(VALUE self); | |
| 27 | static VALUE Rev_Watcher_attached(VALUE self); | |
| 28 | static VALUE Rev_Watcher_enabled(VALUE self); | |
| 29 | ||
| 30 | /* | |
| 31 | * Watchers are Rev's event observers. They contain a set of callback | |
| 32 | * methods prefixed by on_* which fire whenever events occur. | |
| 33 | * | |
| 34 | * In order for a watcher to fire events it must be attached to a running | |
| 35 | * loop. Every watcher has an attach and detach method to control which | |
| 36 | * loop it's associated with. | |
| 37 | * | |
| 38 | * Watchers also have an enable and disable method. This allows a watcher | |
| 39 | * to temporarily ignore certain events while remaining attached to a given | |
| 40 | * loop. This is good for watchers which need to be toggled on and off. | |
| 41 | */ | |
| 42 | void Init_rev_watcher() | |
| 43 | { | |
| 44 | mRev = rb_define_module("Rev"); | |
| 45 | cRev_Watcher = rb_define_class_under(mRev, "Watcher", rb_cObject); | |
| 46 | rb_define_alloc_func(cRev_Watcher, Rev_Watcher_allocate); | |
| 47 | ||
| 48 | rb_define_method(cRev_Watcher, "initialize", Rev_Watcher_initialize, 0); | |
| 49 | rb_define_method(cRev_Watcher, "attach", Rev_Watcher_attach, 1); | |
| 50 | rb_define_method(cRev_Watcher, "detach", Rev_Watcher_detach, 0); | |
| 51 | rb_define_method(cRev_Watcher, "enable", Rev_Watcher_enable, 0); | |
| 52 | rb_define_method(cRev_Watcher, "disable", Rev_Watcher_disable, 0); | |
| 53 | rb_define_method(cRev_Watcher, "evloop", Rev_Watcher_evloop, 0); | |
| 54 | rb_define_method(cRev_Watcher, "attached?", Rev_Watcher_attached, 0); | |
| 55 | rb_define_method(cRev_Watcher, "enabled?", Rev_Watcher_enabled, 0); | |
| 56 | } | |
| 57 | ||
| 58 | static VALUE Rev_Watcher_allocate(VALUE klass) | |
| 59 | { | |
| 60 | struct Rev_Watcher *watcher_data = (struct Rev_Watcher *)xmalloc(sizeof(struct Rev_Watcher)); | |
| 61 | ||
| 62 | watcher_data->loop = Qnil; | |
| 63 | watcher_data->enabled = 0; | |
| 64 | ||
| 65 | return Data_Wrap_Struct(klass, Rev_Watcher_mark, Rev_Watcher_free, watcher_data); | |
| 66 | } | |
| 67 | ||
| 68 | static void Rev_Watcher_mark(struct Rev_Watcher *watcher_data) | |
| 69 | { | |
| 70 | if(watcher_data->loop != Qnil) | |
| 71 | rb_gc_mark(watcher_data->loop); | |
| 72 | } | |
| 73 | ||
| 74 | static void Rev_Watcher_free(struct Rev_Watcher *watcher_data) | |
| 75 | { | |
| 76 | xfree(watcher_data); | |
| 77 | } | |
| 78 | ||
| 79 | static VALUE Rev_Watcher_initialize(VALUE self) | |
| 80 | { | |
| 81 | rb_raise(rb_eRuntimeError, "watcher base class should not be initialized directly"); | |
| 82 | } | |
| 83 | ||
| 84 | /** | |
| 85 | * call-seq: | |
| 86 | * Rev::Watcher.attach(loop) -> Rev::Watcher | |
| 87 | * | |
| 88 | * Attach the watcher to the given Rev::Loop. If the watcher is already attached | |
| 89 | * to a loop, detach it from the old one and attach it to the new one. | |
| 90 | */ | |
| 91 | static VALUE Rev_Watcher_attach(VALUE self, VALUE loop) | |
| 92 | { | |
| 93 | VALUE loop_watchers, active_watchers; | |
| 94 | struct Rev_Watcher *watcher_data; | |
| 95 | ||
| 96 | Data_Get_Struct(self, struct Rev_Watcher, watcher_data); | |
| 97 | watcher_data->enabled = 1; | |
| 98 | ||
| 99 | loop_watchers = rb_iv_get(loop, "@watchers"); | |
| 100 | ||
| 101 | if(loop_watchers == Qnil) { | |
| 102 | loop_watchers = rb_ary_new(); | |
| 103 | rb_iv_set(loop, "@watchers", loop_watchers); | |
| 104 | } | |
| 105 | ||
| 106 | /* Add us to the loop's array of active watchers. This is mainly done | |
| 107 | * to keep the VM from garbage collecting watchers that are associated | |
| 108 | * with a loop (and also lets you see within Ruby which watchers are | |
| 109 | * associated with a given loop), but isn't really necessary for any | |
| 110 | * other reason */ | |
| 111 | rb_ary_push(loop_watchers, self); | |
| 112 | ||
| 113 | active_watchers = rb_iv_get(loop, "@active_watchers"); | |
| 114 | if(active_watchers == Qnil) | |
| 115 | active_watchers = INT2NUM(1); | |
| 116 | else | |
| 117 | active_watchers = INT2NUM(NUM2INT(active_watchers) + 1); | |
| 118 | rb_iv_set(loop, "@active_watchers", active_watchers); | |
| 119 | ||
| 120 | return self; | |
| 121 | } | |
| 122 | ||
| 123 | /** | |
| 124 | * call-seq: | |
| 125 | * Rev::Watcher.detach -> Rev::Watcher | |
| 126 | * | |
| 127 | * Detach the watcher from its current Rev::Loop. | |
| 128 | */ | |
| 129 | static VALUE Rev_Watcher_detach(VALUE self) | |
| 130 | { | |
| 131 | struct Rev_Watcher *watcher_data; | |
| 132 | struct Rev_Loop *loop_data; | |
| 133 | VALUE loop_watchers; | |
| 134 | int i; | |
| 135 | ||
| 136 | Data_Get_Struct(self, struct Rev_Watcher, watcher_data); | |
| 137 | ||
| 138 | if(watcher_data->loop == Qnil) | |
| 139 | rb_raise(rb_eRuntimeError, "not attached to a loop"); | |
| 140 | ||
| 141 | loop_watchers = rb_iv_get(watcher_data->loop, "@watchers"); | |
| 142 | ||
| 143 | /* Remove us from the loop's array of active watchers. This likely | |
| 144 | * has negative performance and scalability characteristics as this | |
| 145 | * isn't an O(1) operation. Hopefully there's a better way... */ | |
| 146 | rb_ary_delete(loop_watchers, self); | |
| 147 | ||
| 148 | if(watcher_data->enabled) { | |
| 149 | rb_iv_set( | |
| 150 | watcher_data->loop, | |
| 151 | "@active_watchers", | |
| 152 | INT2NUM(NUM2INT(rb_iv_get(watcher_data->loop, "@active_watchers")) - 1) | |
| 153 | ); | |
| 154 | } | |
| 155 | ||
| 156 | watcher_data->enabled = 0; | |
| 157 | ||
| 158 | Data_Get_Struct(watcher_data->loop, struct Rev_Loop, loop_data); | |
| 159 | ||
| 160 | /* Iterate through the events in the loop's event buffer. If there | |
| 161 | * are any pending events from this watcher, mark them NULL. The | |
| 162 | * dispatch loop will skip them. This prevents watchers earlier | |
| 163 | * in the event buffer from detaching others which may have pending | |
| 164 | * events in the buffer but get garbage collected in the meantime */ | |
| 165 | for(i = 0; i < loop_data->events_received; i++) { | |
| 166 | if(loop_data->eventbuf[i].watcher == self) | |
| 167 | loop_data->eventbuf[i].watcher = Qnil; | |
| 168 | } | |
| 169 | ||
| 170 | watcher_data->loop = Qnil; | |
| 171 | ||
| 172 | return self; | |
| 173 | } | |
| 174 | ||
| 175 | /** | |
| 176 | * call-seq: | |
| 177 | * Rev::Watcher.enable -> Rev::Watcher | |
| 178 | * | |
| 179 | * Re-enable a watcher which has been temporarily disabled. See the | |
| 180 | * disable method for a more thorough explanation. | |
| 181 | */ | |
| 182 | static VALUE Rev_Watcher_enable(VALUE self) | |
| 183 | { | |
| 184 | struct Rev_Watcher *watcher_data; | |
| 185 | Data_Get_Struct(self, struct Rev_Watcher, watcher_data); | |
| 186 | ||
| 187 | if(watcher_data->enabled) | |
| 188 | rb_raise(rb_eRuntimeError, "already enabled"); | |
| 189 | ||
| 190 | watcher_data->enabled = 1; | |
| 191 | ||
| 192 | rb_iv_set( | |
| 193 | watcher_data->loop, | |
| 194 | "@active_watchers", | |
| 195 | INT2NUM(NUM2INT(rb_iv_get(watcher_data->loop, "@active_watchers")) + 1) | |
| 196 | ); | |
| 197 | ||
| 198 | return self; | |
| 199 | } | |
| 200 | ||
| 201 | /** | |
| 202 | * call-seq: | |
| 203 | * Rev::Watcher.disable -> Rev::Watcher | |
| 204 | * | |
| 205 | * Temporarily disable an event watcher which is attached to a loop. | |
| 206 | * This is useful if you wish to toggle event monitoring on and off. | |
| 207 | */ | |
| 208 | static VALUE Rev_Watcher_disable(VALUE self) | |
| 209 | { | |
| 210 | struct Rev_Watcher *watcher_data; | |
| 211 | Data_Get_Struct(self, struct Rev_Watcher, watcher_data); | |
| 212 | ||
| 213 | if(!watcher_data->enabled) | |
| 214 | rb_raise(rb_eRuntimeError, "already disabled"); | |
| 215 | ||
| 216 | watcher_data->enabled = 0; | |
| 217 | ||
| 218 | rb_iv_set( | |
| 219 | watcher_data->loop, | |
| 220 | "@active_watchers", | |
| 221 | INT2NUM(NUM2INT(rb_iv_get(watcher_data->loop, "@active_watchers")) - 1) | |
| 222 | ); | |
| 223 | ||
| 224 | return self; | |
| 225 | } | |
| 226 | ||
| 227 | /** | |
| 228 | * call-seq: | |
| 229 | * Rev::Watcher.evloop -> Rev::Loop | |
| 230 | * | |
| 231 | * Return the loop to which we're currently attached | |
| 232 | */ | |
| 233 | static VALUE Rev_Watcher_evloop(VALUE self) | |
| 234 | { | |
| 235 | struct Rev_Watcher *watcher_data; | |
| 236 | ||
| 237 | Data_Get_Struct(self, struct Rev_Watcher, watcher_data); | |
| 238 | return watcher_data->loop; | |
| 239 | } | |
| 240 | ||
| 241 | /** | |
| 242 | * call-seq: | |
| 243 | * Rev::Watcher.attached? -> Boolean | |
| 244 | * | |
| 245 | * Is the watcher currently attached to an event loop? | |
| 246 | */ | |
| 247 | static VALUE Rev_Watcher_attached(VALUE self) | |
| 248 | { | |
| 249 | return Rev_Watcher_evloop(self) != Qnil; | |
| 250 | } | |
| 251 | ||
| 252 | /** | |
| 253 | * call-seq: | |
| 254 | * Rev::Watcher.enabled? -> Boolean | |
| 255 | * | |
| 256 | * Is the watcher currently enabled? | |
| 257 | */ | |
| 258 | static VALUE Rev_Watcher_enabled(VALUE self) | |
| 259 | { | |
| 260 | struct Rev_Watcher *watcher_data; | |
| 261 | Data_Get_Struct(self, struct Rev_Watcher, watcher_data); | |
| 262 | ||
| 263 | return watcher_data->enabled ? Qtrue : Qfalse; | |
| 264 | } |