posixtimer 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.
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
+