ice799-shlock 0.0.2 → 0.0.3
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.
- data/README +15 -10
- data/ext/shlock.c +155 -40
- data/shlock.gemspec +1 -1
- metadata +1 -1
data/README
CHANGED
@@ -1,21 +1,26 @@
|
|
1
|
-
What is
|
1
|
+
What is shlock ?
|
2
2
|
===============
|
3
|
+
shlock is a library that provides synchronization primitives that use shared memory.
|
3
4
|
|
4
|
-
|
5
|
-
|
5
|
+
Currently, mutexes (Shlock::Putex) and rwlocks (Shlock::Prwlock) are implemented.
|
6
6
|
|
7
7
|
Why ?
|
8
8
|
=====
|
9
|
-
|
10
9
|
If want to synchronize two ruby processes without using those painful file locks, you can use this.
|
11
10
|
|
11
|
+
TODO
|
12
|
+
====
|
13
|
+
- It is probably not safe to use these pthread exposed APIs in this way. I should probably reimplement
|
14
|
+
the low-level locking primitives in C using atomic.h and futexes.
|
12
15
|
|
13
|
-
|
14
|
-
|
16
|
+
- Add documentation about Shlock::Prwlock
|
17
|
+
|
18
|
+
Sample Usage of Shlock::Putex
|
19
|
+
=============================
|
15
20
|
|
16
21
|
process1.rb:
|
17
|
-
require '
|
18
|
-
f = Putex.new("/test-mutex")
|
22
|
+
require 'shlock'
|
23
|
+
f = Shlock::Putex.new("/test-mutex")
|
19
24
|
f.lock
|
20
25
|
puts("locked the mutex once, grabbing lock again to spin-wait; go run process2.rb once locked up...\n")
|
21
26
|
f.lock
|
@@ -23,8 +28,8 @@ puts("got the lock, exiting\n")
|
|
23
28
|
|
24
29
|
|
25
30
|
process2.rb:
|
26
|
-
require '
|
27
|
-
f = Putex.new("/text-mutex")
|
31
|
+
require 'shlock'
|
32
|
+
f = Shlock::Putex.new("/text-mutex")
|
28
33
|
puts "alive, unlocking"
|
29
34
|
f.unlock
|
30
35
|
puts "done, exiting"
|
data/ext/shlock.c
CHANGED
@@ -6,48 +6,67 @@
|
|
6
6
|
#include <fcntl.h>
|
7
7
|
#include <errno.h>
|
8
8
|
#include <sys/stat.h>
|
9
|
+
#include <semaphore.h>
|
9
10
|
|
10
11
|
struct mutex_info {
|
11
12
|
pthread_mutex_t m;
|
12
|
-
int fd;
|
13
13
|
};
|
14
14
|
|
15
15
|
struct rwlock_info {
|
16
16
|
pthread_rwlock_t rwlock;
|
17
|
-
int fd;
|
18
17
|
};
|
19
18
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
struct sem_info {
|
20
|
+
sem_t sem;
|
21
|
+
};
|
22
|
+
|
23
|
+
/**
|
24
|
+
* @return ret 0 if the shared memory already existed, 1 if it had to be created.
|
25
|
+
*/
|
26
|
+
|
27
|
+
static int shared_mem_open(const char *name, int *fd) {
|
28
|
+
int ret = 0;
|
29
|
+
int mfd = shm_open (name, O_RDWR, S_IRUSR | S_IWUSR);
|
30
|
+
if (mfd < 0) {
|
31
|
+
mfd = shm_open (name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
|
32
|
+
if (mfd < 0) {
|
33
|
+
fprintf(stderr, "shm_open error: %s\n", strerror(errno));
|
34
|
+
return -1;
|
35
|
+
}
|
36
|
+
ret = 1;
|
37
|
+
if (ftruncate(mfd, sizeof(struct mutex_info)) == -1) {
|
38
|
+
fprintf(stderr,"ftruncate error: %s\n", strerror(errno));
|
26
39
|
return -1;
|
27
40
|
}
|
28
41
|
}
|
29
42
|
|
30
|
-
|
31
|
-
|
32
|
-
return -1;
|
33
|
-
}
|
34
|
-
|
35
|
-
return fd;
|
43
|
+
*fd = mfd;
|
44
|
+
return ret;
|
36
45
|
}
|
37
46
|
|
38
47
|
static VALUE rb_putex_new (VALUE self, VALUE name) {
|
39
48
|
struct mutex_info *ms;
|
40
|
-
int
|
41
|
-
|
49
|
+
int mfd = 0;
|
50
|
+
int ret = shared_mem_open(StringValuePtr(name), &mfd);
|
51
|
+
if (mfd < 0)
|
42
52
|
return Qnil;
|
43
53
|
|
44
|
-
ms = (struct mutex_info *)mmap(NULL, sizeof(struct mutex_info), PROT_READ|PROT_WRITE, MAP_SHARED,
|
54
|
+
ms = (struct mutex_info *)mmap(NULL, sizeof(struct mutex_info), PROT_READ|PROT_WRITE, MAP_SHARED, mfd, 0);
|
55
|
+
close(mfd);
|
56
|
+
|
45
57
|
if (ms == MAP_FAILED) {
|
46
|
-
|
58
|
+
fprintf(stderr, "mmap error: %s\n", strerror(errno));
|
47
59
|
return Qnil;
|
48
60
|
}
|
49
|
-
|
50
|
-
|
61
|
+
|
62
|
+
if (ret) {
|
63
|
+
/* mutex was just created so we need to set some attributes and initalize the mutex */
|
64
|
+
pthread_mutexattr_t mattr;
|
65
|
+
pthread_mutexattr_init(&mattr);
|
66
|
+
pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
|
67
|
+
pthread_mutex_init(&(ms->m), &mattr);
|
68
|
+
}
|
69
|
+
|
51
70
|
VALUE obj = Data_Wrap_Struct(self, NULL, NULL, ms);
|
52
71
|
return obj;
|
53
72
|
}
|
@@ -55,41 +74,58 @@ static VALUE rb_putex_new (VALUE self, VALUE name) {
|
|
55
74
|
static VALUE rb_putex_destroy (VALUE self) {
|
56
75
|
struct mutex_info *m;
|
57
76
|
Data_Get_Struct(self, struct mutex_info, m);
|
58
|
-
|
59
|
-
|
77
|
+
if (munmap(m, sizeof(struct mutex_info)) == -1) {
|
78
|
+
fprintf(stderr, "munmap error: %s\n", strerror(errno));
|
79
|
+
}
|
60
80
|
|
61
81
|
return Qnil;
|
62
82
|
}
|
63
83
|
|
64
84
|
static VALUE rb_putex_lock (VALUE self) {
|
65
85
|
struct mutex_info *fs;
|
86
|
+
int ret = 0;
|
66
87
|
Data_Get_Struct(self, struct mutex_info, fs);
|
67
|
-
pthread_mutex_lock(&(fs->m))
|
88
|
+
if ((ret = pthread_mutex_lock(&(fs->m))) == -1) {
|
89
|
+
fprintf(stderr, "pthread_mutex_lock: %s\n", strerror(ret));
|
90
|
+
}
|
68
91
|
|
69
92
|
return Qnil;
|
70
93
|
}
|
71
94
|
|
72
95
|
static VALUE rb_putex_unlock (VALUE self) {
|
73
96
|
struct mutex_info *fs;
|
97
|
+
int ret = 0;
|
74
98
|
Data_Get_Struct(self, struct mutex_info, fs);
|
75
|
-
pthread_mutex_unlock(&(fs->m))
|
99
|
+
if ((ret = pthread_mutex_unlock(&(fs->m))) == -1) {
|
100
|
+
fprintf(stderr, "pthread_mutex_unlock: %s\n", strerror(ret));
|
101
|
+
}
|
76
102
|
|
77
103
|
return Qnil;
|
78
104
|
}
|
79
105
|
|
80
106
|
static VALUE rb_rwlock_new (VALUE self, VALUE name) {
|
81
107
|
struct rwlock_info *rws;
|
82
|
-
int
|
83
|
-
|
108
|
+
int mfd = 0;
|
109
|
+
|
110
|
+
int ret = shared_mem_open (StringValuePtr(name), &mfd);
|
111
|
+
if (ret == -1)
|
84
112
|
return Qnil;
|
85
113
|
|
86
|
-
rws = (struct rwlock_info *)mmap(NULL, sizeof(struct rwlock_info), PROT_READ|PROT_WRITE, MAP_SHARED,
|
114
|
+
rws = (struct rwlock_info *)mmap(NULL, sizeof(struct rwlock_info), PROT_READ|PROT_WRITE, MAP_SHARED, mfd, 0);
|
115
|
+
close(mfd);
|
116
|
+
|
87
117
|
if (rws == MAP_FAILED) {
|
88
|
-
|
118
|
+
fprintf(stderr, "mmap error: %s\n", strerror(errno));
|
89
119
|
return Qnil;
|
90
120
|
}
|
91
|
-
|
92
|
-
|
121
|
+
|
122
|
+
if (ret) {
|
123
|
+
/* rwlock was just created so we need to set some attributes and initalize */
|
124
|
+
pthread_rwlockattr_t rwattr;
|
125
|
+
pthread_rwlockattr_init(&rwattr);
|
126
|
+
pthread_rwlockattr_setpshared(&rwattr, PTHREAD_PROCESS_SHARED);
|
127
|
+
pthread_rwlock_init(&(rws->rwlock), &rwattr);
|
128
|
+
}
|
93
129
|
|
94
130
|
VALUE obj = Data_Wrap_Struct(self, NULL, NULL, rws);
|
95
131
|
return obj;
|
@@ -98,36 +134,109 @@ static VALUE rb_rwlock_new (VALUE self, VALUE name) {
|
|
98
134
|
static VALUE rb_rwlock_destory (VALUE self) {
|
99
135
|
struct rwlock_info *rws;
|
100
136
|
Data_Get_Struct(self, struct rwlock_info, rws);
|
101
|
-
|
102
|
-
close(rws->fd);
|
103
|
-
|
137
|
+
munmap(rws, sizeof(struct rwlock_info));
|
104
138
|
return Qnil;
|
105
139
|
}
|
106
140
|
|
107
141
|
static VALUE rb_rwlock_read_lock (VALUE self) {
|
108
142
|
struct rwlock_info *rws;
|
143
|
+
int ret = 0;
|
109
144
|
Data_Get_Struct(self, struct rwlock_info, rws);
|
110
|
-
pthread_rwlock_rdlock(&(rws->rwlock))
|
145
|
+
if ((ret = pthread_rwlock_rdlock(&(rws->rwlock))) == -1) {
|
146
|
+
fprintf(stderr, "pthread_rwlock_rdlock: %s\n", strerror(ret));
|
147
|
+
}
|
111
148
|
|
112
149
|
return Qnil;
|
113
150
|
}
|
114
151
|
|
115
152
|
static VALUE rb_rwlock_write_lock (VALUE self) {
|
116
153
|
struct rwlock_info *rws;
|
154
|
+
int ret = 0;
|
117
155
|
Data_Get_Struct (self, struct rwlock_info, rws);
|
118
|
-
pthread_rwlock_wrlock(&(rws->rwlock))
|
156
|
+
if ((ret = pthread_rwlock_wrlock(&(rws->rwlock))) == -1) {
|
157
|
+
fprintf(stderr, "pthread_rwlock_wrlock: %s\n", strerror(ret));
|
158
|
+
}
|
119
159
|
|
120
160
|
return Qnil;
|
121
161
|
}
|
122
162
|
|
123
163
|
static VALUE rb_rwlock_unlock (VALUE self) {
|
124
164
|
struct rwlock_info *rws;
|
165
|
+
int ret = 0;
|
166
|
+
Data_Get_Struct (self, struct rwlock_info, rws);
|
167
|
+
if ((ret = pthread_rwlock_unlock(&(rws->rwlock))) == -1) {
|
168
|
+
fprintf(stderr, "pthread_rwlock_unlock: %s\n", strerror(ret));
|
169
|
+
}
|
170
|
+
|
171
|
+
return Qnil;
|
172
|
+
}
|
173
|
+
|
174
|
+
static VALUE rb_rwlock_destroy (VALUE self) {
|
175
|
+
struct rwlock_info *rws;
|
176
|
+
int ret = 0;
|
125
177
|
Data_Get_Struct (self, struct rwlock_info, rws);
|
126
|
-
|
178
|
+
if ((ret = pthread_rwlock_destroy(&(rws->rwlock))) == -1) {
|
179
|
+
fprintf(stderr, "pthread_rwlock_destroy: %s\n", strerror(ret));
|
180
|
+
}
|
127
181
|
|
128
|
-
|
129
|
-
|
182
|
+
return Qnil;
|
183
|
+
}
|
184
|
+
|
185
|
+
static VALUE rb_psem_new (VALUE self, VALUE name, VALUE val) {
|
186
|
+
struct sem_info *si;
|
187
|
+
int mfd = 0;
|
188
|
+
|
189
|
+
int ret = shared_mem_open (StringValuePtr(name), &mfd);
|
190
|
+
if (ret == -1)
|
191
|
+
return Qnil;
|
192
|
+
|
193
|
+
si = (struct sem_info *)mmap(NULL, sizeof(struct sem_info), PROT_READ|PROT_WRITE, MAP_SHARED, mfd, 0);
|
194
|
+
close(mfd);
|
195
|
+
|
196
|
+
if (si == MAP_FAILED) {
|
197
|
+
fprintf(stderr, "mmap error: %s\n", strerror(errno));
|
198
|
+
return Qnil;
|
199
|
+
}
|
200
|
+
|
201
|
+
if (ret) {
|
202
|
+
/* sem was just created so we need to set some attributes and initalize */
|
203
|
+
sem_init(&(si->sem), 1, INT2NUM(val));
|
204
|
+
}
|
205
|
+
|
206
|
+
VALUE obj = Data_Wrap_Struct(self, NULL, NULL, si);
|
207
|
+
return obj;
|
208
|
+
}
|
209
|
+
|
210
|
+
static VALUE rb_psem_lock (VALUE self) {
|
211
|
+
struct sem_info *si;
|
212
|
+
int ret = 0;
|
213
|
+
Data_Get_Struct (self, struct sem_info, si);
|
214
|
+
if ((ret = sem_wait(&(si->sem))) == -1) {
|
215
|
+
fprintf(stderr, "sem_wait: %s\n", strerror(ret));
|
130
216
|
}
|
217
|
+
|
218
|
+
return Qnil;
|
219
|
+
}
|
220
|
+
|
221
|
+
static VALUE rb_psem_unlock (VALUE self) {
|
222
|
+
struct sem_info *si;
|
223
|
+
int ret = 0;
|
224
|
+
Data_Get_Struct (self, struct sem_info, si);
|
225
|
+
if ((ret = sem_post(&(si->sem))) == -1) {
|
226
|
+
fprintf(stderr, "sem_post: %s\n", strerror(ret));
|
227
|
+
}
|
228
|
+
|
229
|
+
return Qnil;
|
230
|
+
}
|
231
|
+
|
232
|
+
static VALUE rb_psem_destroy (VALUE self) {
|
233
|
+
struct sem_info *si;
|
234
|
+
int ret = 0;
|
235
|
+
Data_Get_Struct (self, struct sem_info, si);
|
236
|
+
if ((ret = sem_destroy(&(si->sem))) == -1) {
|
237
|
+
fprintf(stderr, "sem_destroy: %s\n", strerror(ret));
|
238
|
+
}
|
239
|
+
|
131
240
|
return Qnil;
|
132
241
|
}
|
133
242
|
|
@@ -135,7 +244,6 @@ void Init_shlock() {
|
|
135
244
|
VALUE shared_lock_module = rb_define_module("Shlock");
|
136
245
|
|
137
246
|
/* Shlock::Putex */
|
138
|
-
|
139
247
|
VALUE putex_class = rb_define_class_under(shared_lock_module, "Putex", rb_cObject);
|
140
248
|
rb_define_singleton_method(putex_class, "new", rb_putex_new, 1);
|
141
249
|
rb_define_method(putex_class, "lock", rb_putex_lock, 0);
|
@@ -143,10 +251,17 @@ void Init_shlock() {
|
|
143
251
|
rb_define_method(putex_class, "destroy", rb_putex_destroy, 0);
|
144
252
|
|
145
253
|
/* Shlock::Prwlock */
|
146
|
-
|
147
254
|
VALUE prwlock_class = rb_define_class_under(shared_lock_module, "Prwlock", rb_cObject);
|
148
255
|
rb_define_singleton_method(prwlock_class, "new", rb_rwlock_new, 1);
|
149
256
|
rb_define_method(prwlock_class, "read_lock", rb_rwlock_read_lock, 0);
|
150
257
|
rb_define_method(prwlock_class, "write_lock", rb_rwlock_write_lock, 0);
|
151
258
|
rb_define_method(prwlock_class, "unlock", rb_rwlock_unlock, 0);
|
259
|
+
rb_define_method(prwlock_class, "destroy", rb_rwlock_destroy, 0);
|
260
|
+
|
261
|
+
/* Shlock::Psem */
|
262
|
+
VALUE psem_class = rb_define_class_under(shared_lock_module, "Psem", rb_cObject);
|
263
|
+
rb_define_singleton(psem_class, "new", rb_psem_new, 2);
|
264
|
+
rb_define_method(psem_class, "lock", rb_psem_lock, 0);
|
265
|
+
rb_define_method(psem_class, "unlock", rb_psem_unlock, 0);
|
266
|
+
rb_define_method(psem_class, "destroy", rb_psem_destroy, 0);
|
152
267
|
}
|
data/shlock.gemspec
CHANGED