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 +4 -4
- data/ext/semian/extconf.rb +1 -1
- data/ext/semian/resource.c +134 -17
- data/ext/semian/resource.h +56 -3
- data/ext/semian/semian.c +6 -1
- data/ext/semian/sysv_semaphores.c +148 -43
- data/ext/semian/sysv_semaphores.h +49 -5
- data/ext/semian/tickets.c +64 -54
- data/ext/semian/tickets.h +2 -6
- data/ext/semian/types.h +4 -9
- data/lib/semian.rb +111 -17
- data/lib/semian/adapter.rb +5 -0
- data/lib/semian/circuit_breaker.rb +9 -7
- data/lib/semian/mysql2.rb +2 -0
- data/lib/semian/protected_resource.rb +37 -14
- data/lib/semian/redis.rb +8 -6
- data/lib/semian/resource.rb +27 -3
- data/lib/semian/simple_integer.rb +15 -0
- data/lib/semian/simple_sliding_window.rb +35 -10
- data/lib/semian/simple_state.rb +7 -0
- data/lib/semian/unprotected_resource.rb +12 -0
- data/lib/semian/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '084b4a21e0290cba65079b0490496da902f49b4c'
|
4
|
+
data.tar.gz: 07b2fda101604725efad2e5b9c6d478af5792b6b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c351bf24c90d6ccd7b4b15e055451543d218608f663dc229b99fd533a06f731606ecabf93a101f206938b7f39113d447dbb35b234a5ba6b31d603154b1e3c06
|
7
|
+
data.tar.gz: cc23e70d1c25cb10ed075a35b1f184caf07d5c5939e98ad5412cfd4da5a36bb5364811e46e020fbe2a56d93036cf3c0606e14d975519aca3bc391d5d6195a43f
|
data/ext/semian/extconf.rb
CHANGED
data/ext/semian/resource.c
CHANGED
@@ -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
|
-
|
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
|
-
|
190
|
+
semian_resource_key(VALUE self)
|
101
191
|
{
|
102
|
-
|
103
|
-
|
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
|
-
//
|
124
|
-
|
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
|
{
|
data/ext/semian/resource.h
CHANGED
@@ -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);
|
data/ext/semian/semian.c
CHANGED
@@ -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,
|
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
|
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
|
-
|
10
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
99
|
+
get_sem_val(int sem_id, int sem_index)
|
86
100
|
{
|
87
|
-
int ret = semctl(sem_id,
|
101
|
+
int ret = semctl(sem_id, sem_index, GETVAL);
|
88
102
|
if (ret == -1) {
|
89
|
-
rb_raise(eInternal, "error getting
|
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
|
+
}
|