ice799-shlock 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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