semian 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
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()