semian 0.6.2 → 0.7.0
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 +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
|
+
}
|