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 +4 -4
- data/ext/semian/resource.c +257 -0
- data/ext/semian/resource.h +75 -0
- data/ext/semian/semian.c +42 -303
- data/ext/semian/semian.h +67 -0
- data/ext/semian/types.h +45 -0
- data/lib/semian/version.rb +1 -1
- metadata +7 -29
- data/.gitignore +0 -8
- data/.rubocop.yml +0 -113
- data/.ruby-version +0 -1
- data/.travis.yml +0 -15
- data/CHANGELOG.md +0 -11
- data/Gemfile +0 -10
- data/LICENSE.md +0 -21
- data/README.md +0 -576
- data/Rakefile +0 -56
- data/repodb.yml +0 -1
- data/scripts/install_toxiproxy.sh +0 -25
- data/semian.gemspec +0 -29
- data/test/circuit_breaker_test.rb +0 -133
- data/test/fixtures/binary.sql +0 -1
- data/test/helpers/background_helper.rb +0 -25
- data/test/instrumentation_test.rb +0 -61
- data/test/mysql2_test.rb +0 -296
- data/test/net_http_test.rb +0 -515
- data/test/redis_test.rb +0 -237
- data/test/resource_test.rb +0 -322
- data/test/semian_test.rb +0 -32
- data/test/simple_integer_test.rb +0 -49
- data/test/simple_sliding_window_test.rb +0 -65
- data/test/simple_state_test.rb +0 -45
- data/test/test_helper.rb +0 -33
- data/test/unprotected_resource_test.rb +0 -60
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9844c9c807927ab334939c439c4de127033f5189
|
4
|
+
data.tar.gz: 5bb4675179a75c585cd05206dbdbf2777002f403
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/ext/semian/semian.c
CHANGED
@@ -1,122 +1,40 @@
|
|
1
|
-
#include
|
2
|
-
|
3
|
-
|
4
|
-
#
|
5
|
-
|
6
|
-
|
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 *)
|
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
|
-
|
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
|
-
|
77
|
-
|
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,
|
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
|
-
|
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,
|
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,
|
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
|
-
|
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[
|
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[
|
199
|
-
init_vals[
|
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,
|
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,
|
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
|
-
|
245
|
-
create_semaphore(int key,
|
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 |
|
164
|
+
flags = IPC_EXCL | IPC_CREAT | permissions;
|
252
165
|
|
253
|
-
semid = semget(key,
|
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,
|
171
|
+
semid = semget(key, SI_NUM_SEMAPHORES, flags);
|
259
172
|
}
|
260
173
|
return semid;
|
261
174
|
}
|
262
175
|
|
263
|
-
|
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,
|
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
|
-
|
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
|
-
|
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()
|