-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathSSYSystemSemaphore.m
213 lines (188 loc) · 5.58 KB
/
SSYSystemSemaphore.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#import "SSYSystemSemaphore.h"
static SSYSystemSemaphore* sharedSemaphore = nil ;
@implementation SSYSystemSemaphore
+ (void)setError_p:(NSError**)error_p
withErrno:(NSInteger)semErrno {
if (!error_p) {
return ;
}
NSString* errDesc = nil ;
switch (semErrno) {
case EACCES:
errDesc = @"The required permissions (for reading and/or writing) are denied for the given flags; or O_CREAT is specified, the object does not exist, and permission to create the semaphore is denied." ;
break;
case EEXIST:
errDesc = @"O_CREAT and O_EXCL were specified and the semaphore exists." ;
break;
case EINTR:
errDesc = @"The sem_open() operation was interrupted by a signal." ;
break;
case EINVAL:
errDesc = [NSString stringWithFormat:
@"The shm_open() operation is not supported; or O_CREAT is specified and value exceeds SEM_VALUE_MAX = %ld.",
(long)SEM_VALUE_MAX] ;
break;
case EMFILE:
errDesc = @"The process has already reached its limit for semaphores or file descriptors in use." ;
break;
case ENAMETOOLONG:
errDesc = @"Requested name exceeded SEM_NAME_LEN characters." ;
break;
case ENFILE:
errDesc = @"Too many semaphores or file descriptors are open on the system." ;
break;
case ENOENT:
errDesc = @"O_CREAT is not set and the named semaphore does not exist." ;
break;
case ENOSPC:
errDesc = @"O_CREAT is specified, the file does not exist, and there is insufficient space available to create the semaphore." ;
break;
default:
errDesc = @"Unknown errno" ;
}
*error_p = [NSError errorWithDomain:@"SSYSystemSemaphore"
code:semErrno
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
errDesc, NSLocalizedDescriptionKey,
nil]] ;
}
@synthesize name ;
@synthesize descriptor ;
@synthesize gotSemaphore ;
@synthesize initialBackoff ;
@synthesize backoffFactor ;
@synthesize maxBackoff ;
@synthesize timeout ;
// Returns YES if either got semaphore or could not get semaphore because it is exclusively in use.
// Returns NO if unrecoverable error
- (BOOL)tryLockError_p:(NSError**)error_p {
sem_t* descriptor_ ;
const char* name_ = [[self name] UTF8String] ;
// Create a semaphore with an initial value of 1 if it does not exists
// or just open the existing semaphore if it does.
descriptor_ = sem_open(
name_, // name
O_CREAT, // create if does not exist
S_IRWXU, // permissions (rwx by user)
1 // Set to value 1 = "in use", only if one is created
) ;
if (descriptor_ == SEM_FAILED) {
// Unexpected error
}
else {
NSInteger failed = sem_trywait(descriptor_) ;
if (failed == 0) {
// Got semaphore
[self setDescriptor:descriptor_] ;
[self setGotSemaphore:YES] ;
return YES ;
}
else {
if (errno == EAGAIN) {
// Normal error when exclusive semaphore is not available
return YES ;
}
else {
// Unexpected error
}
}
}
// Unexpected error
[[self class] setError_p:error_p
withErrno:errno] ;
return NO ;
}
- (void)setName:(NSString*)name_
initialBackoff:(NSTimeInterval)initialBackoff_
backoffFactor:(CGFloat)backoffFactor_
maxBackoff:(NSTimeInterval)maxBackoff_
timeout:(NSTimeInterval)timeout_ {
[self setName:name_] ;
[self setInitialBackoff:initialBackoff_] ;
[self setBackoffFactor:backoffFactor_] ;
[self setMaxBackoff:maxBackoff_] ;
[self setTimeout:timeout_] ;
}
+ (SSYSystemSemaphore*)sharedSemaphore {
@synchronized(self) {
if (!sharedSemaphore) {
sharedSemaphore = [[self alloc] init] ;
}
}
// No autorelease. This sticks around forever.
return sharedSemaphore ;
}
- (void)dealloc {
[name release] ;
[super dealloc] ;
}
- (BOOL)lockError_p:(NSError**)error_p {
[self setGotSemaphore:NO] ;
NSTimeInterval backoff = [self initialBackoff] ;
NSDate* deadline = [NSDate dateWithTimeIntervalSinceNow:[self timeout]] ;
while ([(NSDate*)[NSDate date] compare:deadline] == NSOrderedAscending) {
if ([self tryLockError_p:error_p]) {
if ([self gotSemaphore]) {
// Got semaphore
return YES ;
}
else {
// Semaphore is not available
usleep( 1000000 * backoff ) ;
backoff = backoff * [self backoffFactor] ;
backoff = MIN(backoff, [self maxBackoff]) ;
}
}
else {
// Unrecoverable error
return NO ;
}
}
// Timeout
if (error_p) {
NSString* msg = [NSString stringWithFormat:
@"Timeout %g secs exceeded attempting to acquire '%@'",
[self timeout],
[self name]
] ;
*error_p = [NSError errorWithDomain:@"SSYSystemSemaphore"
code:ETIME
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
msg, NSLocalizedDescriptionKey,
nil]] ;
}
return NO ;
}
/*
sem_close() must be called once for every prior sem_open(), at which point
the semaphore is deleted from memory and its descriptor is invalidated.
*/
- (BOOL)relinquishError_p:(NSError**)error_p {
if (![self gotSemaphore]) {
if (error_p) {
*error_p = [NSError errorWithDomain:@"SSYSystemSemaphore"
code:ESRCH
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
@"Not got semaphore, can't relinquish", NSLocalizedDescriptionKey,
nil]] ;
}
return NO ;
}
NSInteger failed = NO ;
failed = sem_post([self descriptor]);
if (failed) {
NSLog(@"Error unlocking") ;
[[self class] setError_p:error_p
withErrno:errno] ;
return NO ;
}
failed = sem_close([self descriptor]) ;
if (failed) {
NSLog(@"Error closing") ;
[[self class] setError_p:error_p
withErrno:errno] ;
return NO ;
}
return YES ;
}
@end