mirror of
https://github.com/gnustep/libs-performance.git
synced 2025-02-23 11:51:20 +00:00
use condition locks to avoid polling/spinning
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33251 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
53562f92a9
commit
83dfe04d32
3 changed files with 150 additions and 18 deletions
|
@ -1,3 +1,10 @@
|
|||
2011-06-05 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* GSFIFO.h:
|
||||
* GSFIFO.m:
|
||||
Use condition locks so that, if we have locking for both addition
|
||||
and removal, we can signal threads waiting at the other end.
|
||||
|
||||
2011-06-03 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* GSTicker.m: Thread safety fixes ... make date storage volatile and
|
||||
|
|
38
GSFIFO.h
38
GSFIFO.h
|
@ -24,7 +24,7 @@
|
|||
*/
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@class NSLock;
|
||||
@class NSConditionLock;
|
||||
@class NSString;
|
||||
|
||||
|
||||
|
@ -42,12 +42,12 @@
|
|||
* 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 a the addition of items to the FIFO can be lock-free.<br />
|
||||
* producer thread the addition of items to the FIFO can be lock-free.<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
|
||||
* and it becomes reasonable to split sequentional procesing into a long
|
||||
* 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
|
||||
|
@ -58,7 +58,12 @@
|
|||
* 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).
|
||||
* 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.
|
||||
*/
|
||||
@interface GSFIFO : NSObject
|
||||
{
|
||||
|
@ -69,16 +74,16 @@
|
|||
*/
|
||||
volatile uint64_t _head;
|
||||
volatile uint64_t _tail;
|
||||
void **_items;
|
||||
uint32_t _capacity;
|
||||
void **_items;
|
||||
uint32_t _capacity;
|
||||
@private
|
||||
uint16_t granularity;
|
||||
uint16_t timeout;
|
||||
uint64_t fullCount;
|
||||
uint64_t emptyCount;
|
||||
NSLock *getLock;
|
||||
NSLock *putLock;
|
||||
NSString *name;
|
||||
uint16_t granularity;
|
||||
uint16_t timeout;
|
||||
uint64_t fullCount;
|
||||
uint64_t emptyCount;
|
||||
NSConditionLock *getLock;
|
||||
NSConditionLock *putLock;
|
||||
NSString *name;
|
||||
}
|
||||
|
||||
/** Returns the approximate number of items in the FIFO.
|
||||
|
@ -103,7 +108,12 @@
|
|||
* 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 removng items from the FIFO.<br />
|
||||
* 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 />
|
||||
* The name string is simply used to identify the receiver when printing
|
||||
* diagnostics.
|
||||
*/
|
||||
|
|
123
GSFIFO.m
123
GSFIFO.m
|
@ -29,6 +29,105 @@
|
|||
|
||||
@implementation GSFIFO
|
||||
|
||||
- (void*) _cooperatingGetShouldBlock: (BOOL)block
|
||||
{
|
||||
void *item;
|
||||
BOOL wasFull = NO;
|
||||
BOOL isEmpty = NO;
|
||||
|
||||
if (NO == block)
|
||||
{
|
||||
if (NO == [getLock tryLockWhenCondition: 1])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (0 == timeout)
|
||||
{
|
||||
[getLock lockWhenCondition: 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSDate *d;
|
||||
|
||||
d = [[NSDate alloc] initWithTimeIntervalSinceNow: 1000.0 * timeout];
|
||||
if (NO == [getLock lockWhenCondition: 1 beforeDate: d])
|
||||
{
|
||||
[d release];
|
||||
[NSException raise: NSGenericException
|
||||
format: @"Timeout waiting for new data in FIFO"];
|
||||
}
|
||||
[d release];
|
||||
}
|
||||
|
||||
if (_head - _tail == _capacity)
|
||||
{
|
||||
wasFull = YES;
|
||||
}
|
||||
item = _items[_tail % _capacity];
|
||||
_tail++;
|
||||
if (_head - _tail == 0)
|
||||
{
|
||||
isEmpty = YES;
|
||||
}
|
||||
if (YES == wasFull)
|
||||
{
|
||||
[putLock lock];
|
||||
[putLock unlockWithCondition: 1];
|
||||
}
|
||||
[getLock unlockWithCondition: isEmpty ? 0 : 1];
|
||||
return item;
|
||||
}
|
||||
|
||||
- (BOOL) _cooperatingPut: (void*)item shouldBlock: (BOOL)block
|
||||
{
|
||||
BOOL wasEmpty = NO;
|
||||
BOOL isFull = NO;
|
||||
|
||||
if (NO == block)
|
||||
{
|
||||
if (NO == [putLock tryLockWhenCondition: 1])
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
else if (0 == timeout)
|
||||
{
|
||||
[putLock lockWhenCondition: 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSDate *d;
|
||||
|
||||
d = [[NSDate alloc] initWithTimeIntervalSinceNow: 1000.0 * timeout];
|
||||
if (NO == [putLock lockWhenCondition: 1 beforeDate: d])
|
||||
{
|
||||
[d release];
|
||||
[NSException raise: NSGenericException
|
||||
format: @"Timeout waiting for space in FIFO"];
|
||||
}
|
||||
[d release];
|
||||
}
|
||||
|
||||
if (_head - _tail == 0)
|
||||
{
|
||||
wasEmpty = YES;
|
||||
}
|
||||
_items[_head % _capacity] = item;
|
||||
_head++;
|
||||
if (_head - _tail == _capacity)
|
||||
{
|
||||
isFull = YES;
|
||||
}
|
||||
if (YES == wasEmpty)
|
||||
{
|
||||
[getLock lock];
|
||||
[getLock unlockWithCondition: 1];
|
||||
}
|
||||
[putLock unlockWithCondition: isFull ? 0 : 1];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[name release];
|
||||
|
@ -74,6 +173,10 @@
|
|||
}
|
||||
emptyCount++;
|
||||
}
|
||||
else if (nil != putLock)
|
||||
{
|
||||
return [self _cooperatingGetShouldBlock: YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
[getLock lock];
|
||||
|
@ -99,7 +202,7 @@
|
|||
{
|
||||
[getLock unlock];
|
||||
[NSException raise: NSGenericException
|
||||
format: @"Timout waiting for new data in FIFO"];
|
||||
format: @"Timeout waiting for new data in FIFO"];
|
||||
}
|
||||
tmp = fib + old;
|
||||
old = fib;
|
||||
|
@ -134,8 +237,8 @@
|
|||
granularity = g;
|
||||
timeout = t;
|
||||
_items = (void*)NSZoneMalloc(NSDefaultMallocZone(), sizeof(void*) * c);
|
||||
if (YES == mp) putLock = [NSLock new];
|
||||
if (YES == mc) getLock = [NSLock new];
|
||||
if (YES == mp) putLock = [[NSConditionLock alloc] initWithCondition: 1];
|
||||
if (YES == mc) getLock = [[NSConditionLock alloc] initWithCondition: 0];
|
||||
name = [n copy];
|
||||
return self;
|
||||
}
|
||||
|
@ -156,6 +259,10 @@
|
|||
}
|
||||
fullCount++;
|
||||
}
|
||||
else if (nil != getLock)
|
||||
{
|
||||
[self _cooperatingPut: item shouldBlock: YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
[putLock lock];
|
||||
|
@ -181,7 +288,7 @@
|
|||
{
|
||||
[putLock unlock];
|
||||
[NSException raise: NSGenericException
|
||||
format: @"Timout waiting for space in FIFO"];
|
||||
format: @"Timeout waiting for space in FIFO"];
|
||||
}
|
||||
tmp = fib + old;
|
||||
old = fib;
|
||||
|
@ -213,6 +320,10 @@
|
|||
}
|
||||
emptyCount++;
|
||||
}
|
||||
else if (nil != putLock)
|
||||
{
|
||||
return [self _cooperatingGetShouldBlock: NO];
|
||||
}
|
||||
else
|
||||
{
|
||||
[getLock lock];
|
||||
|
@ -241,6 +352,10 @@
|
|||
}
|
||||
fullCount++;
|
||||
}
|
||||
else if (nil != getLock)
|
||||
{
|
||||
return [self _cooperatingPut: item shouldBlock: NO];
|
||||
}
|
||||
else
|
||||
{
|
||||
[putLock lock];
|
||||
|
|
Loading…
Reference in a new issue