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.
@@ -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
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+
3
+ have_header 'pthread.h'
4
+ have_header 'semaphore.h'
5
+
6
+ create_makefile 'psem/psem'
@@ -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
+ }
@@ -0,0 +1,5 @@
1
+ require 'psem/psem'
2
+
3
+ class PSem
4
+ VERSION = '0.0.1'
5
+ end
@@ -0,0 +1,8 @@
1
+ require 'minitest/autorun'
2
+ require 'psem/psem'
3
+
4
+ class PSemTest < MiniTest::Unit::TestCase
5
+ def test_sanity
6
+ assert_equal 'ok', PSem.new.working
7
+ end
8
+ end
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: []