Oolite 1.91.0.7646-241128-10e222e
Loading...
Searching...
No Matches
OOAsyncQueue.m
Go to the documentation of this file.
1/*
2
3OOAsyncQueue.m
4By Jens Ayton
5
6
7Copyright (C) 2007-2013 Jens Ayton
8
9Permission is hereby granted, free of charge, to any person obtaining a copy
10of this software and associated documentation files (the "Software"), to deal
11in the Software without restriction, including without limitation the rights
12to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13copies of the Software, and to permit persons to whom the Software is
14furnished to do so, subject to the following conditions:
15
16The above copyright notice and this permission notice shall be included in all
17copies or substantial portions of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25SOFTWARE.
26
27*/
28
29#include <assert.h>
30
31#import "OOAsyncQueue.h"
33#import "OOLogging.h"
35#include <stdlib.h>
36
37#ifndef OO_BUGGY_PTHREADS
38#if OOLITE_WINDOWS
39// Maybe add #if OOLITE_64_BIT too?
40#define OO_BUGGY_PTHREADS 1
41#else
42#define OO_BUGGY_PTHREADS 0
43#endif
44#endif
45
46enum
47{
51};
52
53
54enum
55{
57};
58
59
62{
65};
66
67
69{
70 return malloc(sizeof (OOAsyncQueueElement));
71}
72
73
75{
76 free(element);
77}
78
79
80@interface OOAsyncQueue (OOPrivate)
81
84- (void)recycleElementWithAcquiredLock:(OOAsyncQueueElement *)element;
85
86@end
87
88
89@implementation OOAsyncQueue
90
91- (id)init
92{
93 self = [super init];
94 if (self != nil)
95 {
96 _lock = [[NSConditionLock alloc] initWithCondition:kConditionNoData];
97 [_lock setName:@"OOAsyncQueue lock"];
98 if (_lock == nil)
99 {
100 [self release];
101 self = nil;
102 }
103 }
104
105 return self;
106}
107
108
109- (void)dealloc
110{
111 OOAsyncQueueElement *element = NULL;
112
113 [_lock lock];
114
115 if (_elemCount != 0)
116 {
117 OOLogWARN(@"asyncQueue.nonEmpty", @"%@ deallocated while non-empty, flushing.", self);
118 [self doEmptyQueueWithAcquiredLock];
119 }
120
121 // Free element pool.
122 while (_pool != NULL)
123 {
124 element = _pool;
125 _pool = element->next;
126 free(element);
127 }
128
129 [_lock unlockWithCondition:kConditionDead];
130 [_lock release];
131
132 [super dealloc];
133}
134
135
136- (NSString *)description
137{
138 // Don't bother locking, the value would be out of date immediately anyway.
139 return [NSString stringWithFormat:@"<%@ %p>{%u elements}", [self class], self, _elemCount];
140}
141
142
143- (BOOL)enqueue:(id)object
144{
145 OOAsyncQueueElement *element = NULL;
146 BOOL success = NO;
147
148 if (EXPECT_NOT(object == nil)) return NO;
149
150 [_lock lock];
151
152 // Get an element.
153 if (_pool != NULL)
154 {
155 element = _pool;
156 _pool = element->next;
157 --_poolCount;
158 }
159 else
160 {
161 element = AllocElement();
162 if (element == NULL) goto FAIL;
163 }
164
165 // Set element fields.
166 element->object = [object retain];
167 element->next = NULL;
168
169 // Insert in queue.
170 if (_head == NULL)
171 {
172 // Queue was empty, element is entire queue.
173 _head = _tail = element;
174 element->next = NULL;
175 assert(_elemCount == 0);
176 _elemCount = 1;
177 }
178 else
179 {
180 assert(_tail != NULL);
181 assert(_tail->next == NULL);
182 assert(_elemCount != 0);
183
184 _tail->next = element;
185 _tail = element;
186 ++_elemCount;
187 }
188 success = YES;
189
190FAIL:
191 [_lock unlockWithCondition:kConditionQueuedData];
192 return success;
193}
194
195
196- (id)dequeue
197{
198 [_lock lockWhenCondition:kConditionQueuedData];
199 return [self doDequeAndUnlockWithAcquiredLock];
200}
201
202
203- (id)tryDequeue
204{
205#if OO_BUGGY_PTHREADS
206/* pthread_mutex_trylock is buggy on 64-bit windows with the pthread
207 * library in use, so avoid doing things which use it This is a little
208 * more blocking, but no thread should be hanging on to _lock for very
209 * long, so hopefully it won't be noticeable.
210 */
211 [_lock lock];
212 if ([_lock condition] != kConditionQueuedData)
213 {
214 [_lock unlock];
215 return NO;
216 }
217#else
218 // Mac and Linux can do it properly
219 if (![_lock tryLockWhenCondition:kConditionQueuedData]) return nil;
220#endif
221 return [self doDequeAndUnlockWithAcquiredLock];
222}
223
224
225- (BOOL)empty
226{
227 return _head != NULL;
228}
229
230
231- (unsigned)count
232{
233 return _elemCount;
234}
235
236
237- (void)emptyQueue
238{
239 [_lock lock];
240 [self doEmptyQueueWithAcquiredLock];
241
242 assert(_head == NULL && _tail == NULL && _elemCount == 0);
243 [_lock unlockWithCondition:kConditionNoData];
244}
245
246@end
247
248
249@implementation OOAsyncQueue (OOPrivate)
250
252{
253 OOAsyncQueueElement *element = NULL;
254
255 // Loop over queue.
256 while (_head != NULL)
257 {
258 // Dequeue element.
259 element = _head;
260 _head = _head->next;
261 --_elemCount;
262
263 // We don't need the payload any longer.
264 [element->object release];
265
266 // Or the element.
267 [self recycleElementWithAcquiredLock:element];
268 }
269
270 _tail = NULL;
271}
272
273
275{
276 OOAsyncQueueElement *element = NULL;
277 id result;
278
279 if (_head == NULL)
280 {
281 // Can happen if you enter debugger.
282 return nil;
283 }
284
285// assert(_head != NULL);
286
287 // Dequeue element.
288 element = _head;
289 _head = _head->next;
290 if (_head == NULL) _tail = NULL;
291 --_elemCount;
292
293 // Unpack payload.
294 result = [element->object autorelease];
295
296 // Recycle element.
297 [self recycleElementWithAcquiredLock:element];
298
299 // Ensure sane status.
300 assert((_head == NULL && _tail == NULL && _elemCount == 0) || (_head != NULL && _tail != NULL && _elemCount != 0));
301
302 // Unlock with appropriate state.
303 [_lock unlockWithCondition:(_head == NULL) ? kConditionNoData : kConditionQueuedData];
304
305 return result;
306}
307
308
309- (void)recycleElementWithAcquiredLock:(OOAsyncQueueElement *)element
310{
311 if (_poolCount < kMaxPoolElements)
312 {
313 // Add to pool for reuse.
314 element->next = _pool;
315 _pool = element;
316 ++_poolCount;
317 }
318 else
319 {
320 // Delete.
321 FreeElement(element);
322 }
323}
324
325@end
@ kMaxPoolElements
OOINLINE void FreeElement(OOAsyncQueueElement *element)
@ kConditionQueuedData
@ kConditionNoData
@ kConditionDead
OOINLINE OOAsyncQueueElement * AllocElement(void)
#define EXPECT_NOT(x)
#define OOINLINE
#define FAIL(s)
#define OOLogWARN(class, format,...)
Definition OOLogging.h:113
unsigned count
return nil
OOAsyncQueueElement * next