posixtimer 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/README +58 -0
  2. data/ext/extconf.rb +10 -0
  3. data/ext/posixtimer.c +228 -0
  4. data/test/timertests.rb +48 -0
  5. metadata +47 -0
data/README ADDED
@@ -0,0 +1,58 @@
1
+ # posixtimer v0.1
2
+ #
3
+ # caleb@aei-tech.com
4
+ #
5
+ # Released under the Ruby license
6
+ # 12.09.05
7
+ #
8
+ # Things you can do with a posixtimer:
9
+ #
10
+ # timer = Posix::Timer.new
11
+ #
12
+ # Arm the timer for 10 seconds
13
+ #
14
+ # begin
15
+ # timer.arm(10.0)
16
+ # # ..do something
17
+ # #
18
+ #
19
+ # rescue SignalException
20
+ # # The timer times out here
21
+ #
22
+ # end
23
+ #
24
+ #
25
+ # Specify which type of SIGNAL to use:
26
+ #
27
+ # Posix::Timer.new("ALRM") # default
28
+ # Posix::Timer.new("USR1")
29
+ # ...
30
+ #
31
+ # Pass a block, and it will be called upon timeout
32
+ #
33
+ # a = Posix::Timer.new do
34
+ # puts "hi"
35
+ # raise MyCustomException
36
+ # end
37
+ #
38
+ # a.arm(5.0)
39
+ # sleep 10.0
40
+ #
41
+ # => "hi"
42
+ # MyCustomException is raised
43
+ #
44
+ #
45
+ #
46
+ # Caveats
47
+ #
48
+ #
49
+ # The Posix::Timer instance uses POSIX signals to trigger
50
+ # the timeouts. This may conflict with other signals you wish to
51
+ # use in your application.
52
+ #
53
+ # Passing a block to the constructor will perform a Signal.trap on
54
+ # the signal. This may override an existing Signal.trap. In a future
55
+ # version I intend to rollback the previous trap context.
56
+ #
57
+ # Multiple timers using the same signal may conflict.
58
+
data/ext/extconf.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'mkmf'
2
+
3
+ if ( ( have_library('c', 'timer_create') or
4
+ have_library('rt', 'timer_create') ) and
5
+ have_header('signal.h') and
6
+ have_header('time.h') )
7
+
8
+ enable_config('with-cflags=-restrict', true)
9
+ create_makefile ('posixtimer')
10
+ end
data/ext/posixtimer.c ADDED
@@ -0,0 +1,228 @@
1
+ #include "ruby.h"
2
+ #include <signal.h>
3
+ #include <time.h>
4
+
5
+ typedef struct md {
6
+ clockid_t clock_id;
7
+ timer_t timer_id;
8
+ struct itimerspec tspec;
9
+ struct sigevent se;
10
+ struct sigevent *sig_event;
11
+ } mydata;
12
+
13
+ static mydata* create_timer_data()
14
+ {
15
+ mydata *data;
16
+
17
+ data = malloc(sizeof(mydata));
18
+ data->clock_id = CLOCK_REALTIME;
19
+ data->sig_event = NULL;
20
+
21
+ return data;
22
+ }
23
+
24
+ void create_new_timer(mydata *data)
25
+ {
26
+ int err = timer_create(data->clock_id, data->sig_event, &(data->timer_id));
27
+
28
+ if(err == -1)
29
+ rb_sys_fail("timer_create()");
30
+ }
31
+
32
+ static void destroy_timer(mydata *p)
33
+ {
34
+ timer_delete(p->timer_id);
35
+ }
36
+
37
+ static void free_timer(void *p)
38
+ {
39
+ destroy_timer(p);
40
+ free(p);
41
+ }
42
+
43
+ static VALUE timer_alloc(VALUE klass)
44
+ {
45
+ mydata *data;
46
+ data = create_timer_data();
47
+
48
+ create_new_timer(data);
49
+
50
+ VALUE obj = Data_Wrap_Struct(klass, 0, free_timer, data);
51
+
52
+ return obj;
53
+ }
54
+
55
+ /* call-seq:
56
+ * Posix::Timer.new
57
+ * Posix::Timer.new("ALRM")
58
+ * Posix::Timer.new("USR1")
59
+ * Posix::Timer.new { some_block }
60
+ *
61
+ * Create a new Posix::Timer object. The argument passed can be any valid
62
+ * signal type from Signal.list.
63
+ *
64
+ * If a block is passed, that block will be executed when the Posix::Timer timeout
65
+ * has expired. This is done by trapping the signal specified as creation time, so
66
+ * be careful if you have other uses of that signal in your program.
67
+ *
68
+ */
69
+ static VALUE timer_initialize(int argc, VALUE *argv, VALUE self)
70
+ {
71
+ VALUE sigtype, codeblock;
72
+ rb_scan_args(argc, argv, "01&", &sigtype, &codeblock);
73
+
74
+ if(!NIL_P(sigtype)) Check_Type(sigtype, T_STRING);
75
+ if(NIL_P(sigtype)) sigtype = rb_str_new2("ALRM");
76
+
77
+ VALUE xx_mSignal;
78
+ xx_mSignal = rb_const_get(rb_cObject, rb_intern("Signal"));
79
+
80
+ VALUE sighash = rb_funcall(xx_mSignal, rb_intern("list"), 0);
81
+
82
+ VALUE signal = rb_hash_aref(sighash, sigtype);
83
+
84
+ if(NIL_P(signal)) rb_raise(rb_eArgError, "Invalid signal type");
85
+ Check_Type(signal, T_FIXNUM);
86
+
87
+ rb_iv_set(self, "@sigblock", codeblock);
88
+ rb_iv_set(self, "@sigtype", sigtype);
89
+
90
+ mydata *data;
91
+ Data_Get_Struct(self, mydata, data);
92
+
93
+ struct sigevent se;
94
+
95
+ data->se.sigev_notify = SIGEV_SIGNAL;
96
+ data->se.sigev_signo = FIX2INT(signal);
97
+ data->sig_event = &data->se;
98
+
99
+ destroy_timer(data);
100
+ create_new_timer(data);
101
+
102
+ return self;
103
+ }
104
+
105
+ static VALUE timer_new(int argc, VALUE *argv, VALUE self)
106
+ {
107
+ VALUE obj = rb_funcall(self, rb_intern("allocate"), 0);
108
+
109
+ rb_obj_call_init(obj, argc, argv);
110
+ return obj;
111
+ }
112
+
113
+ /* call-seq:
114
+ * timer.arm(float_time_in_seconds) -> true
115
+ *
116
+ * Arm the timer for timeout. A value of 0 disarms the timer.
117
+ *
118
+ * When the timer timeouts, it will raise a SignalException. If a block was
119
+ * passed to the initializer, the SignalException will be caught and the block
120
+ * will execute instead (and no Exception will be raised).
121
+ *
122
+ */
123
+
124
+ static VALUE timer_arm(VALUE klass, VALUE arg)
125
+ {
126
+ mydata *data;
127
+
128
+ int sec, usec=0;
129
+
130
+ if(TYPE(arg) == T_FIXNUM) {
131
+ sec = FIX2INT(arg);
132
+ }
133
+ else {
134
+ Check_Type(arg, T_FLOAT);
135
+ double offset = NUM2DBL(arg);
136
+ sec = (int)offset;
137
+ usec = (int)((offset - (double)((int)offset)) * 1000000000);
138
+ }
139
+
140
+ Data_Get_Struct(klass, mydata, data);
141
+
142
+ data->tspec.it_value.tv_sec = sec;
143
+ data->tspec.it_value.tv_nsec = usec;
144
+
145
+ if(timer_settime(data->timer_id, 0, &(data->tspec), NULL) == -1)
146
+ rb_sys_fail("timer_settime()");
147
+
148
+ VALUE sigblock = rb_iv_get(klass, "@sigblock");
149
+ VALUE sigtype = rb_iv_get(klass, "@sigtype");
150
+
151
+ if(!NIL_P(sigblock)) {
152
+ VALUE xx_mSignal;
153
+ xx_mSignal = rb_const_get(rb_cObject, rb_intern("Signal"));
154
+ VALUE prevblock = rb_funcall(xx_mSignal, rb_intern("trap"), 2, sigtype, sigblock);
155
+ rb_iv_set(klass, "@prevblock", prevblock);
156
+ }
157
+
158
+ return Qtrue;
159
+ }
160
+
161
+ /* call-seq:
162
+ * timer.disarm -> true
163
+ *
164
+ * Disarm an active timer. Same as timer.arm(0).
165
+ */
166
+ static VALUE timer_disarm(VALUE klass)
167
+ {
168
+ return rb_funcall(klass, rb_intern("arm"), 1, INT2FIX(0) );
169
+ }
170
+
171
+ /* call-seq:
172
+ * timer.timeout -> Float
173
+ *
174
+ * Returns the amount of time (in seconds) left before the timer times out.
175
+ * Returns nil if the timer isn't currently active
176
+ */
177
+ static VALUE timer_timeout(VALUE klass)
178
+ {
179
+ mydata *data;
180
+
181
+ Data_Get_Struct(klass, mydata, data);
182
+
183
+ if(timer_gettime(data->timer_id, &(data->tspec) ) == -1)
184
+ rb_sys_fail("timer_gettime()");
185
+
186
+ if(data->tspec.it_value.tv_sec == 0 && data->tspec.it_value.tv_nsec == 0)
187
+ return Qnil;
188
+
189
+ double timeout = (double)(data->tspec.it_value.tv_sec) +
190
+ ((double)(data->tspec.it_value.tv_nsec) / 1000000000);
191
+
192
+ return rb_float_new(timeout);
193
+ }
194
+
195
+ /* call-seq:
196
+ * timer.armed? -> true
197
+ *
198
+ * Return true if the timer is currently armed, false otherwise.
199
+ */
200
+ static VALUE timer_armed(VALUE klass)
201
+ {
202
+ VALUE ret = rb_funcall(klass, rb_intern("timeout"), 0 );
203
+
204
+ if(NIL_P(ret))
205
+ return Qfalse;
206
+ else
207
+
208
+ return Qtrue;
209
+ }
210
+
211
+ /* Interface to the Posix Timer library calls */
212
+
213
+ void Init_posixtimer() {
214
+ VALUE posix_mod = rb_define_module("Posix");
215
+
216
+ VALUE timer_class =
217
+ rb_define_class_under(posix_mod, "Timer", rb_cObject);
218
+
219
+ rb_define_alloc_func(timer_class, timer_alloc);
220
+
221
+ rb_define_singleton_method(timer_class, "new", timer_new, -1);
222
+
223
+ rb_define_method(timer_class, "initialize", timer_initialize, -1);
224
+ rb_define_method(timer_class, "arm", timer_arm, 1);
225
+ rb_define_method(timer_class, "disarm", timer_disarm, 0);
226
+ rb_define_method(timer_class, "armed?", timer_armed, 0);
227
+ rb_define_method(timer_class, "timeout", timer_timeout, 0);
228
+ }
@@ -0,0 +1,48 @@
1
+ require 'test/unit'
2
+ require 'posixtimer'
3
+
4
+ class TimerTest < Test::Unit::TestCase
5
+
6
+ def test_initialization_methods
7
+ assert_nothing_raised(ArgumentError) { Posix::Timer.new }
8
+ assert_nothing_raised(ArgumentError) { Posix::Timer.new("USR1") }
9
+ assert_nothing_raised(ArgumentError) { Posix::Timer.new("ALRM") }
10
+
11
+ assert_nothing_raised(ArgumentError) { Posix::Timer.new { puts "hi" } }
12
+
13
+ assert_raise(ArgumentError) { Posix::Timer.new("BLAH") }
14
+ assert_raise(TypeError) { Posix::Timer.new(10) }
15
+ end
16
+
17
+ def test_initialization_with_block
18
+ end
19
+
20
+ def test_timer_arming
21
+ a = Posix::Timer.new
22
+ assert a.armed? == false
23
+ a.arm(5)
24
+ assert a.armed? == true
25
+ a.disarm
26
+ assert a.armed? == false
27
+ end
28
+
29
+ def test_timer_exception
30
+ assert_raise(SignalException) do
31
+ b = Posix::Timer.new
32
+ b.arm(0.1)
33
+ sleep 0.5
34
+ end
35
+ end
36
+
37
+ def test_timer_with_block
38
+ i = 5
39
+ a = Posix::Timer.new { i = 10 }
40
+ assert_nothing_raised(SignalException) {
41
+ a.arm(1)
42
+ sleep(2)
43
+ }
44
+
45
+ assert i == 10
46
+ end
47
+
48
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.11
3
+ specification_version: 1
4
+ name: posixtimer
5
+ version: !ruby/object:Gem::Version
6
+ version: "0.1"
7
+ date: 2005-12-09 00:00:00 -05:00
8
+ summary: A class that provides a definable timeout
9
+ require_paths:
10
+ - .
11
+ email: caleb@aei-tech.com
12
+ homepage: ""
13
+ rubyforge_project:
14
+ description:
15
+ autorequire: posixtimer
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ authors:
29
+ - Caleb Tennis
30
+ files:
31
+ - ext/posixtimer.c
32
+ - ext/extconf.rb
33
+ - README
34
+ test_files:
35
+ - test/timertests.rb
36
+ rdoc_options: []
37
+
38
+ extra_rdoc_files:
39
+ - README
40
+ executables: []
41
+
42
+ extensions:
43
+ - ext/extconf.rb
44
+ requirements: []
45
+
46
+ dependencies: []
47
+