mirror of
https://github.com/gnustep/libs-performance.git
synced 2025-02-23 20:01:15 +00:00
fix error in locking
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33779 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
373deccf3f
commit
caf79e74d9
2 changed files with 63 additions and 127 deletions
39
GSFIFO.h
39
GSFIFO.h
|
@ -32,41 +32,31 @@
|
|||
|
||||
|
||||
/** GSFIFO manages a first-in-first-out queue of items.<br />
|
||||
* Items in the queue ae <em>NOT</em> retained objects ... memory management
|
||||
* Items in the queue are <em>NOT</em> retained objects ... memory management
|
||||
* is not the job of this class and it is not intended to be treated
|
||||
* as a 'collection', rather its role is intended to be that of an
|
||||
* inter-thread coordination mechanism.<br />
|
||||
* Instances of the GSFIFO class are intended to support the producer-consumer
|
||||
* model of processing. The ideal example is that of a production line,
|
||||
* where you have a stream of items to be processed and while that procesing
|
||||
* where you have a stream of items to be processed and while that processing
|
||||
* can be broken down into separate stages, they must be done in a particular
|
||||
* order. The FIFO is used as the link betwen those stages, ensuring that
|
||||
* the required ordering is maintained even when separate threads handle
|
||||
* each stage.<br />
|
||||
* Where there is a single consumer thread, a fast lock-fre algorthm is
|
||||
* used to get items from the FIFO, similarly, where there is a single
|
||||
* producer thread the addition of items to the FIFO can be lock-free.<br />
|
||||
* Where there is a single producer and a single consumer thread, a fast
|
||||
* lock-free algorthm is used to get/pu items from the FIFO.<br />
|
||||
* To minimise the overheads of using the FIFO, we provide inline functions
|
||||
* to support the addition of items in the single producer thread case and to
|
||||
* support the removal of items in the single consumer thread case. When
|
||||
* operting that way, the overhead of using the FIFO is only a few CPU cycles
|
||||
* operating that way, the overhead of using the FIFO is only a few CPU cycles
|
||||
* and it becomes reasonable to split sequentional processing into a long
|
||||
* series of small operations each handled by a separate thread (making
|
||||
* effective use of a multi-cpu machine).<br />
|
||||
* The FIFO may also be useful where you don't have a strictly sequential
|
||||
* process to manage, but some parts need to be sequential ... in these
|
||||
* cases it may make sense to have either a single producer thread adding
|
||||
* to the FIFO and multiple consumers removing from it, or have a single
|
||||
* consumer with multiple producers or (rarely) have both multiple producers
|
||||
* and multiple consumers. In these rarer cases, some locking is required
|
||||
* and the use of the inline functions is not allowed (you must call the
|
||||
* -get method wherever you have multiple consumer threads, and must call
|
||||
* the -put: method wherever you have multiple producer threads).<br />
|
||||
* Where both multiple producers and multiple consumers are configured,
|
||||
* there are two locks used to coordinate access to the FIFO, and the
|
||||
* addition of an item to an empty FIFO or removal of an item from a full
|
||||
* FIFO signals any blocked threads to cmplete their access to the FIFO
|
||||
* immediately.
|
||||
* cases it may make sense to have multiple consumers and/or producers.
|
||||
* In these cases, some locking is required and the use of the inline
|
||||
* functions is not allowed (you must call the -get and -put: methods.<br />
|
||||
*/
|
||||
@interface GSFIFO : NSObject
|
||||
{
|
||||
|
@ -136,17 +126,8 @@
|
|||
* If the timeout value is non-zero, it is treated as the total time in
|
||||
* milliseconds for which a -get or -put: operation may block, and a
|
||||
* longer delay will cause those methods to raise an exception.<br />
|
||||
* If the multiProducer flag is YES, the FIFO is configured to support
|
||||
* multiple producer threads (ie more than one thread using the -put:
|
||||
* method) by using of locking while adding items to the FIFO.<br />
|
||||
* If the multiConsumer flag is YES, the FIFO is configured to support
|
||||
* multiple consumer threads (ie more than one thread using the -get
|
||||
* method) by using of locking while removing items from the FIFO.<br />
|
||||
* If both multiConsumer and multiProducer are set, cooperative use of
|
||||
* locking by put and get methods means that no retries are required
|
||||
* and the granularity is ignored (a -put: to an empty FIFO allows any
|
||||
* blocked -get to proceed, and a -get from a full FIFO allows any
|
||||
* blocked -put: to proceed immediately).<br />
|
||||
* If the multiProducer or multiConsumer flag is YES, the FIFO is
|
||||
* configured to support multiple producer/consumer threads using locking.<br />
|
||||
* The boundaries array is an ordered list of NSNumber objects containing
|
||||
* time intervals found boundaries of bands into which to categorise wait
|
||||
* time stats. Any wait whose duration is less than the interval specified
|
||||
|
|
151
GSFIFO.m
151
GSFIFO.m
|
@ -179,12 +179,10 @@ stats(NSTimeInterval ti, uint32_t max, NSTimeInterval *bounds, uint64_t *bands)
|
|||
{
|
||||
isEmpty = YES;
|
||||
}
|
||||
if (YES == wasFull)
|
||||
{
|
||||
[putLock lock];
|
||||
[putLock unlockWithCondition: 1];
|
||||
}
|
||||
[getLock unlockWithCondition: isEmpty ? 0 : 1];
|
||||
[putLock lock];
|
||||
[putLock unlockWithCondition: (_head - _tail < _capacity) ? 1 : 0];
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
@ -202,6 +200,7 @@ stats(NSTimeInterval ti, uint32_t max, NSTimeInterval *bounds, uint64_t *bands)
|
|||
_putTryFailure++;
|
||||
if (NO == block)
|
||||
{
|
||||
[getLock unlock];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -220,6 +219,7 @@ stats(NSTimeInterval ti, uint32_t max, NSTimeInterval *bounds, uint64_t *bands)
|
|||
{
|
||||
[d release];
|
||||
ENDPUT
|
||||
[getLock unlock];
|
||||
[NSException raise: NSGenericException
|
||||
format: @"Timeout waiting for space in FIFO"];
|
||||
}
|
||||
|
@ -245,12 +245,9 @@ stats(NSTimeInterval ti, uint32_t max, NSTimeInterval *bounds, uint64_t *bands)
|
|||
{
|
||||
isFull = YES;
|
||||
}
|
||||
if (YES == wasEmpty)
|
||||
{
|
||||
[getLock lock];
|
||||
[getLock unlockWithCondition: 1];
|
||||
}
|
||||
[putLock unlockWithCondition: isFull ? 0 : 1];
|
||||
[getLock lock];
|
||||
[getLock unlockWithCondition: (_head - _tail > 0) ? 1 : 0];
|
||||
return index;
|
||||
}
|
||||
|
||||
|
@ -311,54 +308,30 @@ stats(NSTimeInterval ti, uint32_t max, NSTimeInterval *bounds, uint64_t *bands)
|
|||
|
||||
if (0 == count) return 0;
|
||||
|
||||
if (nil == getLock)
|
||||
{
|
||||
if (nil == getThread)
|
||||
{
|
||||
getThread = [NSThread currentThread];
|
||||
}
|
||||
if (_head > _tail)
|
||||
{
|
||||
for (index = 0; index < count && _head > _tail; index++)
|
||||
{
|
||||
buf[index] = _items[_tail % _capacity];
|
||||
_tail++;
|
||||
_getTrySuccess++;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
_getTryFailure++;
|
||||
emptyCount++;
|
||||
if (NO == block)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (nil != putLock)
|
||||
if (nil != getLock)
|
||||
{
|
||||
return [self _cooperatingGet: buf count: count shouldBlock: block];
|
||||
}
|
||||
else
|
||||
|
||||
if (nil == getThread)
|
||||
{
|
||||
[getLock lock];
|
||||
if (_head > _tail)
|
||||
getThread = [NSThread currentThread];
|
||||
}
|
||||
if (_head > _tail)
|
||||
{
|
||||
for (index = 0; index < count && _head > _tail; index++)
|
||||
{
|
||||
for (index = 0; index < count && _head > _tail; index++)
|
||||
{
|
||||
buf[index] = _items[_tail % _capacity];
|
||||
_tail++;
|
||||
_getTrySuccess++;
|
||||
}
|
||||
[getLock unlock];
|
||||
return index;
|
||||
}
|
||||
_getTryFailure++;
|
||||
emptyCount++;
|
||||
if (NO == block)
|
||||
{
|
||||
[getLock unlock];
|
||||
return 0;
|
||||
buf[index] = _items[_tail % _capacity];
|
||||
_tail++;
|
||||
_getTrySuccess++;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
_getTryFailure++;
|
||||
emptyCount++;
|
||||
if (NO == block)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
START
|
||||
|
@ -402,7 +375,8 @@ stats(NSTimeInterval ti, uint32_t max, NSTimeInterval *bounds, uint64_t *bands)
|
|||
{
|
||||
void *item = 0;
|
||||
|
||||
[self get: &item count: 1 shouldBlock: YES];
|
||||
while (0 == [self get: &item count: 1 shouldBlock: YES])
|
||||
;
|
||||
return item;
|
||||
}
|
||||
|
||||
|
@ -423,8 +397,11 @@ stats(NSTimeInterval ti, uint32_t max, NSTimeInterval *bounds, uint64_t *bands)
|
|||
granularity = g;
|
||||
timeout = t;
|
||||
_items = (void*)NSAllocateCollectable(c * sizeof(void*), NSScannedOption);
|
||||
if (YES == mp) putLock = [[NSConditionLock alloc] initWithCondition: 1];
|
||||
if (YES == mc) getLock = [[NSConditionLock alloc] initWithCondition: 0];
|
||||
if (YES == mp || YES == mc)
|
||||
{
|
||||
putLock = [[NSConditionLock alloc] initWithCondition: 1];
|
||||
getLock = [[NSConditionLock alloc] initWithCondition: 0];
|
||||
}
|
||||
name = [n copy];
|
||||
if (nil == a)
|
||||
{
|
||||
|
@ -506,54 +483,31 @@ stats(NSTimeInterval ti, uint32_t max, NSTimeInterval *bounds, uint64_t *bands)
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
if (nil == putLock)
|
||||
{
|
||||
if (nil == putThread)
|
||||
{
|
||||
putThread = [NSThread currentThread];
|
||||
}
|
||||
if (_head - _tail < _capacity)
|
||||
{
|
||||
for (index = 0; index < count && _head - _tail < _capacity; index++)
|
||||
{
|
||||
_items[_head % _capacity] = buf[index];
|
||||
_head++;
|
||||
}
|
||||
_putTrySuccess++;
|
||||
return index;
|
||||
}
|
||||
_putTryFailure++;
|
||||
fullCount++;
|
||||
if (NO == block)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (nil != getLock)
|
||||
|
||||
if (nil != putLock)
|
||||
{
|
||||
return [self _cooperatingPut: buf count: count shouldBlock: block];
|
||||
}
|
||||
else
|
||||
|
||||
if (nil == putThread)
|
||||
{
|
||||
[putLock lock];
|
||||
if (_head - _tail < _capacity)
|
||||
putThread = [NSThread currentThread];
|
||||
}
|
||||
if (_head - _tail < _capacity)
|
||||
{
|
||||
for (index = 0; index < count && _head - _tail < _capacity; index++)
|
||||
{
|
||||
for (index = 0; index < count && _head - _tail < _capacity; index++)
|
||||
{
|
||||
_items[_head % _capacity] = buf[index];
|
||||
_head++;
|
||||
}
|
||||
_putTrySuccess++;
|
||||
[putLock unlock];
|
||||
return index;
|
||||
}
|
||||
_putTryFailure++;
|
||||
fullCount++;
|
||||
if (NO == block)
|
||||
{
|
||||
[putLock unlock];
|
||||
return 0;
|
||||
_items[_head % _capacity] = buf[index];
|
||||
_head++;
|
||||
}
|
||||
_putTrySuccess++;
|
||||
return index;
|
||||
}
|
||||
_putTryFailure++;
|
||||
fullCount++;
|
||||
if (NO == block)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
START
|
||||
|
@ -595,7 +549,8 @@ stats(NSTimeInterval ti, uint32_t max, NSTimeInterval *bounds, uint64_t *bands)
|
|||
|
||||
- (void) put: (void*)item
|
||||
{
|
||||
[self put: &item count: 1 shouldBlock: YES];
|
||||
while (0 == [self put: &item count: 1 shouldBlock: YES])
|
||||
;
|
||||
}
|
||||
|
||||
- (void) _getStats: (NSMutableString*)s
|
||||
|
|
Loading…
Reference in a new issue