semian_extension 0.11.4.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.
- checksums.yaml +7 -0
- data/ext/semian/extconf.rb +33 -0
- data/ext/semian/resource.c +394 -0
- data/ext/semian/resource.h +133 -0
- data/ext/semian/semian.c +72 -0
- data/ext/semian/semian.h +14 -0
- data/ext/semian/sysv_semaphores.c +271 -0
- data/ext/semian/sysv_semaphores.h +122 -0
- data/ext/semian/tickets.c +76 -0
- data/ext/semian/tickets.h +13 -0
- data/ext/semian/types.h +41 -0
- data/lib/semian/adapter.rb +75 -0
- data/lib/semian/circuit_breaker.rb +167 -0
- data/lib/semian/grpc.rb +104 -0
- data/lib/semian/instrumentable.rb +28 -0
- data/lib/semian/lru_hash.rb +174 -0
- data/lib/semian/mysql2.rb +135 -0
- data/lib/semian/net_http.rb +117 -0
- data/lib/semian/platform.rb +16 -0
- data/lib/semian/protected_resource.rb +65 -0
- data/lib/semian/rails.rb +7 -0
- data/lib/semian/redis.rb +143 -0
- data/lib/semian/resource.rb +65 -0
- data/lib/semian/simple_integer.rb +38 -0
- data/lib/semian/simple_sliding_window.rb +68 -0
- data/lib/semian/simple_state.rb +50 -0
- data/lib/semian/typhoeus.rb +103 -0
- data/lib/semian/unprotected_resource.rb +73 -0
- data/lib/semian/version.rb +3 -0
- data/lib/semian.rb +310 -0
- metadata +260 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2fbd05996a9e15b84c54519a393a2ce1bf453d38552a7b22e80a831721f285f4
|
4
|
+
data.tar.gz: fcd1fa08aa00fd91a7cf0384bafff8c531de237995df5b7b252b2142335afd21
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e28e4d4566e503178e0540bf487addc96a7693fd125bbf8d2e1b51edc1d5249caa5b4911c5364e95e4539ee88140f3c20899c8f786f5a2fb1216dde77da6e8d1
|
7
|
+
data.tar.gz: 2e366ace3707305ed07f2f61461a7aed93909cafa42500161f3ca356da2f70239202fa27ee32a688e8b3216e72942461a07cdbedc1f7bf3961e0120465df350b
|
@@ -0,0 +1,33 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path("../../../lib", __FILE__)
|
2
|
+
|
3
|
+
require 'semian/platform'
|
4
|
+
|
5
|
+
unless Semian.sysv_semaphores_supported?
|
6
|
+
File.write "Makefile", <<MAKEFILE
|
7
|
+
all:
|
8
|
+
clean:
|
9
|
+
install:
|
10
|
+
MAKEFILE
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'mkmf'
|
15
|
+
|
16
|
+
abort 'openssl is missing. please install openssl.' unless find_header('openssl/sha.h')
|
17
|
+
abort 'openssl is missing. please install openssl.' unless find_library('crypto', 'SHA1')
|
18
|
+
|
19
|
+
have_header 'sys/ipc.h'
|
20
|
+
have_header 'sys/sem.h'
|
21
|
+
have_header 'sys/types.h'
|
22
|
+
|
23
|
+
have_func 'rb_thread_blocking_region'
|
24
|
+
have_func 'rb_thread_call_without_gvl'
|
25
|
+
|
26
|
+
$CFLAGS = "-D_GNU_SOURCE -Werror -Wall "
|
27
|
+
if ENV.key?('DEBUG')
|
28
|
+
$CFLAGS << "-O0 -g -DDEBUG"
|
29
|
+
else
|
30
|
+
$CFLAGS << "-O3"
|
31
|
+
end
|
32
|
+
|
33
|
+
create_makefile('semian/semian')
|
@@ -0,0 +1,394 @@
|
|
1
|
+
#include "resource.h"
|
2
|
+
|
3
|
+
// Ruby variables
|
4
|
+
ID id_wait_time;
|
5
|
+
ID id_timeout;
|
6
|
+
int system_max_semaphore_count;
|
7
|
+
|
8
|
+
static VALUE
|
9
|
+
cleanup_semian_resource_acquire(VALUE self);
|
10
|
+
|
11
|
+
static void
|
12
|
+
check_tickets_xor_quota_arg(VALUE tickets, VALUE quota);
|
13
|
+
|
14
|
+
static double
|
15
|
+
check_quota_arg(VALUE quota);
|
16
|
+
|
17
|
+
static int
|
18
|
+
check_tickets_arg(VALUE tickets);
|
19
|
+
|
20
|
+
static long
|
21
|
+
check_permissions_arg(VALUE permissions);
|
22
|
+
|
23
|
+
static const
|
24
|
+
char *check_id_arg(VALUE id);
|
25
|
+
|
26
|
+
static double
|
27
|
+
check_default_timeout_arg(VALUE default_timeout);
|
28
|
+
|
29
|
+
static void
|
30
|
+
ms_to_timespec(long ms, struct timespec *ts);
|
31
|
+
|
32
|
+
static const rb_data_type_t
|
33
|
+
semian_resource_type;
|
34
|
+
|
35
|
+
VALUE
|
36
|
+
semian_resource_acquire(int argc, VALUE *argv, VALUE self)
|
37
|
+
{
|
38
|
+
semian_resource_t *self_res = NULL;
|
39
|
+
semian_resource_t res = { 0 };
|
40
|
+
|
41
|
+
if (!rb_block_given_p()) {
|
42
|
+
rb_raise(rb_eArgError, "acquire requires a block");
|
43
|
+
}
|
44
|
+
|
45
|
+
TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, self_res);
|
46
|
+
res = *self_res;
|
47
|
+
|
48
|
+
/* allow the default timeout to be overridden by a "timeout" param */
|
49
|
+
if (argc == 1 && TYPE(argv[0]) == T_HASH) {
|
50
|
+
VALUE timeout = rb_hash_aref(argv[0], ID2SYM(id_timeout));
|
51
|
+
if (TYPE(timeout) != T_NIL) {
|
52
|
+
if (TYPE(timeout) != T_FLOAT && TYPE(timeout) != T_FIXNUM) {
|
53
|
+
rb_raise(rb_eArgError, "timeout parameter must be numeric");
|
54
|
+
}
|
55
|
+
ms_to_timespec(NUM2DBL(timeout) * 1000, &res.timeout);
|
56
|
+
}
|
57
|
+
} else if (argc > 0) {
|
58
|
+
rb_raise(rb_eArgError, "invalid arguments");
|
59
|
+
}
|
60
|
+
|
61
|
+
/* release the GVL to acquire the semaphore */
|
62
|
+
acquire_semaphore_without_gvl(&res);
|
63
|
+
if (res.error != 0) {
|
64
|
+
if (res.error == EAGAIN) {
|
65
|
+
rb_raise(eTimeout, "timed out waiting for resource '%s'", res.name);
|
66
|
+
} else {
|
67
|
+
raise_semian_syscall_error("semop()", res.error);
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
VALUE wait_time = Qnil;
|
72
|
+
if (res.wait_time >= 0) {
|
73
|
+
wait_time = LONG2NUM(res.wait_time);
|
74
|
+
}
|
75
|
+
|
76
|
+
return rb_ensure(rb_yield, wait_time, cleanup_semian_resource_acquire, self);
|
77
|
+
}
|
78
|
+
|
79
|
+
VALUE
|
80
|
+
semian_resource_destroy(VALUE self)
|
81
|
+
{
|
82
|
+
struct timespec ts = { 0 };
|
83
|
+
ts.tv_sec = INTERNAL_TIMEOUT;
|
84
|
+
|
85
|
+
semian_resource_t *res = NULL;
|
86
|
+
|
87
|
+
TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
|
88
|
+
|
89
|
+
// Prevent a race to deletion
|
90
|
+
if (perform_semop(res->sem_id, SI_SEM_LOCK, -1, 0, &ts) == -1) {
|
91
|
+
if (errno == EINVAL || errno == EIDRM) {
|
92
|
+
return Qtrue;
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
if (semctl(res->sem_id, SI_NUM_SEMAPHORES, IPC_RMID) == -1) {
|
97
|
+
raise_semian_syscall_error("semctl()", errno);
|
98
|
+
}
|
99
|
+
|
100
|
+
return Qtrue;
|
101
|
+
}
|
102
|
+
|
103
|
+
VALUE
|
104
|
+
semian_resource_reset_workers(VALUE self)
|
105
|
+
{
|
106
|
+
int ret;
|
107
|
+
semian_resource_t *res = NULL;
|
108
|
+
|
109
|
+
TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
|
110
|
+
|
111
|
+
sem_meta_lock(res->sem_id);
|
112
|
+
// This SETVAL will purge the SEM_UNDO table
|
113
|
+
ret = semctl(res->sem_id, SI_SEM_REGISTERED_WORKERS, SETVAL, 0);
|
114
|
+
sem_meta_unlock(res->sem_id);
|
115
|
+
|
116
|
+
if (ret == -1) {
|
117
|
+
raise_semian_syscall_error("semctl()", errno);
|
118
|
+
}
|
119
|
+
|
120
|
+
return Qtrue;
|
121
|
+
}
|
122
|
+
|
123
|
+
VALUE
|
124
|
+
semian_resource_unregister_worker(VALUE self)
|
125
|
+
{
|
126
|
+
int ret;
|
127
|
+
semian_resource_t *res = NULL;
|
128
|
+
|
129
|
+
TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
|
130
|
+
|
131
|
+
sem_meta_lock(res->sem_id);
|
132
|
+
ret = perform_semop(res->sem_id, SI_SEM_REGISTERED_WORKERS, -1, IPC_NOWAIT | SEM_UNDO, NULL);
|
133
|
+
sem_meta_unlock(res->sem_id);
|
134
|
+
|
135
|
+
if ( ret == -1) {
|
136
|
+
// Allow EAGAIN with IPC_NOWAIT, as this signals that all workers were unregistered
|
137
|
+
// Otherwise, we might block forever or throw an unintended timeout
|
138
|
+
if (errno != EAGAIN) {
|
139
|
+
rb_raise(eInternal, "error decreasing registered workers, errno: %d (%s)", errno, strerror(errno));
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
return Qtrue;
|
144
|
+
}
|
145
|
+
|
146
|
+
VALUE
|
147
|
+
semian_resource_count(VALUE self)
|
148
|
+
{
|
149
|
+
int ret;
|
150
|
+
semian_resource_t *res = NULL;
|
151
|
+
|
152
|
+
TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
|
153
|
+
ret = semctl(res->sem_id, SI_SEM_TICKETS, GETVAL);
|
154
|
+
if (ret == -1) {
|
155
|
+
raise_semian_syscall_error("semctl()", errno);
|
156
|
+
}
|
157
|
+
|
158
|
+
return LONG2FIX(ret);
|
159
|
+
}
|
160
|
+
|
161
|
+
VALUE
|
162
|
+
semian_resource_tickets(VALUE self)
|
163
|
+
{
|
164
|
+
int ret;
|
165
|
+
semian_resource_t *res = NULL;
|
166
|
+
|
167
|
+
TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
|
168
|
+
ret = semctl(res->sem_id, SI_SEM_CONFIGURED_TICKETS, GETVAL);
|
169
|
+
if (ret == -1) {
|
170
|
+
raise_semian_syscall_error("semctl()", errno);
|
171
|
+
}
|
172
|
+
|
173
|
+
return LONG2FIX(ret);
|
174
|
+
}
|
175
|
+
|
176
|
+
VALUE
|
177
|
+
semian_resource_workers(VALUE self)
|
178
|
+
{
|
179
|
+
int ret;
|
180
|
+
semian_resource_t *res = NULL;
|
181
|
+
|
182
|
+
TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
|
183
|
+
ret = semctl(res->sem_id, SI_SEM_REGISTERED_WORKERS, GETVAL);
|
184
|
+
if (ret == -1) {
|
185
|
+
raise_semian_syscall_error("semctl()", errno);
|
186
|
+
}
|
187
|
+
|
188
|
+
return LONG2FIX(ret);
|
189
|
+
}
|
190
|
+
|
191
|
+
VALUE
|
192
|
+
semian_resource_id(VALUE self)
|
193
|
+
{
|
194
|
+
semian_resource_t *res = NULL;
|
195
|
+
TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
|
196
|
+
return LONG2FIX(res->sem_id);
|
197
|
+
}
|
198
|
+
|
199
|
+
VALUE
|
200
|
+
semian_resource_key(VALUE self)
|
201
|
+
{
|
202
|
+
semian_resource_t *res = NULL;
|
203
|
+
TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
|
204
|
+
return rb_str_new_cstr(res->strkey);
|
205
|
+
}
|
206
|
+
|
207
|
+
VALUE
|
208
|
+
semian_resource_initialize(VALUE self, VALUE id, VALUE tickets, VALUE quota, VALUE permissions, VALUE default_timeout)
|
209
|
+
{
|
210
|
+
long c_permissions;
|
211
|
+
double c_timeout;
|
212
|
+
double c_quota;
|
213
|
+
int c_tickets;
|
214
|
+
semian_resource_t *res = NULL;
|
215
|
+
const char *c_id_str = NULL;
|
216
|
+
|
217
|
+
// Check and cast arguments
|
218
|
+
check_tickets_xor_quota_arg(tickets, quota);
|
219
|
+
c_quota = check_quota_arg(quota);
|
220
|
+
c_tickets = check_tickets_arg(tickets);
|
221
|
+
c_permissions = check_permissions_arg(permissions);
|
222
|
+
c_id_str = check_id_arg(id);
|
223
|
+
c_timeout = check_default_timeout_arg(default_timeout);
|
224
|
+
|
225
|
+
// Build semian resource structure
|
226
|
+
TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
|
227
|
+
|
228
|
+
// Populate struct fields
|
229
|
+
ms_to_timespec(c_timeout * 1000, &res->timeout);
|
230
|
+
res->name = strdup(c_id_str);
|
231
|
+
res->quota = c_quota;
|
232
|
+
res->wait_time = -1;
|
233
|
+
|
234
|
+
// Initialize the semaphore set
|
235
|
+
initialize_semaphore_set(res, c_id_str, c_permissions, c_tickets, c_quota);
|
236
|
+
|
237
|
+
return self;
|
238
|
+
}
|
239
|
+
|
240
|
+
VALUE
|
241
|
+
semian_resource_alloc(VALUE klass)
|
242
|
+
{
|
243
|
+
semian_resource_t *res;
|
244
|
+
VALUE obj = TypedData_Make_Struct(klass, semian_resource_t, &semian_resource_type, res);
|
245
|
+
return obj;
|
246
|
+
}
|
247
|
+
|
248
|
+
VALUE
|
249
|
+
semian_resource_in_use(VALUE self)
|
250
|
+
{
|
251
|
+
return Qtrue;
|
252
|
+
}
|
253
|
+
|
254
|
+
static VALUE
|
255
|
+
cleanup_semian_resource_acquire(VALUE self)
|
256
|
+
{
|
257
|
+
semian_resource_t *res = NULL;
|
258
|
+
TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
|
259
|
+
if (perform_semop(res->sem_id, SI_SEM_TICKETS, 1, SEM_UNDO, NULL) == -1) {
|
260
|
+
res->error = errno;
|
261
|
+
}
|
262
|
+
return Qnil;
|
263
|
+
}
|
264
|
+
|
265
|
+
static long
|
266
|
+
check_permissions_arg(VALUE permissions)
|
267
|
+
{
|
268
|
+
Check_Type(permissions, T_FIXNUM);
|
269
|
+
return FIX2LONG(permissions);
|
270
|
+
}
|
271
|
+
|
272
|
+
static void
|
273
|
+
check_tickets_xor_quota_arg(VALUE tickets, VALUE quota)
|
274
|
+
{
|
275
|
+
if (tickets == Qnil && quota == Qnil) {
|
276
|
+
rb_raise(rb_eArgError, "Semian configuration require either the :ticket or :quota parameter, you provided neither");
|
277
|
+
}
|
278
|
+
if (tickets != Qnil && quota != Qnil) {
|
279
|
+
rb_raise(rb_eArgError, "Semian configuration require either the :ticket or :quota parameter, you provided both");
|
280
|
+
}
|
281
|
+
}
|
282
|
+
|
283
|
+
static double
|
284
|
+
check_quota_arg(VALUE quota)
|
285
|
+
{
|
286
|
+
double c_quota;
|
287
|
+
|
288
|
+
if (TYPE(quota) != T_NIL) {
|
289
|
+
if (TYPE(quota) != T_FIXNUM && TYPE(quota) != T_FLOAT) {
|
290
|
+
rb_raise(rb_eTypeError, "expected decimal type for quota");
|
291
|
+
}
|
292
|
+
if (NUM2DBL(quota) <= 0 || NUM2DBL(quota) > 1) {
|
293
|
+
rb_raise(rb_eArgError, "quota must be a decimal between 0 and 1");
|
294
|
+
}
|
295
|
+
c_quota = NUM2DBL(quota);
|
296
|
+
} else {
|
297
|
+
c_quota = -1.0f;
|
298
|
+
}
|
299
|
+
return c_quota;
|
300
|
+
}
|
301
|
+
|
302
|
+
static int
|
303
|
+
check_tickets_arg(VALUE tickets)
|
304
|
+
{
|
305
|
+
int c_tickets;
|
306
|
+
|
307
|
+
if (TYPE(tickets) != T_NIL) {
|
308
|
+
if (TYPE(tickets) == T_FLOAT) {
|
309
|
+
rb_warn("semian ticket value %f is a float, converting to fixnum", RFLOAT_VALUE(tickets));
|
310
|
+
tickets = INT2FIX((int) RFLOAT_VALUE(tickets));
|
311
|
+
}
|
312
|
+
Check_Type(tickets, T_FIXNUM);
|
313
|
+
|
314
|
+
if (FIX2LONG(tickets) < 0 || FIX2LONG(tickets) > system_max_semaphore_count) {
|
315
|
+
rb_raise(rb_eArgError, "ticket count must be a non-negative value and less than %d", system_max_semaphore_count);
|
316
|
+
}
|
317
|
+
c_tickets = FIX2LONG(tickets);
|
318
|
+
} else {
|
319
|
+
c_tickets = -1;
|
320
|
+
}
|
321
|
+
|
322
|
+
return c_tickets;
|
323
|
+
}
|
324
|
+
|
325
|
+
static const char*
|
326
|
+
check_id_arg(VALUE id)
|
327
|
+
{
|
328
|
+
const char *c_id_str = NULL;
|
329
|
+
|
330
|
+
if (TYPE(id) != T_SYMBOL && TYPE(id) != T_STRING) {
|
331
|
+
rb_raise(rb_eTypeError, "id must be a symbol or string");
|
332
|
+
}
|
333
|
+
if (TYPE(id) == T_SYMBOL) {
|
334
|
+
c_id_str = rb_id2name(rb_to_id(id));
|
335
|
+
} else if (TYPE(id) == T_STRING) {
|
336
|
+
c_id_str = RSTRING_PTR(id);
|
337
|
+
}
|
338
|
+
|
339
|
+
return c_id_str;
|
340
|
+
}
|
341
|
+
|
342
|
+
static double
|
343
|
+
check_default_timeout_arg(VALUE default_timeout)
|
344
|
+
{
|
345
|
+
if (TYPE(default_timeout) != T_FIXNUM && TYPE(default_timeout) != T_FLOAT) {
|
346
|
+
rb_raise(rb_eTypeError, "expected numeric type for default_timeout");
|
347
|
+
}
|
348
|
+
|
349
|
+
if (NUM2DBL(default_timeout) < 0) {
|
350
|
+
rb_raise(rb_eArgError, "default timeout must be non-negative value");
|
351
|
+
}
|
352
|
+
return NUM2DBL(default_timeout);
|
353
|
+
}
|
354
|
+
|
355
|
+
static void
|
356
|
+
ms_to_timespec(long ms, struct timespec *ts)
|
357
|
+
{
|
358
|
+
ts->tv_sec = ms / 1000;
|
359
|
+
ts->tv_nsec = (ms % 1000) * 1000000;
|
360
|
+
}
|
361
|
+
|
362
|
+
static inline void
|
363
|
+
semian_resource_mark(void *ptr)
|
364
|
+
{
|
365
|
+
/* noop */
|
366
|
+
}
|
367
|
+
|
368
|
+
static inline void
|
369
|
+
semian_resource_free(void *ptr)
|
370
|
+
{
|
371
|
+
semian_resource_t *res = (semian_resource_t *) ptr;
|
372
|
+
if (res->name) {
|
373
|
+
free(res->name);
|
374
|
+
res->name = NULL;
|
375
|
+
}
|
376
|
+
xfree(res);
|
377
|
+
}
|
378
|
+
|
379
|
+
static inline size_t
|
380
|
+
semian_resource_memsize(const void *ptr)
|
381
|
+
{
|
382
|
+
return sizeof(semian_resource_t);
|
383
|
+
}
|
384
|
+
|
385
|
+
static const rb_data_type_t
|
386
|
+
semian_resource_type = {
|
387
|
+
"semian_resource",
|
388
|
+
{
|
389
|
+
semian_resource_mark,
|
390
|
+
semian_resource_free,
|
391
|
+
semian_resource_memsize
|
392
|
+
},
|
393
|
+
NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY
|
394
|
+
};
|
@@ -0,0 +1,133 @@
|
|
1
|
+
/*
|
2
|
+
For core semian resource functions exposed directly to ruby.
|
3
|
+
|
4
|
+
Functions here are associated with rubyland operations.
|
5
|
+
*/
|
6
|
+
#ifndef SEMIAN_RESOURCE_H
|
7
|
+
#define SEMIAN_RESOURCE_H
|
8
|
+
|
9
|
+
#include "types.h"
|
10
|
+
#include "sysv_semaphores.h"
|
11
|
+
|
12
|
+
// Ruby variables
|
13
|
+
extern ID id_wait_time;
|
14
|
+
extern ID id_timeout;
|
15
|
+
extern int system_max_semaphore_count;
|
16
|
+
|
17
|
+
/*
|
18
|
+
* call-seq:
|
19
|
+
* Semian::Resource.new(id, tickets, quota, permissions, default_timeout) -> resource
|
20
|
+
*
|
21
|
+
* Creates a new Resource. Do not create resources directly. Use Semian.register.
|
22
|
+
*/
|
23
|
+
VALUE
|
24
|
+
semian_resource_initialize(VALUE self, VALUE id, VALUE tickets, VALUE quota, VALUE permissions, VALUE default_timeout);
|
25
|
+
|
26
|
+
/*
|
27
|
+
* call-seq:
|
28
|
+
* resource.acquire(timeout: default_timeout) { ... } -> result of the block
|
29
|
+
*
|
30
|
+
* Acquires a resource. The call will block for <code>timeout</code> seconds if a ticket
|
31
|
+
* is not available. If no ticket is available within the timeout period, Semian::TimeoutError
|
32
|
+
* will be raised.
|
33
|
+
*
|
34
|
+
* If no timeout argument is provided, the default timeout passed to Semian.register will be used.
|
35
|
+
*
|
36
|
+
*/
|
37
|
+
VALUE
|
38
|
+
semian_resource_acquire(int argc, VALUE *argv, VALUE self);
|
39
|
+
|
40
|
+
/*
|
41
|
+
* call-seq:
|
42
|
+
* resource.destroy() -> true
|
43
|
+
*
|
44
|
+
* Destroys a resource. This method will destroy the underlying SysV semaphore.
|
45
|
+
* If there is any code in other threads or processes blocking or using the resource
|
46
|
+
* they will likely raise.
|
47
|
+
*
|
48
|
+
* Use this method very carefully.
|
49
|
+
*/
|
50
|
+
VALUE
|
51
|
+
semian_resource_destroy(VALUE self);
|
52
|
+
|
53
|
+
/*
|
54
|
+
* call-seq:
|
55
|
+
* resource.reset_registered_workers!() -> true
|
56
|
+
*
|
57
|
+
* Unregisters all registered workers for a resource, forcefully setting the worker count back to 0.
|
58
|
+
* This will purge the SEM_UNDO table.
|
59
|
+
*
|
60
|
+
* Use this method very carefully.
|
61
|
+
*/
|
62
|
+
VALUE
|
63
|
+
semian_resource_reset_workers(VALUE self);
|
64
|
+
|
65
|
+
/*
|
66
|
+
* call-seq:
|
67
|
+
* resource.count -> count
|
68
|
+
*
|
69
|
+
* Returns the current ticket count for a resource.
|
70
|
+
*/
|
71
|
+
VALUE
|
72
|
+
semian_resource_count(VALUE self);
|
73
|
+
|
74
|
+
/*
|
75
|
+
* call-seq:
|
76
|
+
* resource.tickets -> count
|
77
|
+
*
|
78
|
+
* Returns the configured number of tickets for a resource.
|
79
|
+
*/
|
80
|
+
VALUE
|
81
|
+
semian_resource_tickets(VALUE self);
|
82
|
+
|
83
|
+
/*
|
84
|
+
* call-seq:
|
85
|
+
* resource.registered_workers -> count
|
86
|
+
*
|
87
|
+
* Returns the number of workers (processes) registered to use the resource
|
88
|
+
*/
|
89
|
+
VALUE
|
90
|
+
semian_resource_workers(VALUE self);
|
91
|
+
|
92
|
+
/*
|
93
|
+
* call-seq:
|
94
|
+
* resource.semid -> id
|
95
|
+
*
|
96
|
+
* Returns the SysV semaphore id of a resource.
|
97
|
+
* Note: This value varies from system to system, and between
|
98
|
+
* instances of semian.
|
99
|
+
*/
|
100
|
+
VALUE
|
101
|
+
semian_resource_id(VALUE self);
|
102
|
+
|
103
|
+
/*
|
104
|
+
* call-seq:
|
105
|
+
* resource.key -> id
|
106
|
+
*
|
107
|
+
* Returns the hex string representation of SysV semaphore key of a resource.
|
108
|
+
* Note: This is a unique identifier that is portable across system
|
109
|
+
* and instances of semian.
|
110
|
+
*/
|
111
|
+
VALUE
|
112
|
+
semian_resource_key(VALUE self);
|
113
|
+
|
114
|
+
/*
|
115
|
+
* call-seq:
|
116
|
+
* resource.unregister_worker() -> true
|
117
|
+
*
|
118
|
+
* Unregisters a worker, which will affect quota calculations.
|
119
|
+
*
|
120
|
+
* Be careful to call this only once per process.
|
121
|
+
*/
|
122
|
+
VALUE
|
123
|
+
semian_resource_unregister_worker(VALUE self);
|
124
|
+
|
125
|
+
// Allocate a semian_resource_type struct for ruby memory management
|
126
|
+
VALUE
|
127
|
+
semian_resource_alloc(VALUE klass);
|
128
|
+
|
129
|
+
// Returns true if the resource is in use
|
130
|
+
VALUE
|
131
|
+
semian_resource_in_use(VALUE self);
|
132
|
+
|
133
|
+
#endif //SEMIAN_RESOURCE_H
|
data/ext/semian/semian.c
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
#include "semian.h"
|
2
|
+
|
3
|
+
VALUE eSyscall, eTimeout, eInternal;
|
4
|
+
|
5
|
+
void Init_semian()
|
6
|
+
{
|
7
|
+
VALUE cSemian, cResource;
|
8
|
+
struct seminfo info_buf;
|
9
|
+
|
10
|
+
cSemian = rb_const_get(rb_cObject, rb_intern("Semian"));
|
11
|
+
|
12
|
+
/*
|
13
|
+
* Document-class: Semian::Resource
|
14
|
+
*
|
15
|
+
* Resource is the fundamental class of Semian. It is essentially a wrapper around a
|
16
|
+
* SystemV semaphore.
|
17
|
+
*
|
18
|
+
* You should not create this class directly, it will be created indirectly via Semian.register.
|
19
|
+
*/
|
20
|
+
cResource = rb_const_get(cSemian, rb_intern("Resource"));
|
21
|
+
|
22
|
+
/* Document-class: Semian::SyscallError
|
23
|
+
*
|
24
|
+
* Represents a Semian error that was caused by an underlying syscall failure.
|
25
|
+
*/
|
26
|
+
eSyscall = rb_const_get(cSemian, rb_intern("SyscallError"));
|
27
|
+
rb_global_variable(&eSyscall);
|
28
|
+
|
29
|
+
/* Document-class: Semian::TimeoutError
|
30
|
+
*
|
31
|
+
* Raised when a Semian operation timed out.
|
32
|
+
*/
|
33
|
+
eTimeout = rb_const_get(cSemian, rb_intern("TimeoutError"));
|
34
|
+
rb_global_variable(&eTimeout);
|
35
|
+
|
36
|
+
/* Document-class: Semian::InternalError
|
37
|
+
*
|
38
|
+
* An internal Semian error. These errors should be typically never be raised. If
|
39
|
+
* they do, there's a high likelyhood that the underlying SysV semaphore set
|
40
|
+
* has been corrupted.
|
41
|
+
*
|
42
|
+
* If this happens, a strong course of action would be to delete the semaphores
|
43
|
+
* using the <code>ipcrm</code> command line tool. Semian will re-initialize
|
44
|
+
* the semaphore in this case.
|
45
|
+
*/
|
46
|
+
eInternal = rb_const_get(cSemian, rb_intern("InternalError"));
|
47
|
+
rb_global_variable(&eInternal);
|
48
|
+
|
49
|
+
rb_define_alloc_func(cResource, semian_resource_alloc);
|
50
|
+
rb_define_method(cResource, "initialize_semaphore", semian_resource_initialize, 5);
|
51
|
+
rb_define_method(cResource, "acquire", semian_resource_acquire, -1);
|
52
|
+
rb_define_method(cResource, "count", semian_resource_count, 0);
|
53
|
+
rb_define_method(cResource, "semid", semian_resource_id, 0);
|
54
|
+
rb_define_method(cResource, "key", semian_resource_key, 0);
|
55
|
+
rb_define_method(cResource, "tickets", semian_resource_tickets, 0);
|
56
|
+
rb_define_method(cResource, "registered_workers", semian_resource_workers, 0);
|
57
|
+
rb_define_method(cResource, "destroy", semian_resource_destroy, 0);
|
58
|
+
rb_define_method(cResource, "reset_registered_workers!", semian_resource_reset_workers, 0);
|
59
|
+
rb_define_method(cResource, "unregister_worker", semian_resource_unregister_worker, 0);
|
60
|
+
rb_define_method(cResource, "in_use?", semian_resource_in_use, 0);
|
61
|
+
|
62
|
+
id_wait_time = rb_intern("wait_time");
|
63
|
+
id_timeout = rb_intern("timeout");
|
64
|
+
|
65
|
+
if (semctl(0, 0, SEM_INFO, &info_buf) == -1) {
|
66
|
+
rb_raise(eInternal, "unable to determine maximum semaphore count - semctl() returned %d: %s ", errno, strerror(errno));
|
67
|
+
}
|
68
|
+
system_max_semaphore_count = info_buf.semvmx;
|
69
|
+
|
70
|
+
/* Maximum number of tickets available on this system. */
|
71
|
+
rb_define_const(cSemian, "MAX_TICKETS", INT2FIX(system_max_semaphore_count));
|
72
|
+
}
|
data/ext/semian/semian.h
ADDED