mirror of
https://github.com/gnustep/libs-performance.git
synced 2025-02-24 12:21:23 +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>
|
2011-06-03 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
* GSTicker.m: Thread safety fixes ... make date storage volatile and
|
* GSTicker.m: Thread safety fixes ... make date storage volatile and
|
||||||
|
|
38
GSFIFO.h
38
GSFIFO.h
|
@ -24,7 +24,7 @@
|
||||||
*/
|
*/
|
||||||
#import <Foundation/NSObject.h>
|
#import <Foundation/NSObject.h>
|
||||||
|
|
||||||
@class NSLock;
|
@class NSConditionLock;
|
||||||
@class NSString;
|
@class NSString;
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,12 +42,12 @@
|
||||||
* each stage.<br />
|
* each stage.<br />
|
||||||
* Where there is a single consumer thread, a fast lock-fre algorthm is
|
* 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
|
* 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 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
|
* 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
|
* 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
|
* 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
|
* series of small operations each handled by a separate thread (making
|
||||||
* effective use of a multi-cpu machine).<br />
|
* effective use of a multi-cpu machine).<br />
|
||||||
* The FIFO may also be useful where you don't have a strictly sequential
|
* 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 multiple consumers. In these rarer cases, some locking is required
|
||||||
* and the use of the inline functions is not allowed (you must call the
|
* 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
|
* -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
|
@interface GSFIFO : NSObject
|
||||||
{
|
{
|
||||||
|
@ -69,16 +74,16 @@
|
||||||
*/
|
*/
|
||||||
volatile uint64_t _head;
|
volatile uint64_t _head;
|
||||||
volatile uint64_t _tail;
|
volatile uint64_t _tail;
|
||||||
void **_items;
|
void **_items;
|
||||||
uint32_t _capacity;
|
uint32_t _capacity;
|
||||||
@private
|
@private
|
||||||
uint16_t granularity;
|
uint16_t granularity;
|
||||||
uint16_t timeout;
|
uint16_t timeout;
|
||||||
uint64_t fullCount;
|
uint64_t fullCount;
|
||||||
uint64_t emptyCount;
|
uint64_t emptyCount;
|
||||||
NSLock *getLock;
|
NSConditionLock *getLock;
|
||||||
NSLock *putLock;
|
NSConditionLock *putLock;
|
||||||
NSString *name;
|
NSString *name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the approximate number of items in the FIFO.
|
/** 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 />
|
* method) by using of locking while adding items to the FIFO.<br />
|
||||||
* If the multiConsumer flag is YES, the FIFO is configured to support
|
* If the multiConsumer flag is YES, the FIFO is configured to support
|
||||||
* multiple consumer threads (ie more than one thread using the -get
|
* 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
|
* The name string is simply used to identify the receiver when printing
|
||||||
* diagnostics.
|
* diagnostics.
|
||||||
*/
|
*/
|
||||||
|
|
123
GSFIFO.m
123
GSFIFO.m
|
@ -29,6 +29,105 @@
|
||||||
|
|
||||||
@implementation GSFIFO
|
@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
|
- (void) dealloc
|
||||||
{
|
{
|
||||||
[name release];
|
[name release];
|
||||||
|
@ -74,6 +173,10 @@
|
||||||
}
|
}
|
||||||
emptyCount++;
|
emptyCount++;
|
||||||
}
|
}
|
||||||
|
else if (nil != putLock)
|
||||||
|
{
|
||||||
|
return [self _cooperatingGetShouldBlock: YES];
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[getLock lock];
|
[getLock lock];
|
||||||
|
@ -99,7 +202,7 @@
|
||||||
{
|
{
|
||||||
[getLock unlock];
|
[getLock unlock];
|
||||||
[NSException raise: NSGenericException
|
[NSException raise: NSGenericException
|
||||||
format: @"Timout waiting for new data in FIFO"];
|
format: @"Timeout waiting for new data in FIFO"];
|
||||||
}
|
}
|
||||||
tmp = fib + old;
|
tmp = fib + old;
|
||||||
old = fib;
|
old = fib;
|
||||||
|
@ -134,8 +237,8 @@
|
||||||
granularity = g;
|
granularity = g;
|
||||||
timeout = t;
|
timeout = t;
|
||||||
_items = (void*)NSZoneMalloc(NSDefaultMallocZone(), sizeof(void*) * c);
|
_items = (void*)NSZoneMalloc(NSDefaultMallocZone(), sizeof(void*) * c);
|
||||||
if (YES == mp) putLock = [NSLock new];
|
if (YES == mp) putLock = [[NSConditionLock alloc] initWithCondition: 1];
|
||||||
if (YES == mc) getLock = [NSLock new];
|
if (YES == mc) getLock = [[NSConditionLock alloc] initWithCondition: 0];
|
||||||
name = [n copy];
|
name = [n copy];
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
@ -156,6 +259,10 @@
|
||||||
}
|
}
|
||||||
fullCount++;
|
fullCount++;
|
||||||
}
|
}
|
||||||
|
else if (nil != getLock)
|
||||||
|
{
|
||||||
|
[self _cooperatingPut: item shouldBlock: YES];
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[putLock lock];
|
[putLock lock];
|
||||||
|
@ -181,7 +288,7 @@
|
||||||
{
|
{
|
||||||
[putLock unlock];
|
[putLock unlock];
|
||||||
[NSException raise: NSGenericException
|
[NSException raise: NSGenericException
|
||||||
format: @"Timout waiting for space in FIFO"];
|
format: @"Timeout waiting for space in FIFO"];
|
||||||
}
|
}
|
||||||
tmp = fib + old;
|
tmp = fib + old;
|
||||||
old = fib;
|
old = fib;
|
||||||
|
@ -213,6 +320,10 @@
|
||||||
}
|
}
|
||||||
emptyCount++;
|
emptyCount++;
|
||||||
}
|
}
|
||||||
|
else if (nil != putLock)
|
||||||
|
{
|
||||||
|
return [self _cooperatingGetShouldBlock: NO];
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[getLock lock];
|
[getLock lock];
|
||||||
|
@ -241,6 +352,10 @@
|
||||||
}
|
}
|
||||||
fullCount++;
|
fullCount++;
|
||||||
}
|
}
|
||||||
|
else if (nil != getLock)
|
||||||
|
{
|
||||||
|
return [self _cooperatingPut: item shouldBlock: NO];
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[putLock lock];
|
[putLock lock];
|
||||||
|
|
Loading…
Reference in a new issue