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 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
@@ -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
+ }
@@ -0,0 +1,14 @@
1
+ /*
2
+ System, 3rd party, and project includes
3
+
4
+ Implements Init_semian, which is used as C/Ruby entrypoint.
5
+ */
6
+
7
+ #ifndef SEMIAN_H
8
+ #define SEMIAN_H
9
+
10
+ #include "resource.h"
11
+
12
+ void Init_semian();
13
+
14
+ #endif //SEMIAN_H