add a FIFO implementation

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@32914 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
rfm 2011-04-20 10:22:24 +00:00
parent c6fa7c22bc
commit 26e006b78a
4 changed files with 356 additions and 0 deletions

View file

@ -36,6 +36,7 @@ Performance_INTERFACE_VERSION=0.3
Performance_OBJC_FILES += \
GSCache.m \
GSFIFO.m \
GSIOThreadPool.m \
GSLinkedList.m \
GSThreadPool.m \
@ -47,6 +48,7 @@ Performance_OBJC_FILES += \
Performance_HEADER_FILES += \
GSCache.h \
GSFIFO.h \
GSIOThreadPool.h \
GSLinkedList.h \
GSThreadPool.h \
@ -57,6 +59,7 @@ Performance_HEADER_FILES += \
Performance_AGSDOC_FILES += \
GSCache.h \
GSFIFO.h \
GSIOThreadPool.h \
GSLinkedList.h \
GSThreadPool.h \

154
GSFIFO.h Normal file
View file

@ -0,0 +1,154 @@
#if !defined(INCLUDED_GSFIFO)
#define INCLUDED_GSFIFO 1
/**
Copyright (C) 2011 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: April 2011
This file is part of the Performance Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#import <Foundation/NSObject.h>
@class NSLock;
@class NSString;
/** GSFIFO manages a first-in-first-out queue of items.<br />
* Items in the queue ae <em>NOT</em> retained ... 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
* 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 a 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
* 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).
*/
@interface GSFIFO : NSObject
{
@public
/* While the following instance variables are nominally public, they are in
* fact only intended to be used by the provided inline functions ... you
* should not acces them direcdtly in your own code.
*/
id *_items;
uint64_t _head;
uint64_t _tail;
uint32_t _capacity;
@private
uint16_t granularity;
uint16_t timeout;
uint64_t fullCount;
uint64_t emptyCount;
NSLock *getLock;
NSLock *putLock;
NSString *name;
}
/** Gets the next object from the FIFO, blocking if necessary until an
* object is available. Raises an exception if the FIFO is configured
* with a timeout and it is exceeded.
*/
- (id) get;
/** Initialises the receiver with the specified capacity (buffer size).<br />
* If the granularity value is non-zero, it is treated as the maximum time
* in milliseconds for which a -get or -put: operation will pause between
* successive attempts.<br />
* 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 FIFI 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 FIFI 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 />
* The name string is simply used to identify the receiver when printing
* diagnostics.
*/
- (id) initWithCapacity: (uint32_t)c
granularity: (uint16_t)g
timeout: (uint16_t)t
multiProducer: (BOOL)mp
multiConsumer: (BOOL)mc
name: (NSString*)n;
/** Adds an object to the FIFO, blocking if necessary until there is
* space in the buffer. Raises an exception if the FIFO is configured
* with a timeout and it is exceeded.
*/
- (void) put: (id)item;
@end
/** Function to efficiently get an item from a fast FIFO.<br />
* Warning ... only for use if the FIFO is NOT configured for multiple
* consumers.
*/
static inline id
GSGetFastFIFO(GSFIFO *receiver)
{
if (receiver->_head > receiver->_tail)
{
id object;
object = receiver->_items[receiver->_tail % receiver->_capacity];
receiver->_tail++;
return object;
}
return [receiver get];
}
/** Function to efficiently put an item to a fast FIFO.<br />
* Warning ... only for use if the FIFO is NOT configured for multiple
* producers.
*/
static inline void
GSPutFastFIFO(GSFIFO *receiver, id item)
{
if (receiver->_head - receiver->_tail < receiver->_capacity)
{
receiver->_items[receiver->_head % receiver->_capacity] = item;
receiver->_head++;
return;
}
[receiver put: item];
}
#endif

198
GSFIFO.m Normal file
View file

@ -0,0 +1,198 @@
/**
Copyright (C) 2011 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: April 2011
This file is part of the Performance Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#import "GSFIFO.h"
#import <Foundation/NSDate.h>
#import <Foundation/NSLock.h>
#import <Foundation/NSString.h>
#import <Foundation/NSThread.h>
#import <Foundation/NSZone.h>
@implementation GSFIFO
- (void) dealloc
{
[name release];
[getLock release];
[putLock release];
if (0 != _items)
{
NSZoneFree(NSDefaultMallocZone(), _items);
}
[super dealloc];
}
- (NSString*) description
{
return [NSString stringWithFormat:
@"%@ (%@) get:%llu put:%llu empty:%llu full:%llu",
[super description], name,
(unsigned long long)_tail,
(unsigned long long)_head,
(unsigned long long)emptyCount,
(unsigned long long)fullCount];
}
- (id) get
{
id obj;
NSTimeInterval sum;
uint32_t old;
uint32_t fib;
if (nil == getLock)
{
if (_head > _tail)
{
obj = _items[_tail % _capacity];
_tail++;
return obj;
}
emptyCount++;
}
else
{
[getLock lock];
if (_head > _tail)
{
obj = _items[_tail % _capacity];
_tail++;
[getLock unlock];
return obj;
}
emptyCount++;
}
old = 0;
fib = 1;
sum = 0.0;
while (_head <= _tail)
{
uint32_t tmp;
NSTimeInterval dly;
if (timeout > 0 && sum * 1000 > timeout)
{
[getLock unlock];
[NSException raise: NSGenericException
format: @"Timout waiting for new data in FIFO"];
}
tmp = fib + old;
old = fib;
fib = tmp;
if (granularity > 0 && fib > granularity)
{
fib = granularity;
}
dly = ((NSTimeInterval)fib) / 1000.0;
[NSThread sleepForTimeInterval: dly];
sum += dly;
}
obj = _items[_tail % _capacity];
_tail++;
[getLock unlock];
return obj;
}
- (id) initWithCapacity: (uint32_t)c
granularity: (uint16_t)g
timeout: (uint16_t)t
multiProducer: (BOOL)mp
multiConsumer: (BOOL)mc
name: (NSString*)n
{
if (c < 1)
{
[self release];
return nil;
}
_capacity = c;
granularity = g;
timeout = t;
_items = (id*)NSZoneMalloc(NSDefaultMallocZone(), sizeof(id) * c);
if (YES == mp) putLock = [NSLock new];
if (YES == mc) getLock = [NSLock new];
name = [n copy];
return self;
}
- (void) put: (id)item
{
NSTimeInterval sum;
uint32_t old;
uint32_t fib;
if (nil == putLock)
{
if (_head - _tail < _capacity)
{
_items[_head % _capacity] = item;
_head++;
return;
}
fullCount++;
}
else
{
[putLock lock];
if (_head - _tail < _capacity)
{
_items[_head % _capacity] = item;
_head++;
[putLock unlock];
return;
}
fullCount++;
}
old = 0;
fib = 1;
sum = 0.0;
while (_head - _tail >= _capacity)
{
uint32_t tmp;
NSTimeInterval dly;
if (timeout > 0 && sum * 1000 > timeout)
{
[putLock unlock];
[NSException raise: NSGenericException
format: @"Timout waiting for space in FIFO"];
}
tmp = fib + old;
old = fib;
fib = tmp;
if (granularity > 0 && fib > granularity)
{
fib = granularity;
}
dly = ((NSTimeInterval)fib) / 1000.0;
[NSThread sleepForTimeInterval: dly];
sum += dly;
}
_items[_head % _capacity] = item;
_head++;
[putLock unlock];
}
@end

View file

@ -25,6 +25,7 @@
*/
#import "GSCache.h"
#import "GSFIFO.h"
#import "GSIOThreadPool.h"
#import "GSLinkedList.h"
#import "GSThreadPool.h"