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:
rfm 2011-06-05 09:14:05 +00:00
parent 53562f92a9
commit 83dfe04d32
3 changed files with 150 additions and 18 deletions

View file

@ -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

View file

@ -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
{
@ -76,8 +81,8 @@
uint16_t timeout;
uint64_t fullCount;
uint64_t emptyCount;
NSLock *getLock;
NSLock *putLock;
NSConditionLock *getLock;
NSConditionLock *putLock;
NSString *name;
}
@ -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
View file

@ -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];