mirror of
https://github.com/gnustep/libs-performance.git
synced 2025-02-23 11:51:20 +00:00
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:
parent
c6fa7c22bc
commit
26e006b78a
4 changed files with 356 additions and 0 deletions
|
@ -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
154
GSFIFO.h
Normal 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
198
GSFIFO.m
Normal 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
|
||||
|
|
@ -25,6 +25,7 @@
|
|||
*/
|
||||
|
||||
#import "GSCache.h"
|
||||
#import "GSFIFO.h"
|
||||
#import "GSIOThreadPool.h"
|
||||
#import "GSLinkedList.h"
|
||||
#import "GSThreadPool.h"
|
||||
|
|
Loading…
Reference in a new issue