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.
- data/README +58 -0
- data/ext/extconf.rb +10 -0
- data/ext/posixtimer.c +228 -0
- data/test/timertests.rb +48 -0
- 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
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
|
+
}
|
data/test/timertests.rb
ADDED
@@ -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
|
+
|