semian 0.6.0 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5f129376bb8de391e650f09c9c5bd7fdc0b59522
4
- data.tar.gz: fb49ab21418fc18bf30c20b79c510c1db310a015
3
+ metadata.gz: 9844c9c807927ab334939c439c4de127033f5189
4
+ data.tar.gz: 5bb4675179a75c585cd05206dbdbf2777002f403
5
5
  SHA512:
6
- metadata.gz: 4dbfd5d330c92ab3c3bb74a6b0b083903a5aaec0453423d05c53e9ef615125e76e3c5578a5b3b23975916261915ab7e2a5b0707312173f166b6508bd7acfa5c3
7
- data.tar.gz: 2cb0dc4d9765fa70cd7fd7572a532306aba89a4cea16c228634874c1e0ccc12776661c2e9579e284a874d9f47eb25dbee1ac192ef4b1f06bf7e3c94c79da1b79
6
+ metadata.gz: 9331e3c0cdd6c41084d9e7a1ec4ed74914ad6b73ec2b7bd90f22b6a9aebb5ec296ca1995c1a02ff28168c73797f7789c4cdbc35f7bdecb39b89faaf284194379
7
+ data.tar.gz: 12949dc756b01c369844e6742cebf49626ad1af9a1eb37233e2c4aad4dc5ba4faeb8f9260563fd617e93605870f7ad94acf0a9720dfd6feaf68036a1ca264905
@@ -0,0 +1,257 @@
1
+ #include "resource.h"
2
+
3
+ static VALUE
4
+ cleanup_semian_resource_acquire(VALUE self);
5
+
6
+ static int
7
+ check_tickets_arg(VALUE tickets);
8
+
9
+ static long
10
+ check_permissions_arg(VALUE permissions);
11
+
12
+ static const
13
+ char *check_id_arg(VALUE id);
14
+
15
+ static double
16
+ check_default_timeout_arg(VALUE default_timeout);
17
+
18
+ static void
19
+ ms_to_timespec(long ms, struct timespec *ts);
20
+
21
+ static const rb_data_type_t
22
+ semian_resource_type;
23
+
24
+ VALUE
25
+ semian_resource_acquire(int argc, VALUE *argv, VALUE self)
26
+ {
27
+ semian_resource_t *self_res = NULL;
28
+ semian_resource_t res = { 0 };
29
+
30
+ if (!rb_block_given_p()) {
31
+ rb_raise(rb_eArgError, "acquire requires a block");
32
+ }
33
+
34
+ TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, self_res);
35
+ res = *self_res;
36
+
37
+ /* allow the default timeout to be overridden by a "timeout" param */
38
+ if (argc == 1 && TYPE(argv[0]) == T_HASH) {
39
+ VALUE timeout = rb_hash_aref(argv[0], ID2SYM(id_timeout));
40
+ if (TYPE(timeout) != T_NIL) {
41
+ if (TYPE(timeout) != T_FLOAT && TYPE(timeout) != T_FIXNUM) {
42
+ rb_raise(rb_eArgError, "timeout parameter must be numeric");
43
+ }
44
+ ms_to_timespec(NUM2DBL(timeout) * 1000, &res.timeout);
45
+ }
46
+ } else if (argc > 0) {
47
+ rb_raise(rb_eArgError, "invalid arguments");
48
+ }
49
+
50
+ /* release the GVL to acquire the semaphore */
51
+ WITHOUT_GVL(acquire_semaphore_without_gvl, &res, RUBY_UBF_IO, NULL); // FIXME - replace with wrapped version
52
+ if (res.error != 0) {
53
+ if (res.error == EAGAIN) {
54
+ rb_raise(eTimeout, "timed out waiting for resource '%s'", res.name);
55
+ } else {
56
+ raise_semian_syscall_error("semop()", res.error);
57
+ }
58
+ }
59
+
60
+ return rb_ensure(rb_yield, self, cleanup_semian_resource_acquire, self);
61
+ }
62
+
63
+ VALUE
64
+ semian_resource_destroy(VALUE self)
65
+ {
66
+ semian_resource_t *res = NULL;
67
+
68
+ TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
69
+ if (semctl(res->sem_id, SI_NUM_SEMAPHORES, IPC_RMID) == -1) {
70
+ raise_semian_syscall_error("semctl()", errno);
71
+ }
72
+
73
+ return Qtrue;
74
+ }
75
+
76
+ VALUE
77
+ semian_resource_count(VALUE self)
78
+ {
79
+ int ret;
80
+ semian_resource_t *res = NULL;
81
+
82
+ TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
83
+ ret = semctl(res->sem_id, SI_SEM_TICKETS, GETVAL);
84
+ if (ret == -1) {
85
+ raise_semian_syscall_error("semctl()", errno);
86
+ }
87
+
88
+ return LONG2FIX(ret);
89
+ }
90
+
91
+ VALUE
92
+ semian_resource_id(VALUE self)
93
+ {
94
+ semian_resource_t *res = NULL;
95
+ TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
96
+ return LONG2FIX(res->sem_id);
97
+ }
98
+
99
+ VALUE
100
+ semian_resource_initialize(VALUE self, VALUE id, VALUE tickets, VALUE permissions, VALUE default_timeout)
101
+ {
102
+ key_t key;
103
+ int c_permissions;
104
+ double c_timeout;
105
+ int c_tickets;
106
+ int created = 0;
107
+ semian_resource_t *res = NULL;
108
+ const char *c_id_str = NULL;
109
+
110
+ // Check and cast arguments
111
+ c_tickets = check_tickets_arg(tickets);
112
+ c_permissions = check_permissions_arg(permissions);
113
+ c_id_str = check_id_arg(id);
114
+ c_timeout = check_default_timeout_arg(default_timeout);
115
+
116
+ // Build semian resource structure
117
+ TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
118
+
119
+ // Populate struct fields
120
+ ms_to_timespec(c_timeout * 1000, &res->timeout);
121
+ res->name = strdup(c_id_str);
122
+
123
+ // Get or create semaphore set
124
+ // note that tickets = 0 will be used to acquire a semaphore set after it's been created elswhere
125
+ key = generate_key(c_id_str);
126
+ res->sem_id = c_tickets == 0 ? get_semaphore(key) : create_semaphore(key, c_permissions, &created);
127
+ if (res->sem_id == -1) {
128
+ raise_semian_syscall_error("semget()", errno);
129
+ }
130
+
131
+ set_semaphore_permissions(res->sem_id, c_permissions);
132
+
133
+ // Configure semaphore ticket counts
134
+ configure_tickets(res->sem_id, c_tickets, created);
135
+
136
+ return self;
137
+ }
138
+
139
+ VALUE
140
+ semian_resource_alloc(VALUE klass)
141
+ {
142
+ semian_resource_t *res;
143
+ VALUE obj = TypedData_Make_Struct(klass, semian_resource_t, &semian_resource_type, res);
144
+ return obj;
145
+ }
146
+
147
+ static VALUE
148
+ cleanup_semian_resource_acquire(VALUE self)
149
+ {
150
+ semian_resource_t *res = NULL;
151
+ TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
152
+ if (perform_semop(res->sem_id, SI_SEM_TICKETS, 1, SEM_UNDO, NULL) == -1) {
153
+ res->error = errno;
154
+ }
155
+ return Qnil;
156
+ }
157
+
158
+ static long
159
+ check_permissions_arg(VALUE permissions)
160
+ {
161
+ Check_Type(permissions, T_FIXNUM);
162
+ return FIX2LONG(permissions);
163
+ }
164
+
165
+ static int
166
+ check_tickets_arg(VALUE tickets)
167
+ {
168
+ int c_tickets;
169
+
170
+ if (TYPE(tickets) != T_NIL) {
171
+ if (TYPE(tickets) == T_FLOAT) {
172
+ rb_warn("semian ticket value %f is a float, converting to fixnum", RFLOAT_VALUE(tickets));
173
+ tickets = INT2FIX((int) RFLOAT_VALUE(tickets));
174
+ }
175
+ Check_Type(tickets, T_FIXNUM);
176
+
177
+ if (FIX2LONG(tickets) < 0 || FIX2LONG(tickets) > system_max_semaphore_count) {
178
+ rb_raise(rb_eArgError, "ticket count must be a non-negative value and less than %d", system_max_semaphore_count);
179
+ }
180
+ c_tickets = FIX2LONG(tickets);
181
+ } else {
182
+ c_tickets = -1;
183
+ }
184
+
185
+ return c_tickets;
186
+ }
187
+
188
+ static const char*
189
+ check_id_arg(VALUE id)
190
+ {
191
+ const char *c_id_str = NULL;
192
+
193
+ if (TYPE(id) != T_SYMBOL && TYPE(id) != T_STRING) {
194
+ rb_raise(rb_eTypeError, "id must be a symbol or string");
195
+ }
196
+ if (TYPE(id) == T_SYMBOL) {
197
+ c_id_str = rb_id2name(rb_to_id(id));
198
+ } else if (TYPE(id) == T_STRING) {
199
+ c_id_str = RSTRING_PTR(id);
200
+ }
201
+
202
+ return c_id_str;
203
+ }
204
+
205
+ static double
206
+ check_default_timeout_arg(VALUE default_timeout)
207
+ {
208
+ if (TYPE(default_timeout) != T_FIXNUM && TYPE(default_timeout) != T_FLOAT) {
209
+ rb_raise(rb_eTypeError, "expected numeric type for default_timeout");
210
+ }
211
+
212
+ if (NUM2DBL(default_timeout) < 0) {
213
+ rb_raise(rb_eArgError, "default timeout must be non-negative value");
214
+ }
215
+ return NUM2DBL(default_timeout);
216
+ }
217
+
218
+ static void
219
+ ms_to_timespec(long ms, struct timespec *ts)
220
+ {
221
+ ts->tv_sec = ms / 1000;
222
+ ts->tv_nsec = (ms % 1000) * 1000000;
223
+ }
224
+
225
+ static inline void
226
+ semian_resource_mark(void *ptr)
227
+ {
228
+ /* noop */
229
+ }
230
+
231
+ static inline void
232
+ semian_resource_free(void *ptr)
233
+ {
234
+ semian_resource_t *res = (semian_resource_t *) ptr;
235
+ if (res->name) {
236
+ free(res->name);
237
+ res->name = NULL;
238
+ }
239
+ xfree(res);
240
+ }
241
+
242
+ static inline size_t
243
+ semian_resource_memsize(const void *ptr)
244
+ {
245
+ return sizeof(semian_resource_t);
246
+ }
247
+
248
+ static const rb_data_type_t
249
+ semian_resource_type = {
250
+ "semian_resource",
251
+ {
252
+ semian_resource_mark,
253
+ semian_resource_free,
254
+ semian_resource_memsize
255
+ },
256
+ NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY
257
+ };
@@ -0,0 +1,75 @@
1
+ /*
2
+ For core semian resource functions exposed directly to ruby.
3
+
4
+ Functions here are associated with rubyland operations.
5
+ */
6
+
7
+ #ifndef SEMIAN_RESOURCE_H
8
+ #define SEMIAN_RESOURCE_H
9
+
10
+ #include "semian.h" // FIXME remove this once temporary declarations are removed
11
+ #include "types.h"
12
+
13
+ // Ruby variables
14
+ ID id_timeout;
15
+ int system_max_semaphore_count;
16
+
17
+ /*
18
+ * call-seq:
19
+ * Semian::Resource.new(id, tickets, 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 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.count -> count
56
+ *
57
+ * Returns the current ticket count for a resource.
58
+ */
59
+ VALUE
60
+ semian_resource_count(VALUE self);
61
+
62
+ /*
63
+ * call-seq:
64
+ * resource.semid -> id
65
+ *
66
+ * Returns the SysV semaphore id of a resource.
67
+ */
68
+ VALUE
69
+ semian_resource_id(VALUE self);
70
+
71
+ // Allocate a semian_resource_type struct for ruby memory management
72
+ VALUE
73
+ semian_resource_alloc(VALUE klass);
74
+
75
+ #endif //SEMIAN_RESOURCE_H
@@ -1,122 +1,40 @@
1
- #include <sys/types.h>
2
- #include <sys/ipc.h>
3
- #include <sys/sem.h>
4
- #include <sys/time.h>
5
- #include <errno.h>
6
- #include <string.h>
7
-
8
- #include <ruby.h>
9
- #include <ruby/util.h>
10
- #include <ruby/io.h>
11
-
12
- #include <openssl/sha.h>
13
-
14
- #include <stdio.h>
15
-
16
- union semun {
17
- int val; /* Value for SETVAL */
18
- struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
19
- unsigned short *array; /* Array for GETALL, SETALL */
20
- struct seminfo *__buf; /* Buffer for IPC_INFO
21
- (Linux-specific) */
22
- };
23
-
24
- #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) && defined(HAVE_RUBY_THREAD_H)
25
- // 2.0
26
- #include <ruby/thread.h>
27
- #define WITHOUT_GVL(fn,a,ubf,b) rb_thread_call_without_gvl((fn),(a),(ubf),(b))
28
- #elif defined(HAVE_RB_THREAD_BLOCKING_REGION)
29
- // 1.9
30
- typedef VALUE (*my_blocking_fn_t)(void*);
31
- #define WITHOUT_GVL(fn,a,ubf,b) rb_thread_blocking_region((my_blocking_fn_t)(fn),(a),(ubf),(b))
32
- #endif
33
-
34
- static ID id_timeout;
35
- static VALUE eSyscall, eTimeout, eInternal;
36
- static int system_max_semaphore_count;
37
-
38
- static const int kIndexTickets = 0;
39
- static const int kIndexTicketMax = 1;
40
- static const int kIndexLock = 2;
41
-
42
- static const int kNumSemaphores = 3;
43
-
44
- typedef struct {
45
- int sem_id;
46
- struct timespec timeout;
47
- int error;
48
- char *name;
49
- } semian_resource_t;
50
-
51
- static key_t
1
+ #include "semian.h"
2
+
3
+ // Time to wait for timed ops to complete
4
+ #define INTERNAL_TIMEOUT 5 // seconds
5
+
6
+ key_t
52
7
  generate_key(const char *name)
53
8
  {
9
+ char semset_size_key[20];
10
+ char *uniq_id_str;
11
+
12
+ // It is necessary for the cardinatily of the semaphore set to be part of the key
13
+ // or else sem_get will complain that we have requested an incorrect number of sems
14
+ // for the desired key, and have changed the number of semaphores for a given key
15
+ sprintf(semset_size_key, "_NUM_SEMS_%d", SI_NUM_SEMAPHORES);
16
+ uniq_id_str = malloc(strlen(name)+strlen(semset_size_key)+1);
17
+ strcpy(uniq_id_str, name);
18
+ strcat(uniq_id_str, semset_size_key);
19
+
54
20
  union {
55
21
  unsigned char str[SHA_DIGEST_LENGTH];
56
22
  key_t key;
57
23
  } digest;
58
- SHA1((const unsigned char *) name, strlen(name), digest.str);
24
+ SHA1((const unsigned char *) uniq_id_str, strlen(uniq_id_str), digest.str);
25
+ free(uniq_id_str);
59
26
  /* TODO: compile-time assertion that sizeof(key_t) > SHA_DIGEST_LENGTH */
60
27
  return digest.key;
61
28
  }
62
29
 
63
- static void
64
- ms_to_timespec(long ms, struct timespec *ts)
65
- {
66
- ts->tv_sec = ms / 1000;
67
- ts->tv_nsec = (ms % 1000) * 1000000;
68
- }
69
-
70
- static void
30
+ void
71
31
  raise_semian_syscall_error(const char *syscall, int error_num)
72
32
  {
73
33
  rb_raise(eSyscall, "%s failed, errno: %d (%s)", syscall, error_num, strerror(error_num));
74
34
  }
75
35
 
76
- static void
77
- semian_resource_mark(void *ptr)
78
- {
79
- /* noop */
80
- }
81
-
82
- static void
83
- semian_resource_free(void *ptr)
84
- {
85
- semian_resource_t *res = (semian_resource_t *) ptr;
86
- if (res->name) {
87
- free(res->name);
88
- res->name = NULL;
89
- }
90
- xfree(res);
91
- }
92
-
93
- static size_t
94
- semian_resource_memsize(const void *ptr)
95
- {
96
- return sizeof(semian_resource_t);
97
- }
98
-
99
- static const rb_data_type_t
100
- semian_resource_type = {
101
- "semian_resource",
102
- {
103
- semian_resource_mark,
104
- semian_resource_free,
105
- semian_resource_memsize
106
- },
107
- NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY
108
- };
109
-
110
- static VALUE
111
- semian_resource_alloc(VALUE klass)
112
- {
113
- semian_resource_t *res;
114
- VALUE obj = TypedData_Make_Struct(klass, semian_resource_t, &semian_resource_type, res);
115
- return obj;
116
- }
117
-
118
- static void
119
- set_semaphore_permissions(int sem_id, int permissions)
36
+ void
37
+ set_semaphore_permissions(int sem_id, long permissions)
120
38
  {
121
39
  union semun sem_opts;
122
40
  struct semid_ds stat_buf;
@@ -135,14 +53,14 @@ static const int kInternalTimeout = 5; /* seconds */
135
53
  static int
136
54
  get_max_tickets(int sem_id)
137
55
  {
138
- int ret = semctl(sem_id, kIndexTicketMax, GETVAL);
56
+ int ret = semctl(sem_id, SI_SEM_CONFIGURED_TICKETS, GETVAL);
139
57
  if (ret == -1) {
140
58
  rb_raise(eInternal, "error getting max ticket count, errno: %d (%s)", errno, strerror(errno));
141
59
  }
142
60
  return ret;
143
61
  }
144
62
 
145
- static int
63
+ int
146
64
  perform_semop(int sem_id, short index, short op, short flags, struct timespec *ts)
147
65
  {
148
66
  struct sembuf buf = { 0 };
@@ -158,11 +76,6 @@ perform_semop(int sem_id, short index, short op, short flags, struct timespec *t
158
76
  }
159
77
  }
160
78
 
161
- typedef struct {
162
- int sem_id;
163
- int tickets;
164
- } update_ticket_count_t;
165
-
166
79
  static VALUE
167
80
  update_ticket_count(update_ticket_count_t *tc)
168
81
  {
@@ -173,11 +86,11 @@ update_ticket_count(update_ticket_count_t *tc)
173
86
  if (get_max_tickets(tc->sem_id) != tc->tickets) {
174
87
  delta = tc->tickets - get_max_tickets(tc->sem_id);
175
88
 
176
- if (perform_semop(tc->sem_id, kIndexTickets, delta, 0, &ts) == -1) {
89
+ if (perform_semop(tc->sem_id, SI_SEM_TICKETS, delta, 0, &ts) == -1) {
177
90
  rb_raise(eInternal, "error setting ticket count, errno: %d (%s)", errno, strerror(errno));
178
91
  }
179
92
 
180
- if (semctl(tc->sem_id, kIndexTicketMax, SETVAL, tc->tickets) == -1) {
93
+ if (semctl(tc->sem_id, SI_SEM_CONFIGURED_TICKETS, SETVAL, tc->tickets) == -1) {
181
94
  rb_raise(eInternal, "error updating max ticket count, errno: %d (%s)", errno, strerror(errno));
182
95
  }
183
96
  }
@@ -185,18 +98,18 @@ update_ticket_count(update_ticket_count_t *tc)
185
98
  return Qnil;
186
99
  }
187
100
 
188
- static void
101
+ void
189
102
  configure_tickets(int sem_id, int tickets, int should_initialize)
190
103
  {
191
104
  struct timespec ts = { 0 };
192
- unsigned short init_vals[kNumSemaphores];
105
+ unsigned short init_vals[SI_NUM_SEMAPHORES];
193
106
  struct timeval start_time, cur_time;
194
107
  update_ticket_count_t tc;
195
108
  int state;
196
109
 
197
110
  if (should_initialize) {
198
- init_vals[kIndexTickets] = init_vals[kIndexTicketMax] = tickets;
199
- init_vals[kIndexLock] = 1;
111
+ init_vals[SI_SEM_TICKETS] = init_vals[SI_SEM_CONFIGURED_TICKETS] = tickets;
112
+ init_vals[SI_SEM_LOCK] = 1;
200
113
  if (semctl(sem_id, 0, SETALL, init_vals) == -1) {
201
114
  raise_semian_syscall_error("semctl()", errno);
202
115
  }
@@ -222,7 +135,7 @@ configure_tickets(int sem_id, int tickets, int should_initialize)
222
135
  if (get_max_tickets(sem_id) != tickets) {
223
136
  ts.tv_sec = kInternalTimeout;
224
137
 
225
- if (perform_semop(sem_id, kIndexLock, -1, SEM_UNDO, &ts) == -1) {
138
+ if (perform_semop(sem_id, SI_SEM_LOCK, -1, SEM_UNDO, &ts) == -1) {
226
139
  raise_semian_syscall_error("error acquiring internal semaphore lock, semtimedop()", errno);
227
140
  }
228
141
 
@@ -230,7 +143,7 @@ configure_tickets(int sem_id, int tickets, int should_initialize)
230
143
  tc.tickets = tickets;
231
144
  rb_protect((VALUE (*)(VALUE)) update_ticket_count, (VALUE) &tc, &state);
232
145
 
233
- if (perform_semop(sem_id, kIndexLock, 1, SEM_UNDO, NULL) == -1) {
146
+ if (perform_semop(sem_id, SI_SEM_LOCK, 1, SEM_UNDO, NULL) == -1) {
234
147
  raise_semian_syscall_error("error releasing internal semaphore lock, semop()", errno);
235
148
  }
236
149
 
@@ -241,214 +154,40 @@ configure_tickets(int sem_id, int tickets, int should_initialize)
241
154
  }
242
155
  }
243
156
 
244
- static int
245
- create_semaphore(int key, int permissions, int *created)
157
+ int
158
+ create_semaphore(int key, long permissions, int *created)
246
159
  {
247
160
  int semid = 0;
248
161
  int flags = 0;
249
162
 
250
163
  *created = 0;
251
- flags = IPC_EXCL | IPC_CREAT | FIX2LONG(permissions);
164
+ flags = IPC_EXCL | IPC_CREAT | permissions;
252
165
 
253
- semid = semget(key, kNumSemaphores, flags);
166
+ semid = semget(key, SI_NUM_SEMAPHORES, flags);
254
167
  if (semid >= 0) {
255
168
  *created = 1;
256
169
  } else if (semid == -1 && errno == EEXIST) {
257
170
  flags &= ~IPC_EXCL;
258
- semid = semget(key, kNumSemaphores, flags);
171
+ semid = semget(key, SI_NUM_SEMAPHORES, flags);
259
172
  }
260
173
  return semid;
261
174
  }
262
175
 
263
- static int
264
- get_semaphore(int key)
265
- {
266
- return semget(key, kNumSemaphores, 0);
267
- }
268
-
269
- /*
270
- * call-seq:
271
- * Semian::Resource.new(id, tickets, permissions, default_timeout) -> resource
272
- *
273
- * Creates a new Resource. Do not create resources directly. Use Semian.register.
274
- */
275
- static VALUE
276
- semian_resource_initialize(VALUE self, VALUE id, VALUE tickets, VALUE permissions, VALUE default_timeout)
277
- {
278
- key_t key;
279
- int created = 0;
280
- semian_resource_t *res = NULL;
281
- const char *id_str = NULL;
282
-
283
- if (TYPE(id) != T_SYMBOL && TYPE(id) != T_STRING) {
284
- rb_raise(rb_eTypeError, "id must be a symbol or string");
285
- }
286
- if (TYPE(tickets) == T_FLOAT) {
287
- rb_warn("semian ticket value %f is a float, converting to fixnum", RFLOAT_VALUE(tickets));
288
- tickets = INT2FIX((int) RFLOAT_VALUE(tickets));
289
- }
290
- Check_Type(tickets, T_FIXNUM);
291
- Check_Type(permissions, T_FIXNUM);
292
- if (TYPE(default_timeout) != T_FIXNUM && TYPE(default_timeout) != T_FLOAT) {
293
- rb_raise(rb_eTypeError, "expected numeric type for default_timeout");
294
- }
295
- if (FIX2LONG(tickets) < 0 || FIX2LONG(tickets) > system_max_semaphore_count) {
296
- rb_raise(rb_eArgError, "ticket count must be a non-negative value and less than %d", system_max_semaphore_count);
297
- }
298
- if (NUM2DBL(default_timeout) < 0) {
299
- rb_raise(rb_eArgError, "default timeout must be non-negative value");
300
- }
301
-
302
- if (TYPE(id) == T_SYMBOL) {
303
- id_str = rb_id2name(rb_to_id(id));
304
- } else if (TYPE(id) == T_STRING) {
305
- id_str = RSTRING_PTR(id);
306
- }
307
- TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
308
- key = generate_key(id_str);
309
- ms_to_timespec(NUM2DBL(default_timeout) * 1000, &res->timeout);
310
- res->name = strdup(id_str);
311
-
312
- res->sem_id = FIX2LONG(tickets) == 0 ? get_semaphore(key) : create_semaphore(key, permissions, &created);
313
- if (res->sem_id == -1) {
314
- raise_semian_syscall_error("semget()", errno);
315
- }
316
-
317
- configure_tickets(res->sem_id, FIX2LONG(tickets), created);
318
-
319
- set_semaphore_permissions(res->sem_id, FIX2LONG(permissions));
320
-
321
- return self;
322
- }
323
-
324
- static VALUE
325
- cleanup_semian_resource_acquire(VALUE self)
326
- {
327
- semian_resource_t *res = NULL;
328
- TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
329
- if (perform_semop(res->sem_id, kIndexTickets, 1, SEM_UNDO, NULL) == -1) {
330
- res->error = errno;
331
- }
332
- return Qnil;
333
- }
334
-
335
- static void *
176
+ void *
336
177
  acquire_semaphore_without_gvl(void *p)
337
178
  {
338
179
  semian_resource_t *res = (semian_resource_t *) p;
339
180
  res->error = 0;
340
- if (perform_semop(res->sem_id, kIndexTickets, -1, SEM_UNDO, &res->timeout) == -1) {
181
+ if (perform_semop(res->sem_id, SI_SEM_TICKETS, -1, SEM_UNDO, &res->timeout) == -1) {
341
182
  res->error = errno;
342
183
  }
343
184
  return NULL;
344
185
  }
345
186
 
346
- /*
347
- * call-seq:
348
- * resource.acquire(timeout: default_timeout) { ... } -> result of the block
349
- *
350
- * Acquires a resource. The call will block for <code>timeout</code> seconds if a ticket
351
- * is not available. If no ticket is available within the timeout period, Semian::TimeoutError
352
- * will be raised.
353
- *
354
- * If no timeout argument is provided, the default timeout passed to Semian.register will be used.
355
- *
356
- */
357
- static VALUE
358
- semian_resource_acquire(int argc, VALUE *argv, VALUE self)
359
- {
360
- semian_resource_t *self_res = NULL;
361
- semian_resource_t res = { 0 };
362
-
363
- if (!rb_block_given_p()) {
364
- rb_raise(rb_eArgError, "acquire requires a block");
365
- }
366
-
367
- TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, self_res);
368
- res = *self_res;
369
-
370
- /* allow the default timeout to be overridden by a "timeout" param */
371
- if (argc == 1 && TYPE(argv[0]) == T_HASH) {
372
- VALUE timeout = rb_hash_aref(argv[0], ID2SYM(id_timeout));
373
- if (TYPE(timeout) != T_NIL) {
374
- if (TYPE(timeout) != T_FLOAT && TYPE(timeout) != T_FIXNUM) {
375
- rb_raise(rb_eArgError, "timeout parameter must be numeric");
376
- }
377
- ms_to_timespec(NUM2DBL(timeout) * 1000, &res.timeout);
378
- }
379
- } else if (argc > 0) {
380
- rb_raise(rb_eArgError, "invalid arguments");
381
- }
382
-
383
- /* release the GVL to acquire the semaphore */
384
- WITHOUT_GVL(acquire_semaphore_without_gvl, &res, RUBY_UBF_IO, NULL);
385
- if (res.error != 0) {
386
- if (res.error == EAGAIN) {
387
- rb_raise(eTimeout, "timed out waiting for resource '%s'", res.name);
388
- } else {
389
- raise_semian_syscall_error("semop()", res.error);
390
- }
391
- }
392
-
393
- return rb_ensure(rb_yield, self, cleanup_semian_resource_acquire, self);
394
- }
395
-
396
- /*
397
- * call-seq:
398
- * resource.destroy() -> true
399
- *
400
- * Destroys a resource. This method will destroy the underlying SysV semaphore.
401
- * If there is any code in other threads or processes blocking or using the resource
402
- * they will likely raise.
403
- *
404
- * Use this method very carefully.
405
- */
406
- static VALUE
407
- semian_resource_destroy(VALUE self)
408
- {
409
- semian_resource_t *res = NULL;
410
-
411
- TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
412
- if (semctl(res->sem_id, 0, IPC_RMID) == -1) {
413
- raise_semian_syscall_error("semctl()", errno);
414
- }
415
-
416
- return Qtrue;
417
- }
418
-
419
- /*
420
- * call-seq:
421
- * resource.count -> count
422
- *
423
- * Returns the current ticket count for a resource.
424
- */
425
- static VALUE
426
- semian_resource_count(VALUE self)
427
- {
428
- int ret;
429
- semian_resource_t *res = NULL;
430
-
431
- TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
432
- ret = semctl(res->sem_id, 0, GETVAL);
433
- if (ret == -1) {
434
- raise_semian_syscall_error("semctl()", errno);
435
- }
436
-
437
- return LONG2FIX(ret);
438
- }
439
-
440
- /*
441
- * call-seq:
442
- * resource.semid -> id
443
- *
444
- * Returns the SysV semaphore id of a resource.
445
- */
446
- static VALUE
447
- semian_resource_id(VALUE self)
187
+ int
188
+ get_semaphore(int key)
448
189
  {
449
- semian_resource_t *res = NULL;
450
- TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
451
- return LONG2FIX(res->sem_id);
190
+ return semget(key, SI_NUM_SEMAPHORES, 0);
452
191
  }
453
192
 
454
193
  void Init_semian()