semian 0.6.2 → 0.7.0

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: 5952a089360c3f99f037dd49e4bfdec80e010d3b
4
- data.tar.gz: e1862220b675cba619fdddbdde1cccce6c24cd87
3
+ metadata.gz: '084b4a21e0290cba65079b0490496da902f49b4c'
4
+ data.tar.gz: 07b2fda101604725efad2e5b9c6d478af5792b6b
5
5
  SHA512:
6
- metadata.gz: 4d6024b8df7dd42b1a6eb517cb8619c8138f20074529a0ffb1f7181dd5d81f9cdeeb9e2b7752ca7d234f400de2880a1d84ff592affc8f92806e2ce15f6d72f28
7
- data.tar.gz: c7f64858fb8fc83a51b6f43a729cdc2dab9f24633c2a9016b50f3be945921de2893f9bea0eec4ae8419577eb715f108e70bae45ab44134be96b7161e75b6743b
6
+ metadata.gz: 3c351bf24c90d6ccd7b4b15e055451543d218608f663dc229b99fd533a06f731606ecabf93a101f206938b7f39113d447dbb35b234a5ba6b31d603154b1e3c06
7
+ data.tar.gz: cc23e70d1c25cb10ed075a35b1f184caf07d5c5939e98ad5412cfd4da5a36bb5364811e46e020fbe2a56d93036cf3c0606e14d975519aca3bc391d5d6195a43f
@@ -25,7 +25,7 @@ have_func 'rb_thread_call_without_gvl'
25
25
 
26
26
  $CFLAGS = "-D_GNU_SOURCE -Werror -Wall "
27
27
  if ENV.key?('DEBUG')
28
- $CFLAGS << "-O0 -g"
28
+ $CFLAGS << "-O0 -g -DDEBUG"
29
29
  else
30
30
  $CFLAGS << "-O3"
31
31
  end
@@ -3,6 +3,12 @@
3
3
  static VALUE
4
4
  cleanup_semian_resource_acquire(VALUE self);
5
5
 
6
+ static void
7
+ check_tickets_xor_quota_arg(VALUE tickets, VALUE quota);
8
+
9
+ static double
10
+ check_quota_arg(VALUE quota);
11
+
6
12
  static int
7
13
  check_tickets_arg(VALUE tickets);
8
14
 
@@ -48,7 +54,7 @@ semian_resource_acquire(int argc, VALUE *argv, VALUE self)
48
54
  }
49
55
 
50
56
  /* release the GVL to acquire the semaphore */
51
- WITHOUT_GVL(acquire_semaphore_without_gvl, &res, RUBY_UBF_IO, NULL); // FIXME - replace with wrapped version
57
+ acquire_semaphore_without_gvl(&res);
52
58
  if (res.error != 0) {
53
59
  if (res.error == EAGAIN) {
54
60
  rb_raise(eTimeout, "timed out waiting for resource '%s'", res.name);
@@ -63,9 +69,20 @@ semian_resource_acquire(int argc, VALUE *argv, VALUE self)
63
69
  VALUE
64
70
  semian_resource_destroy(VALUE self)
65
71
  {
72
+ struct timespec ts = { 0 };
73
+ ts.tv_sec = INTERNAL_TIMEOUT;
74
+
66
75
  semian_resource_t *res = NULL;
67
76
 
68
77
  TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
78
+
79
+ // Prevent a race to deletion
80
+ if (perform_semop(res->sem_id, SI_SEM_LOCK, -1, 0, &ts) == -1) {
81
+ if (errno == EINVAL || errno == EIDRM) {
82
+ return Qtrue;
83
+ }
84
+ }
85
+
69
86
  if (semctl(res->sem_id, SI_NUM_SEMAPHORES, IPC_RMID) == -1) {
70
87
  raise_semian_syscall_error("semctl()", errno);
71
88
  }
@@ -73,6 +90,49 @@ semian_resource_destroy(VALUE self)
73
90
  return Qtrue;
74
91
  }
75
92
 
93
+ VALUE
94
+ semian_resource_reset_workers(VALUE self)
95
+ {
96
+ int ret;
97
+ semian_resource_t *res = NULL;
98
+
99
+ TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
100
+
101
+ sem_meta_lock(res->sem_id);
102
+ // This SETVAL will purge the SEM_UNDO table
103
+ ret = semctl(res->sem_id, SI_SEM_REGISTERED_WORKERS, SETVAL, 0);
104
+ sem_meta_unlock(res->sem_id);
105
+
106
+ if (ret == -1) {
107
+ raise_semian_syscall_error("semctl()", errno);
108
+ }
109
+
110
+ return Qtrue;
111
+ }
112
+
113
+ VALUE
114
+ semian_resource_unregister_worker(VALUE self)
115
+ {
116
+ int ret;
117
+ semian_resource_t *res = NULL;
118
+
119
+ TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
120
+
121
+ sem_meta_lock(res->sem_id);
122
+ ret = perform_semop(res->sem_id, SI_SEM_REGISTERED_WORKERS, -1, IPC_NOWAIT | SEM_UNDO, NULL);
123
+ sem_meta_unlock(res->sem_id);
124
+
125
+ if ( ret == -1) {
126
+ // Allow EAGAIN with IPC_NOWAIT, as this signals that all workers were unregistered
127
+ // Otherwise, we might block forever or throw an unintended timeout
128
+ if (errno != EAGAIN) {
129
+ rb_raise(eInternal, "error decreasing registered workers, errno: %d (%s)", errno, strerror(errno));
130
+ }
131
+ }
132
+
133
+ return Qtrue;
134
+ }
135
+
76
136
  VALUE
77
137
  semian_resource_count(VALUE self)
78
138
  {
@@ -88,6 +148,36 @@ semian_resource_count(VALUE self)
88
148
  return LONG2FIX(ret);
89
149
  }
90
150
 
151
+ VALUE
152
+ semian_resource_tickets(VALUE self)
153
+ {
154
+ int ret;
155
+ semian_resource_t *res = NULL;
156
+
157
+ TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
158
+ ret = semctl(res->sem_id, SI_SEM_CONFIGURED_TICKETS, GETVAL);
159
+ if (ret == -1) {
160
+ raise_semian_syscall_error("semctl()", errno);
161
+ }
162
+
163
+ return LONG2FIX(ret);
164
+ }
165
+
166
+ VALUE
167
+ semian_resource_workers(VALUE self)
168
+ {
169
+ int ret;
170
+ semian_resource_t *res = NULL;
171
+
172
+ TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
173
+ ret = semctl(res->sem_id, SI_SEM_REGISTERED_WORKERS, GETVAL);
174
+ if (ret == -1) {
175
+ raise_semian_syscall_error("semctl()", errno);
176
+ }
177
+
178
+ return LONG2FIX(ret);
179
+ }
180
+
91
181
  VALUE
92
182
  semian_resource_id(VALUE self)
93
183
  {
@@ -97,17 +187,26 @@ semian_resource_id(VALUE self)
97
187
  }
98
188
 
99
189
  VALUE
100
- semian_resource_initialize(VALUE self, VALUE id, VALUE tickets, VALUE permissions, VALUE default_timeout)
190
+ semian_resource_key(VALUE self)
101
191
  {
102
- key_t key;
103
- int c_permissions;
192
+ semian_resource_t *res = NULL;
193
+ TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
194
+ return rb_str_new_cstr(res->strkey);
195
+ }
196
+
197
+ VALUE
198
+ semian_resource_initialize(VALUE self, VALUE id, VALUE tickets, VALUE quota, VALUE permissions, VALUE default_timeout)
199
+ {
200
+ long c_permissions;
104
201
  double c_timeout;
202
+ double c_quota;
105
203
  int c_tickets;
106
- int created = 0;
107
204
  semian_resource_t *res = NULL;
108
205
  const char *c_id_str = NULL;
109
206
 
110
207
  // Check and cast arguments
208
+ check_tickets_xor_quota_arg(tickets, quota);
209
+ c_quota = check_quota_arg(quota);
111
210
  c_tickets = check_tickets_arg(tickets);
112
211
  c_permissions = check_permissions_arg(permissions);
113
212
  c_id_str = check_id_arg(id);
@@ -119,19 +218,10 @@ semian_resource_initialize(VALUE self, VALUE id, VALUE tickets, VALUE permission
119
218
  // Populate struct fields
120
219
  ms_to_timespec(c_timeout * 1000, &res->timeout);
121
220
  res->name = strdup(c_id_str);
221
+ res->quota = c_quota;
122
222
 
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);
223
+ // Initialize the semaphore set
224
+ initialize_semaphore_set(res, c_id_str, c_permissions, c_tickets, c_quota);
135
225
 
136
226
  return self;
137
227
  }
@@ -162,6 +252,33 @@ check_permissions_arg(VALUE permissions)
162
252
  return FIX2LONG(permissions);
163
253
  }
164
254
 
255
+ static void
256
+ check_tickets_xor_quota_arg(VALUE tickets, VALUE quota)
257
+ {
258
+ if ((TYPE(tickets) == T_NIL && TYPE(quota) == T_NIL) ||(TYPE(tickets) != T_NIL && TYPE(quota) != T_NIL)){
259
+ rb_raise(rb_eArgError, "Must pass exactly one of ticket or quota");
260
+ }
261
+ }
262
+
263
+ static double
264
+ check_quota_arg(VALUE quota)
265
+ {
266
+ double c_quota;
267
+
268
+ if (TYPE(quota) != T_NIL) {
269
+ if (TYPE(quota) != T_FIXNUM && TYPE(quota) != T_FLOAT) {
270
+ rb_raise(rb_eTypeError, "expected decimal type for quota");
271
+ }
272
+ if (NUM2DBL(quota) <= 0 || NUM2DBL(quota) > 1) {
273
+ rb_raise(rb_eArgError, "quota must be a decimal between 0 and 1");
274
+ }
275
+ c_quota = NUM2DBL(quota);
276
+ } else {
277
+ c_quota = -1.0f;
278
+ }
279
+ return c_quota;
280
+ }
281
+
165
282
  static int
166
283
  check_tickets_arg(VALUE tickets)
167
284
  {
@@ -7,7 +7,6 @@ Functions here are associated with rubyland operations.
7
7
  #define SEMIAN_RESOURCE_H
8
8
 
9
9
  #include "types.h"
10
- #include "tickets.h"
11
10
  #include "sysv_semaphores.h"
12
11
 
13
12
  // Ruby variables
@@ -16,12 +15,12 @@ int system_max_semaphore_count;
16
15
 
17
16
  /*
18
17
  * call-seq:
19
- * Semian::Resource.new(id, tickets, permissions, default_timeout) -> resource
18
+ * Semian::Resource.new(id, tickets, quota, permissions, default_timeout) -> resource
20
19
  *
21
20
  * Creates a new Resource. Do not create resources directly. Use Semian.register.
22
21
  */
23
22
  VALUE
24
- semian_resource_initialize(VALUE self, VALUE id, VALUE tickets, VALUE permissions, VALUE default_timeout);
23
+ semian_resource_initialize(VALUE self, VALUE id, VALUE tickets, VALUE quota, VALUE permissions, VALUE default_timeout);
25
24
 
26
25
  /*
27
26
  * call-seq:
@@ -50,6 +49,18 @@ semian_resource_acquire(int argc, VALUE *argv, VALUE self);
50
49
  VALUE
51
50
  semian_resource_destroy(VALUE self);
52
51
 
52
+ /*
53
+ * call-seq:
54
+ * resource.reset_registered_workers!() -> true
55
+ *
56
+ * Unregisters all registered workers for a resource, forcefully setting the worker count back to 0.
57
+ * This will purge the SEM_UNDO table.
58
+ *
59
+ * Use this method very carefully.
60
+ */
61
+ VALUE
62
+ semian_resource_reset_workers(VALUE self);
63
+
53
64
  /*
54
65
  * call-seq:
55
66
  * resource.count -> count
@@ -59,15 +70,57 @@ semian_resource_destroy(VALUE self);
59
70
  VALUE
60
71
  semian_resource_count(VALUE self);
61
72
 
73
+ /*
74
+ * call-seq:
75
+ * resource.tickets -> count
76
+ *
77
+ * Returns the configured number of tickets for a resource.
78
+ */
79
+ VALUE
80
+ semian_resource_tickets(VALUE self);
81
+
82
+ /*
83
+ * call-seq:
84
+ * resource.registered_workers -> count
85
+ *
86
+ * Returns the number of workers (processes) registered to use the resource
87
+ */
88
+ VALUE
89
+ semian_resource_workers(VALUE self);
90
+
62
91
  /*
63
92
  * call-seq:
64
93
  * resource.semid -> id
65
94
  *
66
95
  * Returns the SysV semaphore id of a resource.
96
+ * Note: This value varies from system to system, and between
97
+ * instances of semian.
67
98
  */
68
99
  VALUE
69
100
  semian_resource_id(VALUE self);
70
101
 
102
+ /*
103
+ * call-seq:
104
+ * resource.key -> id
105
+ *
106
+ * Returns the hex string representation of SysV semaphore key of a resource.
107
+ * Note: This is a unique identifier that is portable across system
108
+ * and instances of semian.
109
+ */
110
+ VALUE
111
+ semian_resource_key(VALUE self);
112
+
113
+ /*
114
+ * call-seq:
115
+ * resource.unregister_worker() -> true
116
+ *
117
+ * Unregisters a worker, which will affect quota calculations.
118
+ *
119
+ * Be careful to call this only once per process.
120
+ */
121
+ VALUE
122
+ semian_resource_unregister_worker(VALUE self);
123
+
71
124
  // Allocate a semian_resource_type struct for ruby memory management
72
125
  VALUE
73
126
  semian_resource_alloc(VALUE klass);
@@ -42,11 +42,16 @@ void Init_semian()
42
42
  eInternal = rb_const_get(cSemian, rb_intern("InternalError"));
43
43
 
44
44
  rb_define_alloc_func(cResource, semian_resource_alloc);
45
- rb_define_method(cResource, "initialize_semaphore", semian_resource_initialize, 4);
45
+ rb_define_method(cResource, "initialize_semaphore", semian_resource_initialize, 5);
46
46
  rb_define_method(cResource, "acquire", semian_resource_acquire, -1);
47
47
  rb_define_method(cResource, "count", semian_resource_count, 0);
48
48
  rb_define_method(cResource, "semid", semian_resource_id, 0);
49
+ rb_define_method(cResource, "key", semian_resource_key, 0);
50
+ rb_define_method(cResource, "tickets", semian_resource_tickets, 0);
51
+ rb_define_method(cResource, "registered_workers", semian_resource_workers, 0);
49
52
  rb_define_method(cResource, "destroy", semian_resource_destroy, 0);
53
+ rb_define_method(cResource, "reset_registered_workers!", semian_resource_reset_workers, 0);
54
+ rb_define_method(cResource, "unregister_worker", semian_resource_unregister_worker, 0);
50
55
 
51
56
  id_timeout = rb_intern("timeout");
52
57
 
@@ -1,4 +1,21 @@
1
- #include <sysv_semaphores.h>
1
+ #include "sysv_semaphores.h"
2
+
3
+ static key_t
4
+ generate_key(const char *name);
5
+
6
+ static void *
7
+ acquire_semaphore(void *p);
8
+
9
+ static int
10
+ wait_for_new_semaphore_set(key_t key, long permissions);
11
+
12
+ static void
13
+ initialize_new_semaphore_values(int sem_id, long permissions);
14
+
15
+ // Generate string rep for sem indices for debugging puproses
16
+ static const char *SEMINDEX_STRING[] = {
17
+ FOREACH_SEMINDEX(GENERATE_STRING)
18
+ };
2
19
 
3
20
  void
4
21
  raise_semian_syscall_error(const char *syscall, int error_num)
@@ -6,28 +23,45 @@ raise_semian_syscall_error(const char *syscall, int error_num)
6
23
  rb_raise(eSyscall, "%s failed, errno: %d (%s)", syscall, error_num, strerror(error_num));
7
24
  }
8
25
 
9
- key_t
10
- generate_key(const char *name)
26
+ void
27
+ initialize_semaphore_set(semian_resource_t* res, const char* id_str, long permissions, int tickets, double quota)
11
28
  {
12
- char semset_size_key[20];
13
- char *uniq_id_str;
14
29
 
15
- // It is necessary for the cardinatily of the semaphore set to be part of the key
16
- // or else sem_get will complain that we have requested an incorrect number of sems
17
- // for the desired key, and have changed the number of semaphores for a given key
18
- sprintf(semset_size_key, "_NUM_SEMS_%d", SI_NUM_SEMAPHORES);
19
- uniq_id_str = malloc(strlen(name)+strlen(semset_size_key)+1);
20
- strcpy(uniq_id_str, name);
21
- strcat(uniq_id_str, semset_size_key);
30
+ res->key = generate_key(id_str);
31
+ res->strkey = (char*) malloc((2 /*for 0x*/+ sizeof(uint64_t) /*actual key*/+ 1 /*null*/) * sizeof(char));
32
+ sprintf(res->strkey, "0x%08x", (unsigned int) res->key);
33
+ res->sem_id = semget(res->key, SI_NUM_SEMAPHORES, IPC_CREAT | IPC_EXCL | permissions);
34
+
35
+ /*
36
+ This approach is based on http://man7.org/tlpi/code/online/dist/svsem/svsem_good_init.c.html
37
+ which avoids race conditions when initializing semaphore sets.
38
+ */
39
+ if (res->sem_id != -1) {
40
+ // Happy path - we are the first worker, initialize the semaphore set.
41
+ initialize_new_semaphore_values(res->sem_id, permissions);
42
+ } else {
43
+ // Something went wrong
44
+ if (errno != EEXIST) {
45
+ raise_semian_syscall_error("semget() failed to initialize semaphore values", errno);
46
+ } else {
47
+ // The semaphore set already exists, ensure it is initialized
48
+ res->sem_id = wait_for_new_semaphore_set(res->key, permissions);
49
+ }
50
+ }
22
51
 
23
- union {
24
- unsigned char str[SHA_DIGEST_LENGTH];
25
- key_t key;
26
- } digest;
27
- SHA1((const unsigned char *) uniq_id_str, strlen(uniq_id_str), digest.str);
28
- free(uniq_id_str);
29
- /* TODO: compile-time assertion that sizeof(key_t) > SHA_DIGEST_LENGTH */
30
- return digest.key;
52
+ set_semaphore_permissions(res->sem_id, permissions);
53
+
54
+ /*
55
+ Ensure that a worker for this process is registered.
56
+ Note that from ruby we ensure that at most one worker may be registered per process.
57
+ */
58
+ if (perform_semop(res->sem_id, SI_SEM_REGISTERED_WORKERS, 1, SEM_UNDO, NULL) == -1) {
59
+ rb_raise(eInternal, "error incrementing registered workers, errno: %d (%s)", errno, strerror(errno));
60
+ }
61
+
62
+ sem_meta_lock(res->sem_id); // Sets otime for the first time by acquiring the sem lock
63
+ configure_tickets(res->sem_id, tickets, quota);
64
+ sem_meta_unlock(res->sem_id);
31
65
  }
32
66
 
33
67
  void
@@ -45,26 +79,6 @@ set_semaphore_permissions(int sem_id, long permissions)
45
79
  }
46
80
  }
47
81
 
48
- int
49
- create_semaphore(int key, long permissions, int *created)
50
- {
51
- int semid = 0;
52
- int flags = 0;
53
-
54
- *created = 0;
55
- flags = IPC_EXCL | IPC_CREAT | permissions;
56
-
57
- semid = semget(key, SI_NUM_SEMAPHORES, flags);
58
- if (semid >= 0) {
59
- *created = 1;
60
- } else if (semid == -1 && errno == EEXIST) {
61
- flags &= ~IPC_EXCL;
62
- semid = semget(key, SI_NUM_SEMAPHORES, flags);
63
- }
64
- return semid;
65
- }
66
-
67
-
68
82
  int
69
83
  perform_semop(int sem_id, short index, short op, short flags, struct timespec *ts)
70
84
  {
@@ -82,11 +96,11 @@ perform_semop(int sem_id, short index, short op, short flags, struct timespec *t
82
96
  }
83
97
 
84
98
  int
85
- get_max_tickets(int sem_id)
99
+ get_sem_val(int sem_id, int sem_index)
86
100
  {
87
- int ret = semctl(sem_id, SI_SEM_CONFIGURED_TICKETS, GETVAL);
101
+ int ret = semctl(sem_id, sem_index, GETVAL);
88
102
  if (ret == -1) {
89
- rb_raise(eInternal, "error getting max ticket count, errno: %d (%s)", errno, strerror(errno));
103
+ rb_raise(eInternal, "error getting value of %s for sem %d, errno: %d (%s)", SEMINDEX_STRING[sem_index], sem_id, errno, strerror(errno));
90
104
  }
91
105
  return ret;
92
106
  }
@@ -118,11 +132,102 @@ get_semaphore(int key)
118
132
 
119
133
  void *
120
134
  acquire_semaphore_without_gvl(void *p)
135
+ {
136
+ WITHOUT_GVL(acquire_semaphore, p, RUBY_UBF_IO, NULL);
137
+ return NULL;
138
+ }
139
+
140
+ static void *
141
+ acquire_semaphore(void *p)
121
142
  {
122
143
  semian_resource_t *res = (semian_resource_t *) p;
123
144
  res->error = 0;
145
+ #ifdef DEBUG
146
+ print_sem_vals(res->sem_id);
147
+ #endif
124
148
  if (perform_semop(res->sem_id, SI_SEM_TICKETS, -1, SEM_UNDO, &res->timeout) == -1) {
125
149
  res->error = errno;
126
150
  }
127
151
  return NULL;
128
152
  }
153
+
154
+ static key_t
155
+ generate_key(const char *name)
156
+ {
157
+ char semset_size_key[20];
158
+ char *uniq_id_str;
159
+
160
+ // It is necessary for the cardinatily of the semaphore set to be part of the key
161
+ // or else sem_get will complain that we have requested an incorrect number of sems
162
+ // for the desired key, and have changed the number of semaphores for a given key
163
+ sprintf(semset_size_key, "_NUM_SEMS_%d", SI_NUM_SEMAPHORES);
164
+ uniq_id_str = malloc(strlen(name)+strlen(semset_size_key)+1);
165
+ strcpy(uniq_id_str, name);
166
+ strcat(uniq_id_str, semset_size_key);
167
+
168
+ union {
169
+ unsigned char str[SHA_DIGEST_LENGTH];
170
+ key_t key;
171
+ } digest;
172
+ SHA1((const unsigned char *) uniq_id_str, strlen(uniq_id_str), digest.str);
173
+ free(uniq_id_str);
174
+ /* TODO: compile-time assertion that sizeof(key_t) > SHA_DIGEST_LENGTH */
175
+ return digest.key;
176
+ }
177
+
178
+
179
+ static void
180
+ initialize_new_semaphore_values(int sem_id, long permissions)
181
+ {
182
+ unsigned short init_vals[SI_NUM_SEMAPHORES];
183
+
184
+ init_vals[SI_SEM_TICKETS] = init_vals[SI_SEM_CONFIGURED_TICKETS] = 0;
185
+ init_vals[SI_SEM_REGISTERED_WORKERS] = 0;
186
+ init_vals[SI_SEM_LOCK] = 1;
187
+
188
+ if (semctl(sem_id, 0, SETALL, init_vals) == -1) {
189
+ raise_semian_syscall_error("semctl()", errno);
190
+ }
191
+ #ifdef DEBUG
192
+ print_sem_vals(sem_id);
193
+ #endif
194
+ }
195
+
196
+ static int
197
+ wait_for_new_semaphore_set(key_t key, long permissions)
198
+ {
199
+ int i;
200
+ int sem_id = -1;
201
+ union semun sem_opts;
202
+ struct semid_ds sem_ds;
203
+
204
+ sem_opts.buf = &sem_ds;
205
+ sem_id = semget(key, 1, permissions);
206
+
207
+ if (sem_id == -1){
208
+ raise_semian_syscall_error("semget()", errno);
209
+ }
210
+
211
+ for (i = 0; i < ((INTERNAL_TIMEOUT * MICROSECONDS_IN_SECOND) / INIT_WAIT); i++) {
212
+
213
+ if (semctl(sem_id, 0, IPC_STAT, sem_opts) == -1) {
214
+ raise_semian_syscall_error("semget()", errno);
215
+ }
216
+
217
+ // If a semop has been performed by someone else, the values must be initialized
218
+ if (sem_ds.sem_otime != 0) {
219
+ break;
220
+ }
221
+
222
+ #ifdef DEBUG
223
+ printf("Waiting for another process to initialize semaphore values, checked: %d times\n", i);
224
+ #endif
225
+ usleep(INIT_WAIT);
226
+ }
227
+
228
+ if (sem_ds.sem_otime == 0) {
229
+ rb_raise(eTimeout, "error: timeout waiting for semaphore values to initialize after %d checks", INTERNAL_TIMEOUT);
230
+ }
231
+
232
+ return sem_id;
233
+ }