psem 0.0.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 +7 -0
- data/ext/psem/extconf.rb +6 -0
- data/ext/psem/psem.c +269 -0
- data/lib/psem.rb +5 -0
- data/test/psem_test.rb +8 -0
- metadata +51 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0cc49a5c2fb863dab11ff27cfc82f6cdbd4522f77438c0a072b166c32baf08dd
|
4
|
+
data.tar.gz: c34963bdefe3a9024520602c4c7f417c9c5df14f2a653519980f530fb60eb458
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0def52627a77ed1eddea9e58a9a14907de17465f999a92930f3ff3b00735f9372a7b0cd28bae00beb37000c681f4bb5d2356e70da95640fe217191d68ba09ae4
|
7
|
+
data.tar.gz: 38cd4cbb971fd574cb8fa66295c6250b233a120f66b04a46a5e22cc50ee8839d0466128d9a06ea3facdf162b5b8b324c0a14d7c52c4e862a99503919204b9ce9
|
data/ext/psem/extconf.rb
ADDED
data/ext/psem/psem.c
ADDED
@@ -0,0 +1,269 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
#include <fcntl.h>
|
3
|
+
#include <sys/stat.h>
|
4
|
+
#include <semaphore.h>
|
5
|
+
#include <errno.h>
|
6
|
+
#include <time.h>
|
7
|
+
|
8
|
+
static inline sem_t* get_current_semaphore(VALUE self)
|
9
|
+
{
|
10
|
+
sem_t* sem = (sem_t*)rb_ivar_get(self, rb_intern("inner"));
|
11
|
+
if (sem == NULL) {
|
12
|
+
rb_raise(rb_eRuntimeError, "inner semaphore is not initialized");
|
13
|
+
}
|
14
|
+
return sem;
|
15
|
+
}
|
16
|
+
|
17
|
+
VALUE psem_unlink(VALUE self, VALUE rbStr_semName)
|
18
|
+
{
|
19
|
+
Check_Type(rbStr_semName, T_STRING);
|
20
|
+
|
21
|
+
// Check length and format of semaphore name
|
22
|
+
const char* sem_name = StringValueCStr(rbStr_semName);
|
23
|
+
if (sem_name == NULL || strlen(sem_name) < 2 || sem_name[0] != '/') {
|
24
|
+
rb_raise(rb_eRuntimeError, "invalid name. has to be in the format of '/foobar'");
|
25
|
+
}
|
26
|
+
if (strlen(sem_name) > 253) {
|
27
|
+
rb_raise(rb_eRuntimeError, "name cannot be longer than 253 bytes long");
|
28
|
+
}
|
29
|
+
|
30
|
+
// Unlink the semaphore
|
31
|
+
if (sem_unlink(sem_name) == -1) {
|
32
|
+
switch (errno) {
|
33
|
+
case EACCES:
|
34
|
+
rb_raise(rb_eRuntimeError, "you do not have permission to unlink this semaphore");
|
35
|
+
break;
|
36
|
+
case ENAMETOOLONG:
|
37
|
+
rb_raise(rb_eRuntimeError, "name was too long");
|
38
|
+
break;
|
39
|
+
case ENOENT:
|
40
|
+
rb_raise(rb_eRuntimeError, "no semaphore matching the name");
|
41
|
+
break;
|
42
|
+
default:
|
43
|
+
rb_raise(rb_eRuntimeError, "unknown error");
|
44
|
+
}
|
45
|
+
}
|
46
|
+
return Qtrue;
|
47
|
+
}
|
48
|
+
|
49
|
+
VALUE psem_exists(VALUE self, VALUE rbStr_semName)
|
50
|
+
{
|
51
|
+
Check_Type(rbStr_semName, T_STRING);
|
52
|
+
|
53
|
+
// Check length and format of semaphore name
|
54
|
+
const char* sem_name = StringValueCStr(rbStr_semName);
|
55
|
+
if (sem_name == NULL || strlen(sem_name) < 2 || sem_name[0] != '/') {
|
56
|
+
rb_raise(rb_eRuntimeError, "invalid name. has to be in the format of '/foobar'");
|
57
|
+
}
|
58
|
+
if (strlen(sem_name) > 253) {
|
59
|
+
rb_raise(rb_eRuntimeError, "name cannot be longer than 253 bytes long");
|
60
|
+
}
|
61
|
+
|
62
|
+
// Open the sem without O_CREAT flag so if we don't get a SEM_FAILED, it must exist
|
63
|
+
if (sem_open(sem_name, 0) != SEM_FAILED) {
|
64
|
+
return Qtrue;
|
65
|
+
}
|
66
|
+
else {
|
67
|
+
switch (errno) {
|
68
|
+
case ENOENT:
|
69
|
+
// Does not exist
|
70
|
+
return Qfalse;
|
71
|
+
case EACCES:
|
72
|
+
// Exists but above our priv. level
|
73
|
+
return Qtrue;
|
74
|
+
break;
|
75
|
+
case ENAMETOOLONG:
|
76
|
+
rb_raise(rb_eRuntimeError, "name was too long");
|
77
|
+
break;
|
78
|
+
case EINVAL:
|
79
|
+
rb_raise(rb_eRuntimeError, "given name consists only of / and nothing else");
|
80
|
+
break;
|
81
|
+
default:
|
82
|
+
rb_raise(rb_eRuntimeError, "unknown error");
|
83
|
+
}
|
84
|
+
}
|
85
|
+
return Qnil;
|
86
|
+
}
|
87
|
+
|
88
|
+
VALUE psem_initialize(VALUE self, VALUE rbStr_semName, VALUE rbNum_initialValue)
|
89
|
+
{
|
90
|
+
Check_Type(rbStr_semName, T_STRING);
|
91
|
+
Check_Type(rbNum_initialValue, T_FIXNUM);
|
92
|
+
|
93
|
+
// Check length and format of semaphore name
|
94
|
+
const char* sem_name = StringValueCStr(rbStr_semName);
|
95
|
+
if (sem_name == NULL || strlen(sem_name) < 2 || sem_name[0] != '/') {
|
96
|
+
rb_raise(rb_eRuntimeError, "invalid name. has to be in the format of '/foobar'");
|
97
|
+
}
|
98
|
+
if (strlen(sem_name) > 251) {
|
99
|
+
rb_raise(rb_eRuntimeError, "name cannot be longer than 251 bytes long");
|
100
|
+
}
|
101
|
+
|
102
|
+
// Check initial value is positive
|
103
|
+
int initialValue = FIX2INT(rbNum_initialValue);
|
104
|
+
if (initialValue < 0) {
|
105
|
+
rb_raise(rb_eRuntimeError, "semaphores cannot have negative values");
|
106
|
+
}
|
107
|
+
|
108
|
+
// Create the semaphore
|
109
|
+
sem_t* sem = sem_open(sem_name, O_CREAT, strtol("0644", 0, 8), initialValue);
|
110
|
+
if (sem == SEM_FAILED) {
|
111
|
+
char buf[64];
|
112
|
+
snprintf(buf, 64, "sem_open() failed with errno: %d", errno);
|
113
|
+
rb_raise(rb_eRuntimeError, buf);
|
114
|
+
}
|
115
|
+
|
116
|
+
// Set inner
|
117
|
+
rb_ivar_set(self, rb_intern("inner"), (VALUE)sem);
|
118
|
+
return self;
|
119
|
+
}
|
120
|
+
|
121
|
+
VALUE psem_get_value(VALUE self)
|
122
|
+
{
|
123
|
+
sem_t* sem = get_current_semaphore(self);
|
124
|
+
int val = 0xFFFF;
|
125
|
+
if (sem_getvalue(sem, &val) == -1) {
|
126
|
+
rb_raise(rb_eRuntimeError, "inner semaphore is not a valid semaphore");
|
127
|
+
}
|
128
|
+
return INT2NUM(val);
|
129
|
+
}
|
130
|
+
|
131
|
+
VALUE psem_post(VALUE self)
|
132
|
+
{
|
133
|
+
sem_t* sem = get_current_semaphore(self);
|
134
|
+
if (sem_post(sem) == -1) {
|
135
|
+
switch (errno) {
|
136
|
+
case EINVAL:
|
137
|
+
rb_raise(rb_eRuntimeError, "inner semaphore is not a valid semaphore");
|
138
|
+
break;
|
139
|
+
case EOVERFLOW:
|
140
|
+
rb_raise(rb_eRuntimeError, "maximum allowable value for a semaphore would be exceeded");
|
141
|
+
break;
|
142
|
+
default:
|
143
|
+
rb_raise(rb_eRuntimeError, "unknown error");
|
144
|
+
}
|
145
|
+
}
|
146
|
+
return Qnil;
|
147
|
+
}
|
148
|
+
|
149
|
+
VALUE psem_wait(VALUE self)
|
150
|
+
{
|
151
|
+
sem_t* sem = get_current_semaphore(self);
|
152
|
+
if (sem_wait(sem) == -1) {
|
153
|
+
switch (errno) {
|
154
|
+
case EINTR:
|
155
|
+
rb_raise(rb_eRuntimeError, "got interrupted by a signal handler");
|
156
|
+
break;
|
157
|
+
case EINVAL:
|
158
|
+
rb_raise(rb_eRuntimeError, "inner semaphore is not a valid semaphore");
|
159
|
+
break;
|
160
|
+
default:
|
161
|
+
rb_raise(rb_eRuntimeError, "unknown error");
|
162
|
+
}
|
163
|
+
}
|
164
|
+
return Qtrue;
|
165
|
+
}
|
166
|
+
|
167
|
+
VALUE psem_trywait(VALUE self)
|
168
|
+
{
|
169
|
+
sem_t* sem = get_current_semaphore(self);
|
170
|
+
if (sem_trywait(sem) == -1) {
|
171
|
+
switch (errno) {
|
172
|
+
case EAGAIN:
|
173
|
+
return Qfalse;
|
174
|
+
case EINTR:
|
175
|
+
rb_raise(rb_eRuntimeError, "got interrupted by a signal handler");
|
176
|
+
break;
|
177
|
+
case EINVAL:
|
178
|
+
rb_raise(rb_eRuntimeError, "inner semaphore is not a valid semaphore");
|
179
|
+
break;
|
180
|
+
default:
|
181
|
+
rb_raise(rb_eRuntimeError, "unknown error");
|
182
|
+
}
|
183
|
+
}
|
184
|
+
return Qtrue;
|
185
|
+
}
|
186
|
+
|
187
|
+
VALUE psem_waitmillis(VALUE self, VALUE rbNum_timeoutMillis)
|
188
|
+
{
|
189
|
+
Check_Type(rbNum_timeoutMillis, T_FIXNUM);
|
190
|
+
|
191
|
+
int ms = FIX2INT(rbNum_timeoutMillis);
|
192
|
+
struct timespec ts;
|
193
|
+
ts.tv_sec = ms / 1000;
|
194
|
+
ts.tv_nsec = (ms % 1000) * 1000000;
|
195
|
+
|
196
|
+
sem_t* sem = get_current_semaphore(self);
|
197
|
+
if (sem_timedwait(sem, &ts) == -1) {
|
198
|
+
switch (errno) {
|
199
|
+
case ETIMEDOUT:
|
200
|
+
return Qfalse;
|
201
|
+
case EINTR:
|
202
|
+
rb_raise(rb_eRuntimeError, "got interrupted by a signal handler");
|
203
|
+
break;
|
204
|
+
case EINVAL:
|
205
|
+
rb_raise(rb_eRuntimeError, "inner semaphore is not a valid semaphore");
|
206
|
+
break;
|
207
|
+
default:
|
208
|
+
rb_raise(rb_eRuntimeError, "unknown error");
|
209
|
+
}
|
210
|
+
}
|
211
|
+
return Qtrue;
|
212
|
+
}
|
213
|
+
|
214
|
+
VALUE psem_close(VALUE self)
|
215
|
+
{
|
216
|
+
sem_t* sem = get_current_semaphore(self);
|
217
|
+
if (sem_close(sem) == -1) {
|
218
|
+
switch (errno) {
|
219
|
+
case EINVAL:
|
220
|
+
rb_raise(rb_eRuntimeError, "inner semaphore is not a valid semaphore");
|
221
|
+
break;
|
222
|
+
default:
|
223
|
+
rb_raise(rb_eRuntimeError, "unknown error");
|
224
|
+
}
|
225
|
+
}
|
226
|
+
return Qnil;
|
227
|
+
}
|
228
|
+
|
229
|
+
VALUE psem_get_inner(VALUE self)
|
230
|
+
{
|
231
|
+
// Get inner as size_t type. Primarily for debugging purposes.
|
232
|
+
size_t cast = (size_t)rb_ivar_get(self, rb_intern("inner"));
|
233
|
+
return INT2NUM(cast);
|
234
|
+
}
|
235
|
+
|
236
|
+
void Init_psem()
|
237
|
+
{
|
238
|
+
VALUE psemClass = rb_define_class("PSem", rb_cObject);
|
239
|
+
|
240
|
+
// Initialize (does not change the value of the semaphore if it already exists
|
241
|
+
// If the initial value is important or if the semaphore's presence is important,
|
242
|
+
// they can be accessed via psem_exists.
|
243
|
+
rb_define_method(psemClass, "initialize", psem_initialize, 2);
|
244
|
+
|
245
|
+
// View the inner sem_t pointer as a BigNum for debugging
|
246
|
+
rb_define_method(psemClass, "get_inner", psem_get_inner, 0);
|
247
|
+
|
248
|
+
// Get current value -- nonblocking but in Linux returns the number
|
249
|
+
// of processes/threads waiting on the semaphore as a negative integer
|
250
|
+
// if it has reached zero already.
|
251
|
+
rb_define_method(psemClass, "get_value", psem_get_value, 0);
|
252
|
+
|
253
|
+
// Increment owned
|
254
|
+
rb_define_method(psemClass, "post", psem_post, 0);
|
255
|
+
|
256
|
+
// Decrement owned
|
257
|
+
rb_define_method(psemClass, "wait", psem_wait, 0);
|
258
|
+
rb_define_method(psemClass, "try_wait", psem_trywait, 0);
|
259
|
+
rb_define_method(psemClass, "wait_millis", psem_waitmillis, 1);
|
260
|
+
|
261
|
+
// Unlink owned
|
262
|
+
rb_define_method(psemClass, "close", psem_close, 0);
|
263
|
+
|
264
|
+
// Unlink by name
|
265
|
+
rb_define_singleton_method(psemClass, "unlink", psem_unlink, 1);
|
266
|
+
|
267
|
+
// Check if exists
|
268
|
+
rb_define_singleton_method(psemClass, "exists", psem_exists, 1);
|
269
|
+
}
|
data/lib/psem.rb
ADDED
data/test/psem_test.rb
ADDED
metadata
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: psem
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Can Selcik
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-02-22 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: |
|
14
|
+
A named semaphore is identified by a name of the form /somename; that is, a null-terminated string of up to 251 characters consisting of an initial slash, followed by one or more characters, none of which are slashes. Two processes can operate on the same named semaphore by passing the same name to sem_open.
|
15
|
+
|
16
|
+
The sem_open function creates a new named semaphore or opens an existing named semaphore. After the semaphore has been opened, it can be operated on using sem_post and sem_wait. When a process has finished using the semaphore, it can use sem_close to close the semaphore. When all processes have finished using the semaphore, it can be removed from the system using sem_unlink.
|
17
|
+
email: selcik.can@gmail.com
|
18
|
+
executables: []
|
19
|
+
extensions:
|
20
|
+
- ext/psem/extconf.rb
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- ext/psem/extconf.rb
|
24
|
+
- ext/psem/psem.c
|
25
|
+
- lib/psem.rb
|
26
|
+
- test/psem_test.rb
|
27
|
+
homepage: http://rubygems.org/gems/psem
|
28
|
+
licenses:
|
29
|
+
- MIT
|
30
|
+
metadata:
|
31
|
+
source_code_uri: https://github.com/canselcik/psem
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options: []
|
34
|
+
require_paths:
|
35
|
+
- lib
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
requirements: []
|
47
|
+
rubygems_version: 3.0.2
|
48
|
+
signing_key:
|
49
|
+
specification_version: 4
|
50
|
+
summary: A Simple Ruby Gem for POSIX Named Semaphores
|
51
|
+
test_files: []
|