Compare commits

..

3 commits

Author SHA1 Message Date
Richard Frith-MacDonald
31d3e95305 Add bug reporting link
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/tags/0.1.0@22557 72102866-910b-0410-8b05-ffd578937521
2006-02-22 14:53:35 +00:00
Richard Frith-MacDonald
ec8b8ec81b Added release notes
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/tags/0.1.0@22553 72102866-910b-0410-8b05-ffd578937521
2006-02-22 14:39:15 +00:00
Richard Frith-MacDonald
7c994f42e2 Tag initial release
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/tags/0.1.0@22541 72102866-910b-0410-8b05-ffd578937521
2006-02-22 11:13:07 +00:00
31 changed files with 595 additions and 7840 deletions

View file

@ -1,166 +0,0 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

383
ChangeLog
View file

@ -1,386 +1,3 @@
2023-02-03 Richard Frith-Macdonald <rfm@gnu.org>
* GSIOThreadPool.m: If the maximum number of threads desired is
decreased so that we have more threads than we want, the excess
threads are now excluded from the set which may be acquired.
This should increase the chance of those threads being unacquired
so they can be terminated and removed from the pool.
2023-01-25 Wolfgang Lux <wolfgang.lux@gmail.com>
* GSCache.m: Raise exception when attempting to insert object with
nil key.
* GSCache.m: Remove erroneous unlock in exception handler.
2023-01-20 Richard Frith-Macdonald <rfm@gnu.org>
* GSLinkedList.h: The produce and consume functins of a link store
should not count as modifying ownership of the link ... we should
consider the link store to own links whether in the free list or
the main list.
2023-01-13 Richard Frith-Macdonald <rfm@gnu.org>
* GNUmakefile: bump version for 0.6.0 release
2022-07-06 Richard Frith-Macdonald <rfm@gnu.org>
* GSThreadPool.h:
* GSIOThreadPool.h:
* GSThreadPool.m:
* GSIOThreadPool.m:
Add support for naming the pools, with threads named sequentially (as
they are created) based on the pool name.
2019-04-16 Richard Frith-Macdonald <rfm@gnu.org>
* GSCache.h:
* GSCache.m:
Added -setName:forConfiguration: method to set a cache to be
configured by NSUserDefaults settings rather than programmatically.
2018-03-27 Richard Frith-Macdonald <rfm@gnu.org>
* GSIOThreadPool.m: Make the code use NSThread's designated
initialiser so that GSIOThread can be subclassed safely.
2017-07-13 Richard Frith-Macdonald <rfm@gnu.org>
* GSIOThreadPool.m: Make the -terminate: method safe to call from
any thread (performs itself in the correct thread if it is executing).
2015-10-07 Richard Frith-Macdonald <rfm@gnu.org>
* GSFIFO.m: Add method for writing a whole block of data to a FIFO
in one go.
2015-07-29 Richard Frith-Macdonald <rfm@gnu.org>
* GSLinkedList.h:
* GSLinkedList.m:
Add GSLinkedStore for a list which controls its own links and stores
unused links to avoid having to create/destroy link objects
repeatedly.
2015-07-17 Niels Grewe <niels.grewe@halbordnung.de>
* GSFIFO.m: Implement methods that allow waiting on an empty
FIFO for an arbitrary period of time, without raising an
exception.
2015-07-16 Niels Grewe <niels.grewe@halbordnung.de>
* GSFIFO.m: Implement -sizeInBytesExcluding:
2015-07-15 Richard Frith-Macdonald <rfm@gnu.org>
* GSCache.m: Experimental change (use in conjunction with gnustep-base
from svn 38799) to put object memory footprint interrogation into the
base library and make it more accurate and efficient.
2015-05-05 Niels Grewe <niels.grewe@halbordnung.de>
* GSFIFO.m: Fix a potential race condition in -peekObject.
2015-05-05 Niels Grewe <niels.grewe@halbordnung.de>
* GSFIFO.[hm]: Add methods to peek at the top/front object in the
FIFO without removing it.
2015-04-28 Niels Grewe <niels.grewe@halbordnung.de>
* GSFIFO.m: Use -autorelease rather than -release when returning
objects from a FIFO. Fixes a bug where a returned reference would
already be invalid because the FIFO was the last owner of the object.
2015-01-09 Richard Frith-Macdonald <rfm@gnu.org>
* GSFIFO.[hm]: add methods for working with objects and handling
retain/release to treat a FIFO as a container.
2014-11-12 Riccardo Mottola <rm@gnu.org>
* GSThreadPool.m
* GSSkipMutableArray.m
* GSCache.m
Import inttypes.h for pointer formatting
2014-11-12 Riccardo Mottola <rm@gnu.org>
* GSThreadPool.h
Forward-declare NSRecursiveLock, not NSLock
2014-07-11 Yavor Doganov <yavor@gnu.org>
* GNUmakefile (LIBRARIES_DEPEND_UPON): Define.
2014-04-26 Richard Frith-Macdonald <rfm@gnu.org>
* GSUniqued.h:
* GSUniqued.m:
* Performance.h:
* GNUmakefile:
New code to implement uniqued copies of objects for lowered memory
footprint and faster collection lookups.
2013-11-05 Niels Grewe <niels.grewe@halbordnung.de>
* GSFIFO.m: Fix calculation of the timeout for cooperating
get/put.
2013-08-21 Richard Frith-Macdonald <rfm@gnu.org>
* GSFIFO.m: 64bit printf format changes
* GSSkipMutableArray.m: 64bit printf format changes
* GSCache.m: Fix mime size calculations for nonfragile abi.
Change API to use NSUInteger for sizes
* GNUmakefile: bump version fro new release
2013-06-25 Richard Frith-Macdonald <rfm@gnu.org>
* GNUmakefile: bump subminor version
* GSIOThreadPool.h:
* GSIOThreadPool.m:
Add default for initialisation of shared pool, fix bug in setting
new thread and add code for shrinking the pool size (previously
setting it smaller did not actually cause old threads to exit).
2013-02-07 Sebastian Reitenbach <sebastia@l00-bugdead-prods.de>
* GSThreadPool.h, GSCache.m
shutup clang warnings
2013-02-03 Richard Frith-Macdonald <rfm@gnu.org>
* GSTicker.m: Retain timer so that we can safely invalidate it even
on thread exit when the runloop of the current thread may have been
deallocated already.
2012-01-11 Niels Grewe <niels.grewe@halbordnung.de>
* GSSkipMutableArray.m: Change -initWithObjects:count: declaration
to match the superclass one in gnustep-base trunk.
2011-10-25 Richard Frith-Macdonald <rfm@gnu.org>
* GSFIFO.h:
* GSFIFO.m:
Add new method to create a named FIFO and configure it from the
defaults system.
* GNUmakefile: bump version to 0.3.2
2011-08-29 Richard Frith-Macdonald <rfm@gnu.org>
* GSCache.m: Try to fool clang/llvm to avoid useless warning/error.
2011-08-24 Richard Frith-Macdonald <rfm@gnu.org>
* GSFIFO.h:
* GSFIFO.m:
Use NSCondition to support multiple producer/consumer on FIFO.
2011-06-11 Richard Frith-Macdonald <rfm@gnu.org>
* GSFIFO.h:
* GSFIFO.m:
Add the ability to monitor statistics on how long the FIFO blocks
on put and get operations.
Add methods to get/put multiple items.
Add method to get stats for all FIFOs.
2011-06-06 Richard Frith-Macdonald <rfm@gnu.org>
* GSCache.h:
* GSCache.m:
Add a new method to let the delegate know when a cache hit has
occurred on an item which is nearning the end of its lifetime ...
providing an opportunity for the delegate to refresh the cache
before the actual expiry.
Also fix minor memory leak.
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
ensure that only one thread gets to set the base timestamp.
2011-06-02 Richard Frith-Macdonald <rfm@gnu.org>
* GSFIFO.h:
* GSFIFO.m:
Make FIFO items be void* rather than id since we never send them
any messages anyway.
2011-05-19 Richard Frith-Macdonald <rfm@gnu.org>
* GNUmakefile: Add FIFO
* Performance.h: Add FIFO
* GSFIFO.h: Add a FIFO class
* GSFIFO.m: Add a FIFO class
2011-03-08 Richard Frith-Macdonald <rfm@gnu.org>
* GNUmakefile: Bump version number for next release.
2010-11-11 Riccardo Mottola
* GSCache.m
use always class_getInstanceSize() on GNUSTEP for all runtimes
and define a compatibility macro for old macs
* GSThreadPool.h
* GSLinkedList.h
* GSThreadPool.m
* GSSkipMutableArray.m
* GSIOThreadPool.h
* GSIOThreadPool.m
Complete includes and use typedef instead of #define
2010-11-11 Richard Frith-Macdonald <rfm@gnu.org>
* GSCache.m: Try to get defines right to get instance size depending
on the runtime in use.
2010-11-10 Riccardo Mottola
* GSLinkedList.h
* GSSkipMutableArray.m
* GSIOThreadPool.h
Mac 10.4 compatibility definitions
2010-10-01 Richard Frith-Macdonald <rfm@gnu.org>
* GSIOThreadPool.h:
* GSIOThreadPool.m:
Add new methed class for pooling threads to handle I/O and timers.
2010-09-30 Richard Frith-Macdonald <rfm@gnu.org>
* GSThreadPool.h:
* GSThreadPool.m:
Add new methed to return a shared pool.
2010-09-29 Richard Frith-Macdonald <rfm@gnu.org>
* GSLinkedList.h:
* GSLinkedList.m:
Revise structure and API for greater flexibility.
* GSThreadPool.h:
* GSThreadPool.m:
Modify for new linked list api.
2010-09-23 Richard Frith-Macdonald <rfm@gnu.org>
* performance/GSThreadPool.h:
* performance/GSThreadPool.m:
Track count of active threads and count of number of methods
performed in the thread pool. Display via -description.
2010-09-22 Richard Frith-Macdonald <rfm@gnu.org>
* GSLinkedList.h:
* GSLinkedList.m: Simple doubly linked list implementation
* GSThreadPool.h:
* GSThreadPool.m: Lightweight thread pool implementation
* Performance.h: Include new headers
* GNUmakefile: Build/install new source
Add classes for a simple linked list and for a lightweight thread
pool for rapid handoff of operations to other threads.
2008-12-12 Richard Frith-Macdonald <rfm@gnu.org>
* GSSkipMutableArray.m: Fix uninitialised variable.
2008-05-30 Richard Frith-Macdonald <rfm@gnu.org>
* GSThroughput.h:
* GSThroughput.m:
Add support for sending a notification of stats for the last minute
so that rates by the minute can be logged.
2008-02-21 Richard Frith-Macdonald <rfm@gnu.org>
* GSCache.h:
* GSCache.m:
Improve handling of case when delegate asks for an item to be kept...
changes the delegat callback methoid to include lifetime information.
Make the cache thread-safe.
2008-01-31 Richard Frith-Macdonald <rfm@gnu.org>
* GSCache.h:
* GSCache.m:
Make cache keys 'id' rather than 'NSString*' as they can actually be
any copyable object.
2007-12-08 Richard Frith-Macdonald <rfm@gnu.org>
* GNUmakefile: bump subminor version for release
* GSThroughput.m: ([-description]) output information for periods
from last cycle as well as current cycle.
2007-09-14 Richard Frith-Macdonald <rfm@gnu.org>
Update to LGPL3
2007-05-30 Richard Frith-Macdonald <rfm@gnu.org>
* GSTicker.m: Ensure tick is current before calling -newSecond:
2007-04-01 Richard Frith-Macdonald <rfm@gnu.org>
* GSCache.m:
* GSThroughput.m:
* GSSkipMutableArray.m:
* GSTicker.m:
Changes to avoid compiler warnings on MacOS-X
Improvements to calculations for size of instances for cache.
2006-11-11 Matt Rice <ratmice@gmail.com>
* GSMutableSkipArray.m: Fix bug when deallocating an empty list.
2006-11-11 Richard Frith-Macdonald <rfm@gnu.org>
* GSThroughput.h: New ([add:duration:]) method.
* GSThroughput.m: Method to add multiple events with a total
duration (eg where recording individual event times has too
high an overhead).
2006-11-09 Richard Frith-Macdonald <rfm@gnu.org>
* GNUmakefile: update version
* GSThroughput.m: Allow use to keep running totals of stats
2006-09-27 Richard Frith-Macdonald <rfm@gnu.org>
* GNUmakefile:
* GSIndexedSkipList.m:
* GSSkipMutableArray.h:
* GSSkipMutableArray.m:
Minor fix to (hopefully) build on windows.
Hide implementation details.
Add convenience method (to create skiplist) to NSMutableArray.
Some reformatting for coding standards.
* Performance.h: Ad new header for skip lists.
2006-09-27 Matt Rice <ratmice@yahoo.com>
* GSSkipMutableArray.[hm]: New NSMutableArray subclass.
* GSIndexedSkipList.[hm]: Underlying C implementation.
2006-06-07 Richard Frith-Macdonald <rfm@gnu.org>
* GSCache.h:
* GSCache.m:
New convenience method to cache an item until a date.
2006-01-11 Nicola Pero <nicola@brainstorm.co.uk>
* GNUmakefile.wrapper.objc.preamble: New file that fixes the

Binary file not shown.

View file

@ -1,80 +1,55 @@
ifeq ($(GNUSTEP_MAKEFILES),)
GNUSTEP_MAKEFILES := $(shell gnustep-config --variable=GNUSTEP_MAKEFILES 2>/dev/null)
ifeq ($(GNUSTEP_MAKEFILES),)
$(warning )
$(warning Unable to obtain GNUSTEP_MAKEFILES setting from gnustep-config!)
$(warning Perhaps gnustep-make is not properly installed,)
$(warning so gnustep-config is not in your PATH.)
$(warning )
$(warning Your PATH is currently $(PATH))
$(warning )
endif
endif
ifeq ($(GNUSTEP_MAKEFILES),)
$(error You need to set GNUSTEP_MAKEFILES before compiling!)
endif
include $(GNUSTEP_MAKEFILES)/common.make
-include config.make
PACKAGE_NAME = Performance
PACKAGE_VERSION = 0.6.0
Performance_INTERFACE_VERSION=0.6
SVN_BASE_URL = svn+ssh://svn.gna.org/svn/gnustep/libs
SVN_MODULE_NAME = performance
NEEDS_GUI = NO
LIBRARIES_DEPEND_UPON = $(FND_LIBS) $(OBJC_LIBS)
PACKAGE_VERSION = 0.1.0
CVS_MODULE_NAME = gnustep/dev-libs/Performance
CVS_TAG_NAME = Performance
TEST_TOOL_NAME=
LIBRARY_NAME=Performance
DOCUMENT_NAME=Performance
Performance_INTERFACE_VERSION=0.1
Performance_OBJC_FILES += \
GSCache.m \
GSFIFO.m \
GSIOThreadPool.m \
GSLinkedList.m \
GSThreadPool.m \
GSThroughput.m \
GSTicker.m \
GSIndexedSkipList.m \
GSSkipMutableArray.m \
GSUniqued.m \
Performance_HEADER_FILES += \
GSCache.h \
GSFIFO.h \
GSIOThreadPool.h \
GSLinkedList.h \
GSThreadPool.h \
GSThroughput.h \
GSTicker.h \
GSSkipMutableArray.h \
GSUniqued.h \
Performance_AGSDOC_FILES += \
GSCache.h \
GSFIFO.h \
GSIOThreadPool.h \
GSLinkedList.h \
GSThreadPool.h \
GSThroughput.h \
GSTicker.h \
GSSkipMutableArray.h \
GSUniqued.h \
GSTicker.h
# Optional Java wrappers for the library
JAVA_WRAPPER_NAME = Performance
#
# Assume that the use of the gnu runtime means we have the gnustep
# base library and can use its extensions to build Performance stuff.
#
ifeq ($(OBJC_RUNTIME_LIB),gnu)
APPLE=0
else
APPLE=1
endif
ifeq ($(APPLE),1)
ADDITIONAL_OBJC_LIBS += -lgnustep-baseadd
Performance_LIBRARIES_DEPEND_UPON = -lgnustep-baseadd
endif
Performance_HEADER_FILES_INSTALL_DIR = Performance
-include GNUmakefile.preamble

184
GNUstep.h
View file

@ -1,184 +0,0 @@
/* GNUstep.h - macros to make easier to port gnustep apps to macos-x
Copyright (C) 2001 Free Software Foundation, Inc.
Written by: Nicola Pero
Date: March, October 2001
This file is part of GNUstep.
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 2 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
Library 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., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef Performance_GNUstep_h
#define Performance_GNUstep_h
#ifndef RETAIN
/**
* Basic retain operation ... calls [NSObject-retain]<br />
* Does nothing when ARC is in use.
*/
#define RETAIN(object) [(id)(object) retain]
#endif
#ifndef RELEASE
/**
* Basic release operation ... calls [NSObject-release]<br />
* Does nothing when ARC is in use.
*/
#define RELEASE(object) [(id)(object) release]
#endif
#ifndef AUTORELEASE
/**
* Basic autorelease operation ... calls [NSObject-autorelease]<br />
* Does nothing when ARC is in use.
*/
#define AUTORELEASE(object) [(id)(object) autorelease]
#endif
#ifndef TEST_RETAIN
/**
* Tested retain - only invoke the
* objective-c method if the receiver is not nil.<br />
* Does nothing when ARC is in use.
*/
#define TEST_RETAIN(object) ({\
void *__object = (void*)(object);\
(__object != 0) ? [(id)__object retain] : nil; })
#endif
#ifndef TEST_RELEASE
/**
* Tested release - only invoke the
* objective-c method if the receiver is not nil.<br />
* Does nothing when ARC is in use.
*/
#define TEST_RELEASE(object) ({\
void *__object = (void*)(object);\
if (__object != 0) [(id)__object release]; })
#endif
#ifndef TEST_AUTORELEASE
/**
* Tested autorelease - only invoke the
* objective-c method if the receiver is not nil.<br />
* Does nothing when ARC is in use.
*/
#define TEST_AUTORELEASE(object) ({\
void *__object = (void*)(object);\
(__object != 0) ? [(id)__object autorelease] : nil; })
#endif
#ifndef ASSIGN
/**
* ASSIGN(object,value) assigns the value to the object with
* appropriate retain and release operations.<br />
* Use this to avoid retain/release errors.
*/
#define ASSIGN(object,value) ({\
void *__object = (void*)object; \
object = (__typeof__(object))[(value) retain]; \
[(id)__object release]; \
})
#endif
#ifndef ASSIGNCOPY
/**
* ASSIGNCOPY(object,value) assigns a copy of the value to the object
* with release of the original.<br />
* Use this to avoid retain/release errors.
*/
#define ASSIGNCOPY(object,value) ({\
void *__object = (void*)object; \
object = (__typeof__(object))[(value) copy];\
[(id)__object release]; \
})
#endif
#ifndef ASSIGNMUTABLECOPY
/**
* ASSIGNMUTABLECOPY(object,value) assigns a mutable copy of the value
* to the object with release of the original.<br />
* Use this to avoid retain/release errors.
*/
#define ASSIGNMUTABLECOPY(object,value) ({\
void *__object = (void*)object; \
object = (__typeof__(object))[(value) mutableCopy];\
[(id)__object release]; \
})
#endif
#ifndef DESTROY
/**
* DESTROY() is a release operation which also sets the variable to be
* a nil pointer for tidiness - we can't accidentally use a DESTROYED
* object later. It also makes sure to set the variable to nil before
* releasing the object - to avoid side-effects of the release trying
* to reference the object being released through the variable.
*/
#define DESTROY(object) ({ \
void *__o = (void*)object; \
object = nil; \
[(id)__o release]; \
})
#endif
#ifndef DEALLOC
/**
* DEALLOC calls the superclass implementation of dealloc, unless
* ARC is in use (in which case it does nothing).
*/
#define DEALLOC [super dealloc];
#endif
#ifndef ENTER_POOL
/**
* ENTER_POOL creates an autorelease pool and places subsequent code
* in a block.<br />
* The block must be terminated with a corresponding LEAVE_POOL.<br />
* You should not break, continue, or return from such a block of code
* (to do so could leak an autorelease pool and give objects a longer
* lifetime than they ought to have. If you wish to leave the block of
* code early, you should ensure that doing so causes the autorelease
* pool outside the block to be released promptly (since that will
* implicitly release the pool created at the start of the block too).
*/
#define ENTER_POOL {NSAutoreleasePool *_lARP=[NSAutoreleasePool new];
#endif
#ifndef LEAVE_POOL
/**
* LEAVE_POOL terminates a block of code started with ENTER_POOL.
*/
#define LEAVE_POOL [_lARP drain];}
#endif
#ifndef __has_feature // Optional.
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif
#ifndef NS_CONSUMED
#if __has_feature(attribute_ns_consumed)
#define NS_CONSUMED __attribute__((ns_consumed))
#else
#define NS_CONSUMED
#endif
#endif
#endif

165
GSCache.h
View file

@ -7,16 +7,16 @@
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
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
version 2 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.
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
You should have received a copy of the GNU Library 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.
@ -26,17 +26,14 @@
#ifndef INCLUDED_GSCache_H
#define INCLUDED_GSCache_H
#import <Foundation/NSObject.h>
#include <Foundation/NSObject.h>
#include <Foundation/NSArray.h>
@class NSArray;
@class NSDate;
@class NSDate;
@class NSMutableSet;
@class NSString;
/**
* The GSCache class is used to maintain a cache of objects in memory
* for relatively rapid access.<br />
* for relatiovely rapid access.<br />
* Typical usage might be to keep the results of a database query around
* for a while in order to re-use them ... for instance when application
* configuration is obtained from a database which might be updated while
@ -50,19 +47,7 @@
* Objects stored in the cache may be given a limited lifetime,
* in which case an attempt to fetch an <em>expired</em> object
* from the cache will cause it to be removed from the cache instead
* (subject to control by the delegate).<br />
* Cache keys may be objects of any type as long as they are copyable
* (and the copied keys are immutable) and implement the -hash and
* -isEqual: methods such that any two keys can be tested for equality
* and used as dictionary keys.<br />
* For object sizing we use the -sizeInBytesExcluding: method, which is
* declared in the GNUstep-base additions library headers as follows:<br />
* - (NSUInteger) sizeInBytesExcluding: (NSHashTable*)exclude;<br />
* If you wish to store objects in a size-limited cache, you should
* implement that method to return an appropriate size for the object
* you are caching.<br />
* NB. GSCache currently does not support subclassing ... use it as is
* or extend it via categories, but do not try to add instance variables.
* (subject to control by the delegate).
*/
@interface GSCache : NSObject
{
@ -86,14 +71,9 @@
- (unsigned) currentObjects;
/**
* Return the total size of the objects currently in the cache.<br />
* NB. Object sizes are considered independently ... so where cached
* objects are containers with common content, the size of the cache
* may appear larger than is actually used.<br />
* Also, this figure does not consider memmory used by the cache itself
* or by the keys, only the memory used by the objects cached.
* Return the total size of the objects currently in the cache.
*/
- (NSUInteger) currentSize;
- (unsigned) currentSize;
/**
* Return the delegate object previously set using the -setDelegate: method.
@ -118,13 +98,13 @@
- (unsigned) maxObjects;
/**
* Return the maximum total size of items in the cache.<br />
* Return the maximum tital size of items in the cache.<br />
* A value of zero means there is no limit.
*/
- (NSUInteger) maxSize;
- (unsigned) maxSize;
/**
* Return the name of this instance (as set using -setName:forConfiguration:)
* Return the name of this instance (as set using -setName:)
*/
- (NSString*) name;
@ -132,7 +112,7 @@
* Return the cached value for the specified key, or nil if there
* is no value in the cache.
*/
- (id) objectForKey: (id)aKey;
- (id) objectForKey: (NSString*)aKey;
/**
* Remove all items whose lifetimes have passed
@ -140,32 +120,20 @@
*/
- (void) purge;
/**
* Similar to -setObject:forKey:lifetime: but, if there is an existing
* object in the cache which -isEqual: to anObject (or is anObject is nil),
* the existing object is retained in the cache (though its lifetime is
* updated/refreshed).<br />
* The value of the object in the cache is returned.
*/
- (id) refreshObject: (id)anObject
forKey: (id)aKey
lifetime: (unsigned)lifetime;
/**
* Sets the delegate for the receiver.<br />
* The delegate object is not retained.<br />
* If a delegate it set, it will be sent the messages in the
* (GSCacheDelegate) protocol (if it implements them ... which
* it does not need to do).
* If a delegate it set, it must implement the methods in the
* (GSCacheDelegate) protocol.
*/
- (void) setDelegate: (id)anObject;
/**
* Sets the default lifetime (seconds) for items added to the cache.
* If this is set to zero then items are not removed from the cache
* based on lifetimes when the cache is full and an object is added,
* though <em>expired</em> items are still removed when an attempt to
* retrieve them is made.
* Sets the lifetime (seconds) for items added to the cache. If this
* is set to zero then items are not removed from the cache based on
* lifetimes when the cache is full and an object is added, though
* <em>expired</em> items are still removed when an attempt to retrieve
* them is made.
*/
- (void) setLifetime: (unsigned)max;
@ -181,58 +149,27 @@
* then an attempt to set an object whose size would exceed the cache limit
* will result in the least recently used items in the cache being removed.
*/
- (void) setMaxSize: (NSUInteger)max;
- (void) setMaxSize: (unsigned)max;
/**
* Sets the name of this instance and whether the instance is to be
* configured using information from the user defaults system.<br />
* If useDefaults is YES, values from the user defaults system will be
* used to override the -setLifetime: -setMaxObjects: and -setMaxSize:
* methods.<br />
* The defaults keys for the configurationm are GSCacheLifetimeX,
* GSCacheMaxObjectsX and GSCacheMaxSizeX where X is the name of the
* cache being configured (an empty string for caches with no name).
*/
- (void) setName: (NSString*)name forConfiguration: (BOOL)useDefaults;
/**
* Calls -setName:forConfiguration: to have the receiver configured
* by calling configuration methods rather than by using the defaults
* system.
* Sets the name of this instance.
*/
- (void) setName: (NSString*)name;
/**
* Sets (or replaces) the cached value for the specified key.<br />
* The value of anObject may be nil to remove any cached object
* for aKey.
* Sets (or replaces)the cached value for the specified key.
*/
- (void) setObject: (id)anObject forKey: (id)aKey;
- (void) setObject: (id)anObject forKey: (NSString*)aKey;
/**
* Sets (or replaces) the cached value for the specified key, giving
* Sets (or replaces)the cached value for the specified key, giving
* the value the specified lifetime (in seconds). A lifetime of zero
* means that the item is not limited by lifetime.<br />
* The value of anObject may be nil to remove any cached object
* for aKey.
* means that the item is not limited by lifetime.
*/
- (void) setObject: (id)anObject
forKey: (id)aKey
forKey: (NSString*)aKey
lifetime: (unsigned)lifetime;
/**
* Sets (or replaces) the cached value for the specified key, giving
* the value the specified expiry date. Calls -setObject:forKey:lifetime:
* to do the real work ... this is just a convenience method to
* handle working out the lifetime in seconds.<br />
* If expires is nil or not in the future, this method simply removes the
* cache entry for aKey. If it is many years in the future, the item is
* set in the cache so that it is not limited by lifetime.
*/
- (void) setObject: (id)anObject
forKey: (id)aKey
until: (NSDate*)expires;
/**
* Called by -setObject:forKey:lifetime: to make space for a new
* object in the cache (also when the cache is resized).<br />
@ -245,33 +182,15 @@
* The size argument is used <em>only</em> if a maximum size is set
* for the cache.
*/
- (void) shrinkObjects: (unsigned)objects andSize: (NSUInteger)size;
- (void) shrinkObjects: (unsigned)objects andSize: (unsigned)size;
@end
/**
* This protocol defines the messages which may be sent to a delegate
* of a GSCache object. The messages are only sent if the delegate
* actually implements them, so a delegate does not need to actually
* conform to the protocol.
* of a GSCache object.
*/
@protocol GSCacheDelegate
/**
* Alerts the delegate to the fact that anObject, which was cached
* using aKey and will expire delay seconds in the future has been
* looked up now, and needs to be refreshed if it is not to expire
* from the cache.<br />
* This is called the first time an attempt is made to access the
* cached value for aKey and the object is found in the cache but
* more than half its lifetime has expired.<br />
* The delegate method (if implemented) may replace the item in the
* cache immediately, or do it later asynchronously, or may simply
* take no action.
*/
- (void) mayRefreshItem: (id)anObject
withKey: (id)aKey
lifetime: (unsigned)lifetime
after: (unsigned)delay;
/**
* Asks the delegate to decide whether anObject, which was cached
* using aKey and expired delay seconds ago should still be retained
@ -287,16 +206,30 @@
* when its lifetime has expired.<br />
* Another possibility would be for the delegate to return YES (in order
* to continue using the existing object) and queue an asynchronous
* database query to update the cache later. In this case the expiry
* time of the item will be reset relative to the current time, based
* upon its original lifetime.
* database query to update the cache later.
*/
- (BOOL) shouldKeepItem: (id)anObject
withKey: (id)aKey
lifetime: (unsigned)lifetime
withKey: (NSString*)aKey
after: (unsigned)delay;
@end
@interface NSArray (SizeInBytes)
- (unsigned) sizeInBytes: (NSMutableSet*)exclude;
@end
@interface NSData (SizeInBytes)
- (unsigned) sizeInBytes: (NSMutableSet*)exclude;
@end
@interface NSObject (SizeInBytes)
- (unsigned) sizeInBytes: (NSMutableSet*)exclude;
@end
@interface NSString (SizeInBytes)
- (unsigned) sizeInBytes: (NSMutableSet*)exclude;
@end
#endif

676
GSCache.m
View file

@ -9,128 +9,92 @@
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
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
version 2 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.
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
You should have received a copy of the GNU Library 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.
$Date$ $Revision$
*/
#include <inttypes.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSString.h>
#include <Foundation/NSData.h>
#include <Foundation/NSDate.h>
#include <Foundation/NSException.h>
#include <Foundation/NSNotification.h>
#include <Foundation/NSHashTable.h>
#include <Foundation/NSMapTable.h>
#include <Foundation/NSLock.h>
#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSValue.h>
#include <Foundation/NSDebug.h>
#include <Foundation/NSSet.h>
#include <Foundation/NSTimer.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSData.h>
#import <Foundation/NSDate.h>
#import <Foundation/NSDebug.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSException.h>
#import <Foundation/NSHashTable.h>
#import <Foundation/NSLock.h>
#import <Foundation/NSMapTable.h>
#import <Foundation/NSNotification.h>
#import <Foundation/NSString.h>
#import <Foundation/NSThread.h>
#import <Foundation/NSUserDefaults.h>
#import <Foundation/NSValue.h>
#import "GSCache.h"
#import "GSTicker.h"
#if !defined(GNUSTEP)
#include <objc/objc-class.h>
#if (MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4)
#define class_getInstanceSize(isa) ((struct objc_class *)isa)->instance_size
#endif
#import "NSObject+GSExtensions.h"
#endif
@interface GSCache (Private)
- (void) _useDefaults: (NSNotification*)n;
@end
#include "GSCache.h"
#include "GSTicker.h"
@interface GSCacheItem : NSObject
{
@public
GSCacheItem *next;
GSCacheItem *prev;
unsigned life;
unsigned warn;
unsigned when;
NSUInteger size;
id key;
unsigned size;
NSString *key;
id object;
}
+ (GSCacheItem*) newWithObject: (id)anObject forKey: (id)aKey;
+ (GSCacheItem*) newWithObject: (id)anObject forKey: (NSString*)aKey;
@end
@implementation GSCacheItem
+ (GSCacheItem*) newWithObject: (id)anObject forKey: (id)aKey
+ (GSCacheItem*) newWithObject: (id)anObject forKey: (NSString*)aKey
{
GSCacheItem *i;
i = (GSCacheItem*)NSAllocateObject(self, 0, NSDefaultMallocZone());
i->object = [anObject retain];
i->object = RETAIN(anObject);
i->key = [aKey copy];
return i;
}
- (void) dealloc
{
[key release];
[object release];
[super dealloc];
}
- (NSUInteger) sizeInBytesExcluding: (NSHashTable*)exclude
{
NSUInteger bytes = [super sizeInBytesExcluding: exclude];
if (bytes > 0)
{
bytes += [key sizeInBytesExcluding: exclude];
bytes += [object sizeInBytesExcluding: exclude];
}
return bytes;
RELEASE(key);
RELEASE(object);
NSDeallocateObject(self);
}
@end
@implementation GSCache
static NSHashTable *allCaches = 0;
static NSRecursiveLock *allCachesLock = nil;
static int itemOffset = 0;
static NSHashTable *GSCacheInstances = 0;
static NSLock *GSCacheLock = nil;
typedef struct {
id delegate;
void (*refresh)(id, SEL, id, id, unsigned, unsigned);
BOOL (*replace)(id, SEL, id, id, unsigned, unsigned);
unsigned currentObjects;
NSUInteger currentSize;
unsigned currentSize;
unsigned lifetime;
unsigned maxObjects;
NSUInteger maxSize;
unsigned maxSize;
unsigned hits;
unsigned misses;
NSMapTable *contents;
GSCacheItem *first;
NSString *name;
NSHashTable *exclude;
NSRecursiveLock *lock;
BOOL useDefaults;
NSMutableSet *exclude;
} Item;
#define my ((Item*)((void*)self + itemOffset))
#define my ((Item*)&self[1])
/*
* Add item to linked list starting at *first
@ -176,9 +140,9 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
{
NSArray *a;
[allCachesLock lock];
a = NSAllHashTableObjects(allCaches);
[allCachesLock unlock];
[GSCacheLock lock];
a = NSAllHashTableObjects(GSCacheInstances);
[GSCacheLock unlock];
return a;
}
@ -192,6 +156,9 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
GSCache *c;
c = (GSCache*)NSAllocateObject(self, sizeof(Item), z);
[GSCacheLock lock];
NSHashInsert(GSCacheInstances, (void*)c);
[GSCacheLock unlock];
return c;
}
@ -202,23 +169,24 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
GSCache *c;
ms = [NSMutableString stringWithString: [super description]];
[allCachesLock lock];
e = NSEnumerateHashTable(allCaches);
[GSCacheLock lock];
e = NSEnumerateHashTable(GSCacheInstances);
while ((c = (GSCache*)NSNextHashEnumeratorItem(&e)) != nil)
{
[ms appendFormat: @"\n%@", [c description]];
}
NSEndHashTableEnumeration(&e);
[allCachesLock unlock];
[GSCacheLock unlock];
return ms;
}
+ (void) initialize
{
if (allCaches == 0)
if (GSCacheInstances == 0)
{
itemOffset = class_getInstanceSize(self);
allCaches = NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0);
GSCacheLock = [NSLock new];
GSCacheInstances
= NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0);
GSTickerTimeNow();
}
}
@ -228,27 +196,24 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
return my->currentObjects;
}
- (NSUInteger) currentSize
- (unsigned) currentSize
{
return my->currentSize;
}
- (void) dealloc
{
if (my->useDefaults)
{
[[NSNotificationCenter defaultCenter] removeObserver: self
name: NSUserDefaultsDidChangeNotification object: nil];
}
[GSCacheLock lock];
if (my->contents != 0)
{
[self shrinkObjects: 0 andSize: 0];
NSFreeMapTable(my->contents);
}
[my->exclude release];
[my->name release];
[my->lock release];
[super dealloc];
RELEASE(my->exclude);
RELEASE(my->name);
NSHashRemove(GSCacheInstances, (void*)self);
NSDeallocateObject(self);
[GSCacheLock unlock];
}
- (id) delegate
@ -258,18 +223,16 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
- (NSString*) description
{
NSString *n;
NSString *n = my->name;
[my->lock lock];
n = my->name;
if (n == nil)
{
n = [super description];
}
n = [NSString stringWithFormat:
return [NSString stringWithFormat:
@" %@\n"
@" Items: %u(%u)\n"
@" Size: %"PRIuPTR"(%"PRIuPTR")\n"
@" Size: %u(%u)\n"
@" Life: %u\n"
@" Hit: %u\n"
@" Miss: %u\n",
@ -279,21 +242,12 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
my->lifetime,
my->hits,
my->misses];
[my->lock unlock];
return n;
}
- (id) init
{
if (nil != (self = [super init]))
{
my->lock = [NSRecursiveLock new];
my->contents = NSCreateMapTable(NSObjectMapKeyCallBacks,
NSObjectMapValueCallBacks, 0);
[allCachesLock lock];
NSHashInsert(allCaches, (void*)self);
[allCachesLock unlock];
}
my->contents = NSCreateMapTable(NSObjectMapKeyCallBacks,
NSObjectMapValueCallBacks, 0);
return self;
}
@ -307,89 +261,42 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
return my->maxObjects;
}
- (NSUInteger) maxSize
- (unsigned) maxSize
{
return my->maxSize;
}
- (NSString*) name
{
NSString *n;
[my->lock lock];
n = [my->name retain];
[my->lock unlock];
return [n autorelease];
return my->name;
}
- (id) objectForKey: (id)aKey
- (id) objectForKey: (NSString*)aKey
{
id object;
GSCacheItem *item;
unsigned when = GSTickerTimeTick();
[my->lock lock];
item = (GSCacheItem*)NSMapGet(my->contents, aKey);
if (item == nil)
{
my->misses++;
[my->lock unlock];
return nil;
}
if (item->when > 0 && item->when < when)
{
BOOL keep = NO;
if (0 != my->replace)
if ([my->delegate shouldKeepItem: item->object
withKey: aKey
after: when - item->when] == YES)
{
GSCacheItem *orig = [item retain];
[my->lock unlock];
keep = (*(my->replace))(my->delegate,
@selector(shouldKeepItem:withKey:lifetime:after:),
item->object,
aKey,
item->life,
when - item->when);
[my->lock lock];
if (keep == YES)
// Refetch in case delegate changed it.
item = (GSCacheItem*)NSMapGet(my->contents, aKey);
if (item == nil)
{
GSCacheItem *current;
/* Refetch in case delegate changed it.
*/
current = (GSCacheItem*)NSMapGet(my->contents, aKey);
if (current == nil)
{
/* Delegate must have deleted the item even though
* it returned YES to say we should keep it ...
* we count this as a miss.
*/
my->misses++;
[my->lock unlock];
[orig release];
return nil;
}
else if (orig == current)
{
/* Delegate told us to keep the original item so we
* update its expiry time.
*/
item->when = when + item->life;
item->warn = when + item->life / 2;
}
else
{
/* Delegate replaced the item with another and told
* us to keep that one.
*/
item = current;
}
my->misses++;
return nil;
}
[orig release];
}
if (keep == NO)
else
{
removeItem(item, &my->first);
my->currentObjects--;
@ -399,67 +306,27 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
}
NSMapRemove(my->contents, (void*)item->key);
my->misses++;
[my->lock unlock];
return nil; // Lifetime expired.
}
}
else if (item->warn > 0 && item->warn < when)
{
item->warn = 0; // Don't warn again.
if (0 != my->refresh)
{
GSCacheItem *orig = [item retain];
GSCacheItem *current;
[my->lock unlock];
(*(my->refresh))(my->delegate,
@selector(mayRefreshItem:withKey:lifetime:after:),
item->object,
aKey,
item->life,
when - item->when);
[my->lock lock];
/* Refetch in case delegate changed it.
*/
current = (GSCacheItem*)NSMapGet(my->contents, aKey);
if (current == nil)
{
/* Delegate must have deleted the item!
* So we count this as a miss.
*/
my->misses++;
[my->lock unlock];
[orig release];
return nil;
}
else
{
item = current;
}
[orig release];
}
}
// Least recently used ... move to end of list.
removeItem(item, &my->first);
appendItem(item, &my->first);
my->hits++;
object = [item->object retain];
[my->lock unlock];
return [object autorelease];
return item->object;
}
- (void) purge
{
unsigned when = GSTickerTimeTick();
if (my->contents != 0)
{
unsigned when = GSTickerTimeTick();
NSMapEnumerator e;
GSCacheItem *i;
id k;
GSCacheItem *i;
NSString *k;
[my->lock lock];
e = NSEnumerateMapTable(my->contents);
while (NSNextMapEnumeratorPair(&e, (void**)&k, (void**)&i) != 0)
{
@ -475,185 +342,62 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
}
}
NSEndMapTableEnumeration(&e);
[my->lock unlock];
}
}
- (oneway void) release
{
/* We lock the table while checking, to prevent
* another thread from grabbing this object while we are
* checking it.
* If we are going to deallocate the object, we first remove
* it from the table so that no other thread will find it
* and try to use it while it is being deallocated.
*/
[allCachesLock lock];
if ([self retainCount] == 1
&& NSHashGet(allCaches, (void*)self) == self)
{
NSHashRemove(allCaches, (void*)self);
}
[super release];
[allCachesLock unlock];
}
- (id) refreshObject: (id)anObject
forKey: (id)aKey
lifetime: (unsigned)lifetime
{
id object;
GSCacheItem *item;
[my->lock lock];
item = (GSCacheItem*)NSMapGet(my->contents, aKey);
if (item == nil)
{
if (nil != anObject)
{
[self setObject: anObject
forKey: aKey
lifetime: lifetime];
}
[my->lock unlock];
return anObject;
}
if (nil != anObject && NO == [anObject isEqual: item->object])
{
object = [anObject retain];
[item->object release];
item->object = object;
}
if (lifetime > 0)
{
unsigned tick = GSTickerTimeTick();
item->when = tick + lifetime;
item->warn = tick + lifetime / 2;
}
item->life = lifetime;
object = [[item->object retain] autorelease];
[my->lock unlock];
return object;
}
- (void) setDelegate: (id)anObject
{
[my->lock lock];
my->delegate = anObject;
if ([my->delegate respondsToSelector:
@selector(shouldKeepItem:withKey:lifetime:after:)])
{
my->replace = (BOOL (*)(id,SEL,id,id,unsigned,unsigned))
[my->delegate methodForSelector:
@selector(shouldKeepItem:withKey:lifetime:after:)];
}
else
{
my->replace = 0;
}
if ([my->delegate respondsToSelector:
@selector(mayRefreshItem:withKey:lifetime:after:)])
{
my->refresh = (void (*)(id,SEL,id,id,unsigned,unsigned))
[my->delegate methodForSelector:
@selector(mayRefreshItem:withKey:lifetime:after:)];
}
else
{
my->refresh = 0;
}
[my->lock unlock];
}
- (void) setLifetime: (unsigned)max
{
[my->lock lock];
if (YES == my->useDefaults)
{
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
NSString *n = (nil == my->name) ? @"" : my->name;
NSString *k = [@"GSCacheLifetime" stringByAppendingString: n];
if (nil != [defs objectForKey: k])
{
max = (unsigned) [defs integerForKey: k];
}
}
my->lifetime = max;
[my->lock unlock];
}
- (void) setMaxObjects: (unsigned)max
{
[my->lock lock];
if (YES == my->useDefaults)
{
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
NSString *n = (nil == my->name) ? @"" : my->name;
NSString *k = [@"GSCacheMaxObjects" stringByAppendingString: n];
if (nil != [defs objectForKey: k])
{
max = (unsigned) [defs integerForKey: k];
}
}
my->maxObjects = max;
if (my->currentObjects > my->maxObjects)
{
[self shrinkObjects: my->maxObjects
andSize: my->maxSize];
andSize: my->maxSize];
}
[my->lock unlock];
}
- (void) setMaxSize: (NSUInteger)max
- (void) setMaxSize: (unsigned)max
{
[my->lock lock];
if (YES == my->useDefaults)
{
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
NSString *n = (nil == my->name) ? @"" : my->name;
NSString *k = [@"GSCacheMaxSize" stringByAppendingString: n];
if (nil != [defs objectForKey: k])
{
max = (NSUInteger) [defs integerForKey: k];
}
}
if (max > 0 && my->maxSize == 0)
{
NSMapEnumerator e = NSEnumerateMapTable(my->contents);
GSCacheItem *i;
id k;
NSUInteger size = 0;
GSCacheItem *i;
NSString *k;
unsigned size = 0;
if (nil == my->exclude)
{
my->exclude
= NSCreateHashTable(NSNonOwnedPointerHashCallBacks, 0);
}
if (my->exclude == nil)
{
my->exclude = [NSMutableSet new];
}
while (NSNextMapEnumeratorPair(&e, (void**)&k, (void**)&i) != 0)
{
if (i->size == 0)
{
i->size = [i->object sizeInBytesExcluding: my->exclude];
[my->exclude removeAllObjects];
}
if (i->size > max)
{
/*
* Item in cache is too big for new size limit ...
* Remove it.
*/
removeItem(i, &my->first);
NSMapRemove(my->contents, (void*)i->key);
my->currentObjects--;
continue;
}
size += i->size;
}
{
if (i->size == 0)
{
[my->exclude removeAllObjects];
i->size = [i->object sizeInBytes: my->exclude];
}
if (i->size > max)
{
/*
* Item in cache is too big for new size limit ...
* Remove it.
*/
removeItem(i, &my->first);
NSMapRemove(my->contents, (void*)i->key);
my->currentObjects--;
continue;
}
size += i->size;
}
NSEndMapTableEnumeration(&e);
my->currentSize = size;
}
@ -665,69 +409,30 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
if (my->currentSize > my->maxSize)
{
[self shrinkObjects: my->maxObjects
andSize: my->maxSize];
andSize: my->maxSize];
}
[my->lock unlock];
}
- (void) setName: (NSString*)name forConfiguration: (BOOL)useDefaults
{
NSString *c;
[my->lock lock];
c = [name copy];
[my->name release];
my->name = c;
useDefaults = (useDefaults ? YES : NO); // Make sure this is a real bool
if (my->useDefaults != useDefaults)
{
if (my->useDefaults)
{
[[NSNotificationCenter defaultCenter] removeObserver: self
name: NSUserDefaultsDidChangeNotification object: nil];
}
my->useDefaults = useDefaults;
if (my->useDefaults)
{
[[NSNotificationCenter defaultCenter]
addObserver: self
selector: @selector(_useDefaults:)
name: NSUserDefaultsDidChangeNotification
object: nil];
[self _useDefaults: nil];
}
}
[my->lock unlock];
}
- (void) setName: (NSString*)name
{
[self setName: name forConfiguration: NO];
ASSIGN(my->name, name);
}
- (void) setObject: (id)anObject forKey: (id)aKey
- (void) setObject: (id)anObject forKey: (NSString*)aKey
{
[self setObject: anObject forKey: aKey lifetime: my->lifetime];
}
- (void) setObject: (id)anObject
forKey: (id)aKey
forKey: (NSString*)aKey
lifetime: (unsigned)lifetime
{
GSCacheItem *item;
unsigned maxObjects;
NSUInteger maxSize;
unsigned maxObjects = my->maxObjects;
unsigned maxSize = my->maxSize;
unsigned addObjects = (anObject == nil ? 0 : 1);
NSUInteger addSize = 0;
unsigned addSize = 0;
if (aKey == nil)
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to add nil key to cache"];
}
[my->lock lock];
maxObjects = my->maxObjects;
maxSize = my->maxSize;
item = (GSCacheItem*)NSMapGet(my->contents, aKey);
if (item != nil)
{
@ -740,20 +445,19 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
NSMapRemove(my->contents, (void*)aKey);
}
if (addObjects > 0 && (maxSize > 0 || maxObjects > 0))
if (maxSize > 0 || maxObjects > 0)
{
if (maxSize > 0)
{
if (nil == my->exclude)
{
my->exclude
= NSCreateHashTable(NSNonOwnedPointerHashCallBacks, 0);
}
addSize = [anObject sizeInBytesExcluding: my->exclude];
if (my->exclude == nil)
{
my->exclude = [NSMutableSet new];
}
[my->exclude removeAllObjects];
addSize = [anObject sizeInBytes: my->exclude];
if (addSize > maxSize)
{
addObjects = 0; // Object too big to cache.
return; // Object too big to cache.
}
}
}
@ -768,60 +472,22 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
item = [GSCacheItem newWithObject: anObject forKey: aKey];
if (lifetime > 0)
{
unsigned tick = GSTickerTimeTick();
item->when = tick + lifetime;
item->warn = tick + lifetime / 2;
item->when = GSTickerTimeTick() + lifetime;
}
item->life = lifetime;
item->size = addSize;
NSMapInsert(my->contents, (void*)item->key, (void*)item);
appendItem(item, &my->first);
my->currentObjects += addObjects;
my->currentSize += addSize;
[item release];
}
[my->lock unlock];
}
- (void) setObject: (id)anObject
forKey: (id)aKey
until: (NSDate*)expires
{
NSTimeInterval i;
i = (expires == nil) ? 0.0 : [expires timeIntervalSinceReferenceDate];
i -= GSTickerTimeNow();
if (i <= 0.0)
{
[self setObject: nil forKey: aKey]; // Already expired
}
else
{
unsigned limit;
if (i > 2415919103.0)
{
limit = 0; // Limit in far future.
}
else
{
limit = (unsigned)i;
}
[self setObject: anObject
forKey: aKey
lifetime: limit];
RELEASE(item);
}
}
- (void) shrinkObjects: (unsigned)objects andSize: (NSUInteger)size
- (void) shrinkObjects: (unsigned)objects andSize: (unsigned)size
{
NSUInteger newSize;
unsigned newObjects;
unsigned newSize = [self currentSize];
unsigned newObjects = [self currentObjects];
[my->lock lock];
newSize = [self currentSize];
newObjects = [self currentObjects];
if (newObjects > objects || (my->maxSize > 0 && newSize > size))
{
[self purge];
@ -843,68 +509,56 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
my->currentObjects = newObjects;
my->currentSize = newSize;
}
[my->lock unlock];
}
- (NSUInteger) sizeInBytesExcluding: (NSHashTable*)exclude
{
NSUInteger size;
[my->lock lock];
size = [super sizeInBytesExcluding: exclude];
if (size > 0)
{
size += sizeof(Item)
+ [my->contents sizeInBytesExcluding: exclude]
+ [my->exclude sizeInBytesExcluding: exclude]
+ [my->name sizeInBytesExcluding: exclude]
+ [my->lock sizeInBytesExcluding: exclude];
}
[my->lock unlock];
return size;
}
@end
@implementation GSCache (Private)
- (void) _useDefaults: (NSNotification*)n
{
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
NSString *conf = (nil == my->name ? @"" : my->name);
NSString *key;
[my->lock lock];
NS_DURING
@implementation NSArray (SizeInBytes)
- (unsigned) sizeInBytes: (NSMutableSet*)exclude
{
if ([exclude member: self] != nil)
{
if (YES == my->useDefaults)
return 0;
}
else
{
unsigned count = [self count];
unsigned size = [super sizeInBytes: exclude] + count*sizeof(void*);
if (exclude == nil)
{
my->useDefaults = NO;
key = [@"GSCacheLifetime" stringByAppendingString: conf];
if (nil != [defs objectForKey: key])
{
[self setLifetime: (unsigned)[defs integerForKey: key]];
}
key = [@"GSCacheMaxObjects" stringByAppendingString: conf];
if (nil != [defs objectForKey: key])
{
[self setMaxObjects: (unsigned)[defs integerForKey: key]];
}
key = [@"GSCacheMaxSize" stringByAppendingString: conf];
if (nil != [defs objectForKey: key])
{
[self setMaxSize: (NSUInteger)[defs integerForKey: key]];
}
my->useDefaults = YES;
exclude = [NSMutableSet setWithCapacity: 8];
}
[my->lock unlock];
[exclude addObject: self];
while (count-- > 0)
{
size += [[self objectAtIndex: count] sizeInBytes: exclude];
}
return size;
}
NS_HANDLER
{
my->useDefaults = YES;
[my->lock unlock];
[localException raise];
}
NS_ENDHANDLER
}
@end
@implementation NSData (SizeInBytes)
- (unsigned) sizeInBytes: (NSMutableSet*)exclude
{
if ([exclude member: self] != nil) return 0;
return [super sizeInBytes: exclude] + [self length];
}
@end
@implementation NSObject (SizeInBytes)
- (unsigned) sizeInBytes: (NSMutableSet*)exclude
{
if ([exclude member: self] != nil) return 0;
return isa->instance_size;
}
@end
@implementation NSString (SizeInBytes)
- (unsigned) sizeInBytes: (NSMutableSet*)exclude
{
if ([exclude member: self] != nil) return 0;
return [super sizeInBytes: exclude] + sizeof(unichar) * [self length];
}
@end

421
GSFIFO.h
View file

@ -1,421 +0,0 @@
#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>
#import <Foundation/NSDate.h>
#if !defined (GNUSTEP)
#import "GNUstep.h"
#endif
@class NSArray;
@class NSCondition;
@class NSNumber;
@class NSString;
@class NSThread;
/** GSFIFO manages a first-in-first-out queue of items.<br />
* Items in the queue are <em>NOT</em> retained objects ... 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 processing
* 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 producer and a single consumer thread, a fast
* lock-free algorthm is used to get/pu items from the FIFO.<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
* operating that way, the overhead of using the FIFO is only a few CPU cycles
* 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
* process to manage, but some parts need to be sequential ... in these
* cases it may make sense to have multiple consumers and/or producers.
* In these cases, some locking is required and the use of the inline
* functions is not allowed (you must call the -get and -put: methods.<br />
* It is recommended that you create FIFOs using the -initWithName: method
* so that you can easily use the NSUserDefaults system to adjust their
* configurations to tests/tweak performance.<br />
* While a FIFO fundamentally works on abstract items without memory
* management, the API provides methods for handling NSObject values
* threating the FIFO as a container which retains the objects until
* they are removed from the FIFO.
*/
@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 access them directly in your own code.
*/
volatile uint64_t _head;
volatile uint64_t _tail;
uint64_t _getTryFailure;
uint64_t _getTrySuccess;
uint64_t _putTryFailure;
uint64_t _putTrySuccess;
void **_items;
uint32_t _capacity;
@private
uint32_t boundsCount;
uint16_t granularity;
uint16_t timeout;
uint64_t fullCount; // Total waits for full FIFO
uint64_t emptyCount; // Total waits for empty FIFO
NSCondition *condition;
NSString *name;
NSTimeInterval getWaitTotal; // Total time waiting for gets
NSTimeInterval putWaitTotal; // Total time waiting for puts
NSTimeInterval *waitBoundaries; // Stats boundaries
uint64_t *getWaitCounts; // Waits for gets by time
uint64_t *putWaitCounts; // Waits for puts by time
NSThread *getThread; // Single consumer thread
NSThread *putThread; // Single producer thread
}
/** Return statistics for all current GSFIFO instances.<br />
* Statistics for FIFOs which are configued to be lock-free are empty
* (listing the name only) except where we can safely obtain get or put
* stats because the FIFOs consumer/producer thread is the same as the
* current thread.
*/
+ (NSString*) stats;
/** Returns the approximate number of items in the FIFO.
*/
- (NSUInteger) count;
/** Reads up to count items from the FIFO into buf.
* If block is YES, this blocks if necessary until at least one item
* is available, and raises an exception if the FIFO is configured
* with a timeout and it is exceeded.<br />
* Returns the number of items actually read.
*/
- (unsigned) get: (void**)buf count: (unsigned)count shouldBlock: (BOOL)block;
/**
* Reads up to count items from the FIFO into the buf. If blocking is requested
* and a before date is specified, the operation blocks until the specified time
* and returns 0 if it could not read any items. The timeout configured for the
* FIFO still takes precedence.
*/
- (unsigned) get: (void**)buf
count: (unsigned)count
shouldBlock: (BOOL)block
before: (NSDate*)date;
/** Reads up to count objects from the FIFO (which must contain objects
* or nil items) into buf and autoreleases them.<br />
* If block is YES, this blocks if necessary until at least one object
* is available, and raises an exception if the FIFO is configured
* with a timeout and it is exceeded.<br />
* Returns the number of object actually read.
*/
- (unsigned) getObjects: (NSObject**)buf
count: (unsigned)count
shouldBlock: (BOOL)block;
/**
* Reads up to count autoreleased objects from the FIFO into the buf.
* If blocking
* is requested and a before date is specified, the operation blocks until the
* specified time and returns 0 if it could not read any items. The timeout
* configured for the FIFO still takes precedence.
*/
- (unsigned) getObjects: (NSObject**)buf
count: (unsigned)count
shouldBlock: (BOOL)block
before: (NSDate*)date;
/** Gets the next item from the FIFO, blocking if necessary until an
* item is available. Raises an exception if the FIFO is configured
* with a timeout and it is exceeded.<br />
* Implemented using -get:count:shouldBlock:
*/
- (void*) get;
/** Gets the next object from the FIFO (which must contain objects or
* nil items), blocking if necessary until an object is available.
* Autoreleases the object before returning it.<br />
* Raises an exception if the FIFO is configured
* with a timeout and it is exceeded.<br />
* Implemented using -get:count:shouldBlock:
*/
- (NSObject*) getObject;
/** Gets the next item from the FIFO, blocking if necessary until an
* item is available. Raises an exception if the FIFO is configured
* with a timeout and it is exceeded.<br />
* The returned item/object, is not autoreleased.<br />
* Implemented using -get:count:shouldBlock:
*/
- (NSObject*) getObjectRetained NS_RETURNS_RETAINED;
/** <init/>
* Initialises the receiver with the specified capacity (buffer size).<br />
* The capacity must lie in the range from one to a hundred million, otherwise
* the receiver is deallocated and this method returns nil.<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 or multiConsumer flag is YES, the FIFO is
* configured to support multiple producer/consumer threads using locking.<br />
* The boundaries array is an ordered list of NSNumber objects containing
* time intervals found boundaries of bands into which to categorise wait
* time stats. Any wait whose duration is less than the interval specified
* in the Nth element is counted in the stat's for the Nth band.
* If this is nil, a default set of boundaries is used. If it is an empty
* array then no time based stats are recorded.<br />
* The name string is a unique identifier for the receiver and is used when
* printing diagnostics and statistics. If an instance with the same name
* already exists, the receiveris deallocated and an exception is raised.
*/
- (id) initWithCapacity: (uint32_t)c
granularity: (uint16_t)g
timeout: (uint16_t)t
multiProducer: (BOOL)mp
multiConsumer: (BOOL)mc
boundaries: (NSArray*)a
name: (NSString*)n;
/** Initialises the receiver as a multi-producer, multi-consumer FIFO with
* no timeout and with default stats gathering enabled.<br />
* However, these values (including the supplied capacity) may be overridden
* as specified in -initWithName:
*/
- (id) initWithCapacity: (uint32_t)c
name: (NSString*)n;
/** Initialises the receiver using the specified name and obtaining other
* details from the NSUserDefaults system using defaults keys where 'NNN'
* is the supplied name.<br />
* The GSFIFOCapacityNNN default specifies the capacity for the FIFO, and
* if missing a capacity of 1000 is assumed.<br />
* The GSFIFOGranularityNNN integer is zero by default.<br />
* The GSFIFOTimeoutNNN integer is zero by default.<br />
* The GSFIFOSingleConsumerNNN boolean is NO by default.<br />
* The GSFIFOSingleProducerNNN boolean is NO by default.<br />
* The GSFIFOBoundariesNNN array is missing by default.<br />
* If no default is found for the specific named FIFO, the default set
* for a FIFO with an empty name is used.
*/
- (id) initWithName: (NSString*)n;
/** Writes exactly count items from buf into the FIFO, blocking if
* necessary until there is space for the entire write.<br />
* Raises an exception if the FIFO is configured with a timeout and it is
* exceeded, or if the count is greater than the FIFO size, or if the
* FIFO was not configured for multi producer or multi consumer use.<br />
* If rtn is YES, the method treats the buffer as containing objects
* which are retained as they are added.
*/
- (void) putAll: (void**)buf count: (unsigned)count shouldRetain: (BOOL)rtn;
/** Writes up to count items from buf into the FIFO.
* If block is YES, this blocks if necessary until at least one item
* can be written, and raises an exception if the FIFO is configured
* with a timeout and it is exceeded.<br />
* Returns the number of items actually written.
*/
- (unsigned) put: (void**)buf count: (unsigned)count shouldBlock: (BOOL)block;
/** Writes up to count objects from buf into the FIFO, retaining each.<br />
* If block is YES, this blocks if necessary until at least one object
* can be written, and raises an exception if the FIFO is configured
* with a timeout and it is exceeded.<br />
* Returns the number of objects actually written.
*/
- (unsigned) putObjects: (NSObject**)buf
count: (unsigned)count
shouldBlock: (BOOL)block;
/** Adds an item 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.br />
* The item may be an object (which is not retained when added) or a
* pointer to memory. The ownership of the item memory should be
* transferred to the FIFO.<br />
* Implemented using -put:count:shouldBlock:
*/
- (void) put: (void*)item;
/** Adds an object to the FIFO (retaining the object), blocking if
* necessary until there is space in the buffer.<br />
* Raises an exception if the FIFO is configured
* with a timeout and it is exceeded.br />
* Implemented using -put:count:shouldBlock:
*/
- (void) putObject: (NSObject*)item;
/** 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.br />
* The object is not retained when added, so ownership of the memory
* should be transferred to the FIFO.<br />
* Implemented using -put:count:shouldBlock:
*/
- (void) putObjectConsumed: (NSObject*) NS_CONSUMED item;
/** Return any available statistics for the receiver.<br />
*/
- (NSString*) stats;
/** Return statistics on get operations for the receiver.<br />
* NB. If the recever is not configured for multiple consumers,
* this method may only be called from the single consumer thread.
*/
- (NSString*) statsGet;
/** Return statistics on put operations for the receiver.<br />
* NB. If the recever is not configured for multiple producers,
* this method may only be called from the single producer thread.
*/
- (NSString*) statsPut;
/** Checks the FIFO and returns the first available item or NULL if the
* FIFO is empty.<br />
* Implemented using -get:count:shouldBlock:
*/
- (void*) tryGet;
/** Checks the FIFO and returns the first available object (autoreleased)
* or nil if the FIFO is empty (or contains a nil object).<br />
* Implemented using -get:count:shouldBlock:
*/
- (NSObject*) tryGetObject;
/**
* Checks the FIFO and returns a reference to the first available item
* or NULL if the FIFO is empty. <br />Calling this method does
* <em>not</em> remove the item from the queue.
*/
- (void*) peek;
/**
* Checks the FIFO and returns the an autoreleased reference to the first
* available object, or nil if the FIFO is empty (or contains a nil object).
* <br /> Calling this method does <em>not</em> remove the object from the
* queue.
*/
- (NSObject*) peekObject;
/** Attempts to put an object (or nil) into the FIFO, returning YES
* on success or NO if the FIFO is full.<br />
* Implemented using -put:count:shouldBlock:
*/
- (BOOL) tryPut: (void*)item;
/** Attempts to retain an object while putting it into the FIFO,
* returning YES on success or NO if the FIFO is full.<br />
* Implemented using -put:count:shouldBlock:
*/
- (BOOL) tryPutObject: (NSObject*)item;
@end
/** Function to efficiently get an item from a fast FIFO.<br />
* Returns NULL if the FIFO is empty.<br />
* Warning ... only for use if the FIFO is NOT configured for multiple
* consumers.
*/
static inline void*
GSGetFastNonBlockingFIFO(GSFIFO *receiver)
{
if (receiver->_head > receiver->_tail)
{
void *item;
item = receiver->_items[receiver->_tail % receiver->_capacity];
receiver->_tail++;
receiver->_getTrySuccess++;
return item;
}
receiver->_getTryFailure++;
return NULL;
}
/** Function to efficiently get an item from a fast FIFO, blocking if
* necessary until an item is available or the timeout occurs.<br />
* Warning ... only for use if the FIFO is NOT configured for multiple
* consumers.
*/
static inline void*
GSGetFastFIFO(GSFIFO *receiver)
{
void *item = GSGetFastNonBlockingFIFO(receiver);
if (0 == item)
{
item = [receiver get];
}
return item;
}
/** Function to efficiently put an item to a fast FIFO.<br />
* Returns YES on success, NO on failure (FIFO is full).<br />
* Warning ... only for use if the FIFO is NOT configured for multiple
* producers.
*/
static inline BOOL
GSPutFastNonBlockingFIFO(GSFIFO *receiver, void *item)
{
if (receiver->_head - receiver->_tail < receiver->_capacity)
{
receiver->_items[receiver->_head % receiver->_capacity] = item;
receiver->_head++;
receiver->_putTrySuccess++;
return YES;
}
receiver->_putTryFailure++;
return NO;
}
/** Function to efficiently put an item to a fast FIFO, blocking if
* necessary until there is space in the FIFO or until the timeout
* occurs.<br />
* Warning ... only for use if the FIFO is NOT configured for multiple
* producers.
*/
static inline void
GSPutFastFIFO(GSFIFO *receiver, void *item)
{
if (NO == GSPutFastNonBlockingFIFO(receiver, item))
{
[receiver put: item];
}
}
#endif

1089
GSFIFO.m

File diff suppressed because it is too large Load diff

View file

@ -1,152 +0,0 @@
#if !defined(INCLUDED_GSIOTHREADPOOL)
#define INCLUDED_GSIOTHREADPOOL 1
/**
Copyright (C) 2010 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: September 2010
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>
#import <Foundation/NSThread.h>
@class NSMutableArray;
@class NSTimer;
/** This is the class for threads in the pool.<br />
* Each thread runs a runloop and is kept 'alive' waiting for a timer in
* the far future, but can be terminated earlier using the -terminate:
* method (which is called when the pool size is changed and the pool
* wishes to stop an idle thread).<br />
*/
@interface GSIOThread : NSThread
{
@private
NSTimer *_timer; /** Pool termination timer */
NSUInteger _count; /** Number of times acquired */
}
/** Terminates the thread by the specified date (as soon as possible if
* the date is nil or is in the past).<br />
* If called from another thread, this method asks the receiver thread to
* perform the method, and waits (running the run loop in the calling
* thread) until either the receiver thread finishes executing or until
* the timeout date (when) is reached.
*/
- (void) terminate: (NSDate*)when;
/** Called when the thread is shut down (immediately before exit).<br />
* Does nothing, provided for subclasses to override.
*/
- (void) shutdown;
/** Called when the thread is started up (before the run loop starts).<br />
* Does nothing, provided for subclasses to override.
*/
- (void) startup;
@end
/** This class provides a thread pool for performing methods which need to
* make use of a runloop for I/O and/or timers.<br />
* Operations are performed on these threads using the standard
* -performSelector:onThread:withObject:waitUntilDone: method ... the
* pool is simply used to keep track of allocation of threads so that
* you can share jobs between them.<br />
* NB. The threading API in OSX 10.4 and earlier is incapable of supporting
* this functionality ... in that case this class cannot be instantiated
* and initialised.
*/
@interface GSIOThreadPool : NSObject
{
NSMutableArray *threads;
NSTimeInterval timeout;
NSUInteger maxThreads;
Class threadClass;
NSString *poolName;
unsigned created;
}
/** Returns an instance intended for sharing between sections of code which
* wish to make use of threading by performing operations in other threads,
* but which don't mind operations being interleaved with those belonging to
* other sections of code.<br />
* Always returns the same instance whenever the method is called.<br />
* The shared pool is created with an initial size as specified by the
* GSIOThreadPoolSize user default (zero if there is no such positive
* integer in the defauilts system), however you can modify that using
* the -setThreads: method.
*/
+ (GSIOThreadPool*) sharedPool;
/** Selects a thread from the pool to be used for some job.<br />
* This method selectes the least used thread in the pool (ie the
* one with the lowest acquire count).<br />
* If the receiver is configured with a size of zero, the main thread
* is returned.
*/
- (NSThread*) acquireThread;
/** Returns the acquire count for the specified thread.
*/
- (NSUInteger) countForThread: (NSThread*)aThread;
/** Returns the currently configured maximum number of threads in the pool.
*/
- (NSUInteger) maxThreads;
/** Sets the base name for threads in this pool. As threads are created they
* are given names formed by assing the value of a counter to the base name.
*/
- (void) setPoolName: (NSString*)aName;
/** Sets the class to be used to create any new threads in this pool.<br />
* Must be a subclass of the GSIOThread class.
*/
- (void) setThreadClass: (Class)aClass;
/** Specify the maximum number of threads in the pool (the actual number
* used may be lower than this value).<br />
* Default is 0 (no thread pooling in use).<br />
* The pool creates threads on demand up to the specified limit (or a lower
* limit if dictated by system resources) but will not destroy idle threads
* unless the limit is subsequently lowered.<br />
* Setting a value of zero means that operations are performed in the
* main thread.
*/
- (void) setThreads: (NSUInteger)max;
/** Specifies the timeout allowed for a thread to close down when the pool
* is deallocated or has its size decreased. Any operations in progress in
* the thread need to close down within this period.
*/
- (void) setTimeout: (NSTimeInterval)t;
/** Returns the current timeout set for the pool.
*/
- (NSTimeInterval) timeout;
/** Releases a thread previously selected from the pool. This decreases the
* acquire count for the thread. If a thread has a zero acquire count, it is
* a candidate for termination and removal from the pool if/when the pool
* has its size changed.
*/
- (void) unacquireThread: (NSThread*)aThread;
@end
#endif

View file

@ -1,426 +0,0 @@
/**
Copyright (C) 2010 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: September 2010
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/NSArray.h>
#import <Foundation/NSDate.h>
#import <Foundation/NSLock.h>
#import <Foundation/NSRunLoop.h>
#import <Foundation/NSThread.h>
#import <Foundation/NSTimer.h>
#import <Foundation/NSException.h>
#import <Foundation/NSUserDefaults.h>
#import <Foundation/NSAutoreleasePool.h>
#import "GSIOThreadPool.h"
#if !defined (GNUSTEP)
#import "GNUstep.h"
#endif
/* Protect changes to a thread's counter
*/
static NSRecursiveLock *classLock = nil;
@interface GSIOThread (Private)
- (NSUInteger) _count;
- (void) _finish: (NSTimer*)t;
- (void) _setCount: (NSUInteger)c;
@end
@implementation GSIOThread (Private)
+ (void) initialize
{
if (nil == classLock)
{
classLock = [NSRecursiveLock new];
}
}
- (NSUInteger) _count
{
return _count;
}
/* Force termination of this thread.
*/
- (void) _finish: (NSTimer*)t
{
_timer = nil;
[self shutdown];
[NSThread exit];
}
- (void) _setCount: (NSUInteger)c
{
if (NSNotFound != _count)
{
_count = c;
}
}
@end
@implementation GSIOThread
/* Run the thread's main runloop until terminated.
*/
- (void) main
{
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSDate *when = [NSDate distantFuture];
NSTimeInterval delay = [when timeIntervalSinceNow];
[self startup];
_timer = [NSTimer scheduledTimerWithTimeInterval: delay
target: self
selector: @selector(_finish:)
userInfo: nil
repeats: NO];
[[NSRunLoop currentRunLoop] run];
[pool release];
}
- (void) shutdown
{
return;
}
- (void) startup
{
return;
}
/* End execution of the thread by the specified date.
*/
- (void) terminate: (NSDate*)when
{
if (YES == [self isFinished])
{
return;
}
if (NO == [when isKindOfClass: [NSDate class]])
{
when = [NSDate date];
}
[self retain];
if ([NSThread currentThread] == self)
{
NSTimeInterval delay = [when timeIntervalSinceNow];
[_timer invalidate];
[classLock lock];
if (0 == _count || delay <= 0.0)
{
_count = NSNotFound; // Mark as terminating
_timer = nil;
delay = 0.0;
}
[classLock unlock];
if (delay > 0.0)
{
_timer = [NSTimer scheduledTimerWithTimeInterval: delay
target: self
selector: @selector(_finish:)
userInfo: nil
repeats: NO];
}
else
{
[self _finish: nil];
}
}
else if ([self isExecuting])
{
NSRunLoop *loop;
/* We need to execute -terminate: in the thread itself,
* and wait until either the thread actually finishes or
* until our time limit expires, whichever comes first.
*/
[self performSelector: _cmd
onThread: self
withObject: when
waitUntilDone: NO];
loop = [NSRunLoop currentRunLoop];
while ([self isExecuting] && [when timeIntervalSinceNow] > 0.0)
{
NSDate *d;
d = [[NSDate alloc] initWithTimeIntervalSinceNow: 0.01];
NS_DURING
{
[loop runUntilDate: d];
}
NS_HANDLER
{
NSLog(@"Problem waiting for thread termination: %@",
localException);
}
NS_ENDHANDLER
[d release];
}
}
[self release];
}
@end
@implementation GSIOThreadPool
static GSIOThreadPool *shared = nil;
/* Return the thread with the lowest usage.
* If there are more threads in the array than we want to use,
* those excess threads are excluded from the check so that
* their usage can drop to zero and they can be terminated.
*/
static GSIOThread *
best(NSMutableArray *a, NSUInteger max)
{
NSUInteger c = [a count];
NSUInteger l = NSNotFound;
GSIOThread *t = nil;
if (c > max)
{
c = max;
}
while (c-- > 0)
{
GSIOThread *o = [a objectAtIndex: c];
if ([o isExecuting])
{
NSUInteger i;
if ((i = [o _count]) < l)
{
t = o;
l = i;
}
}
else if ([o isCancelled] || [o isFinished])
{
[a removeObjectAtIndex: c];
}
}
return t;
}
+ (void) initialize
{
if ([GSIOThreadPool class] == self && nil == shared)
{
NSInteger size;
[GSIOThread class];
size = [[NSUserDefaults standardUserDefaults]
integerForKey: @"GSIOThreadPoolSize"];
if (size < 0)
{
size = 0;
}
shared = [self new];
[shared setThreads: size];
}
}
+ (GSIOThreadPool*) sharedPool
{
return shared;
}
- (NSThread*) acquireThread
{
GSIOThread *t;
NSUInteger c;
if (0 == maxThreads)
{
return [NSThread mainThread];
}
[classLock lock];
t = best(threads, maxThreads);
if (nil == t || ((c = [t _count]) > 0 && [threads count] < maxThreads))
{
NSString *n;
t = [threadClass new];
if (nil == (n = poolName))
{
n = @"GSIOThreadPool";
}
n = [NSString stringWithFormat: @"%@-%u", n, ++created];
[t setName: n];
[threads addObject: t];
[t release];
[t start];
c = 0;
}
[t _setCount: c + 1];
[classLock unlock];
return t;
}
- (NSUInteger) countForThread: (NSThread*)aThread
{
NSUInteger count = 0;
[classLock lock];
if ([threads indexOfObjectIdenticalTo: aThread] != NSNotFound)
{
count = [((GSIOThread*)aThread) _count];
}
[classLock unlock];
return count;
}
- (void) dealloc
{
#if defined(GNUSTEP) || (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4)
GSIOThread *thread;
NSDate *when = [NSDate dateWithTimeIntervalSinceNow: timeout];
[classLock lock];
while ((thread = [threads lastObject]) != nil)
{
[thread performSelector: @selector(terminate:)
onThread: thread
withObject: when
waitUntilDone: NO];
[threads removeObjectIdenticalTo: thread];
}
[threads release];
[classLock unlock];
#endif
DESTROY(poolName);
[super dealloc];
}
- (id) init
{
#if defined(GNUSTEP) || (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4)
if ((self = [super init]) != nil)
{
threads = [NSMutableArray new];
threadClass = [GSIOThread class];
}
#else
[self release];
NSLog(@"WARNING, your OSX system is too old to use GSIOThreadPool");
return nil;
#endif
return self;
}
- (NSUInteger) maxThreads
{
return maxThreads;
}
- (NSString*) poolName
{
NSString *n;
[classLock lock];
n = RETAIN(poolName);
[classLock unlock];
return AUTORELEASE(n);
}
- (void) setPoolName: (NSString*)aName
{
NSString *s = nil;
if (aName)
{
s = AUTORELEASE([aName copy]);
NSAssert([s isKindOfClass: [NSString class]], NSInvalidArgumentException);
}
[classLock lock];
ASSIGN(poolName, s);
[classLock unlock];
}
- (void) setThreads: (NSUInteger)max
{
maxThreads = max;
}
- (void) setThreadClass: (Class)aClass
{
if (NO == [aClass isSubclassOfClass: [GSIOThread class]])
{
[NSException raise: NSInvalidArgumentException
format: @"Thread class must be a subclass of GSIOThread"];
}
threadClass = aClass;
}
- (void) setTimeout: (NSTimeInterval)t
{
timeout = t;
}
- (NSTimeInterval) timeout
{
return timeout;
}
- (void) unacquireThread: (NSThread*)aThread
{
[classLock lock];
if ([threads indexOfObjectIdenticalTo: aThread] != NSNotFound)
{
NSUInteger c;
c = [((GSIOThread*)aThread) _count];
if (0 == c)
{
[classLock unlock];
[NSException raise: NSInternalInconsistencyException
format: @"-unacquireThread: called too many times"];
}
[((GSIOThread*)aThread) _setCount: --c];
if (0 == c && [threads count] > maxThreads)
{
[aThread retain];
[threads removeObjectIdenticalTo: aThread];
[aThread performSelector: @selector(terminate:)
onThread: aThread
withObject: [NSDate date]
waitUntilDone: NO];
[aThread release];
}
}
[classLock unlock];
}
@end

View file

@ -1,82 +0,0 @@
/**
Copyright (C) 2006 Free Software Foundation, Inc.
Written by: Matt Rice <ratmice@yahoo.com>
Date: 2006
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/NSZone.h>
#define GSISLMaxNumberOfLevels 16
#define GSISLMaxLevel 15
/*
* attempt at caching the previously looked up index
* to reduce the search time required when wantedIndex > previousIndex
* this didn't seem to provide any benefit, actually negatively impacting
* performance though it was never thoroughly tested it
*/
#define GSISL_CACHE_OPT 0
typedef id GSISLValueType;
typedef struct GSISLNode_t *GSISLNode;
extern GSISLNode GSISLNil;
struct GSISLForward_t
{
unsigned delta;
GSISLNode next;
};
struct GSISLNode_t
{
GSISLValueType value;
struct GSISLForward_t forward[1];
};
typedef struct GSIndexedSkipList
{
int level; /* Maximum level of the list
(1 more than the number of levels in the list) */
GSISLNode header; /* pointer to header */
unsigned count;
NSZone *zone;
#if GSISL_CACHE_OPT
unsigned indexCache[GSISLMaxNumberOfLevels];
GSISLNode nodeCache[GSISLMaxNumberOfLevels];
#endif
} * GSISList;
void GSISLInitialize();
void GSISLFreeList(GSISList l);
GSISList GSISLInitList(NSZone *zone);
void GSISLInsertItemAtIndex(GSISList l,
GSISLValueType value,
unsigned index);
GSISLValueType GSISLItemAtIndex(GSISList l, unsigned index);
GSISLValueType GSISLRemoveItemAtIndex(GSISList l,
unsigned index);
GSISLValueType GSISLReplaceItemAtIndex(GSISList l,
GSISLValueType newVal,
unsigned index);

View file

@ -1,371 +0,0 @@
/**
Copyright (C) 2006 Free Software Foundation, Inc.
Written by: Matt Rice <ratmice@yahoo.com>
Date: 2006
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.
*/
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#import "GSIndexedSkipList.h"
/* This no longer seems to be needed/correct
#if defined(__MINGW32__)
#include <cstdlib.h> // declares rand()
#endif
*/
#define PrettyErr(x) do { fprintf(stderr, "%s:%i: %s\n",__FILE__, __LINE__, x); exit(EXIT_FAILURE); } while (0)
GSISLNode GSISLNil;
GSISLNode GSISLNewNodeOfLevel(int l, NSZone *zone)
{
GSISLNode ret = (GSISLNode) NSZoneMalloc(zone, sizeof(struct GSISLNode_t)
+ ((l) * sizeof(struct GSISLForward_t)));
if (ret == NULL)
{
PrettyErr(strerror(errno));
}
do
{
ret->forward[l].delta = 0;
} while (--l >= 0);
return ret;
}
void GSISLInitialize()
{
if (GSISLNil != NULL) return;
GSISLNil = GSISLNewNodeOfLevel(0, NSDefaultMallocZone());
GSISLNil->forward[0].delta = UINT_MAX;
GSISLNil->value = nil;
GSISLNil->forward[0].next = NULL;
}
GSISList GSISLInitList(NSZone *zone)
{
GSISList l;
int i;
l = (GSISList)NSZoneMalloc(zone, sizeof(struct GSIndexedSkipList));
if (l == NULL)
{
PrettyErr(strerror(errno));
}
l->zone = zone;
l->level = 0;
l->count = 0;
l->header = GSISLNewNodeOfLevel(GSISLMaxNumberOfLevels, l->zone);
l->header->value = nil;
for (i=0; i < GSISLMaxNumberOfLevels; i++)
{
l->header->forward[0].delta = 0;
l->header->forward[0].next = GSISLNil;
#if GSISL_CACHE_OPT
l->indexCache[i] = 0;
l->nodeCache[i] = l->header;
#endif
}
return(l);
}
void GSISLFreeList(GSISList l)
{
GSISLNode p,q;
p = l->header;
do
{
q = p->forward[0].next;
NSZoneFree(l->zone,p);
p = q;
} while (p != GSISLNil);
NSZoneFree(l->zone, l);
};
int GSISLRandomLevel()
{
int level = 0;
static int p = RAND_MAX / 4;
while (rand() < p && level < GSISLMaxLevel)
{
level++;
}
return level;
}
void GSISLInsertItemAtIndex(GSISList l, GSISLValueType value,
unsigned index)
{
int k, i;
GSISLNode update[GSISLMaxNumberOfLevels];
unsigned updateIndexes[GSISLMaxNumberOfLevels];
GSISLNode p,q;
unsigned depth;
depth = 0;
k = l->level;
#if GSISL_CACHE_OPT
if (l->indexCache[k] < index)
{
p = l->nodeCache[k];
depth = l->indexCache[k];
}
else
{
p = l->header;
}
#else
p = l->header;
#endif
do
{
while (q = p->forward[k].next,
q != GSISLNil && depth + p->forward[k].delta < index + 1)
{
depth += p->forward[k].delta;
p = q;
}
updateIndexes[k] = depth;
update[k] = p;
} while(--k >= 0);
k = GSISLRandomLevel();
q = GSISLNewNodeOfLevel(k, l->zone);
if (k > l->level)
{
/* we are creating a new level that looks like
* header ---> new node ---> tail
*/
k = l->level;
l->level++;
#if GSISL_CACHE_OPT
l->nodeCache[l->level] = l->header;
l->indexCache[l->level] = 0;
#endif
l->header->forward[l->level].delta = index + 1;
l->header->forward[l->level].next = q;
q->forward[l->level].delta = 0;
q->forward[l->level].next = GSISLNil;
}
else
{
/* if there are higher nodes than this nodes level.
increment the deltas in the update, as we are inserting
a node inbetween their starting point and their ending
*/
for (i = k + 1; i <= l->level; i++)
{
if (update[i]->forward[i].delta != 0)
update[i]->forward[i].delta++;
#if GSISL_CACHE_OPT
l->nodeCache[i] = update[i];
l->indexCache[i] = updateIndexes[i];
#endif
}
}
q->value = value;
do
{
/* update from the nodes highest level down to level 0
* on all the levels already existing in the list
*/
p = update[k];
if (p->forward[k].delta)
q->forward[k].delta = updateIndexes[k] + p->forward[k].delta - depth;
p->forward[k].delta = depth + 1 - updateIndexes[k];
q->forward[k].next = p->forward[k].next;
p->forward[k].next = q;
#if GSISL_CACHE_OPT
l->indexCache[k] = updateIndexes[k];
l->nodeCache[k] = update[k];
#endif
} while(--k >= 0);
l->count++;
}
GSISLValueType GSISLRemoveItemAtIndex(GSISList l, unsigned index)
{
int k,m;
GSISLNode update[GSISLMaxNumberOfLevels];
unsigned updateIndexes[GSISLMaxNumberOfLevels];
GSISLNode p,q;
unsigned depth = 0;
GSISLValueType ret;
k = m = l->level;
#if GSISL_CACHE_OPT
if (l->indexCache[k] < index)
{
p = l->nodeCache[k];
depth = l->indexCache[k];
}
else
{
p = l->header;
}
#else
p = l->header;
#endif
do
{
while (q = p->forward[k].next,
q != GSISLNil && depth + p->forward[k].delta < index + 1)
{
depth += p->forward[k].delta;
p = q;
}
update[k] = p;
updateIndexes[k] = depth;
} while(--k >= 0);
for (k = 0; k <= m; k++)
{
p = update[k];
#if GSISL_CACHE_OPT
l->indexCache[k] = updateIndexes[k];
l->nodeCache[k] = update[k];
#endif
if (p->forward[k].next == q)
{
p->forward[k].delta
= (q->forward[k].next == GSISLNil) ? 0
: p->forward[k].delta + q->forward[k].delta - 1;
p->forward[k].next = q->forward[k].next;
}
else if (p->forward[k].next != GSISLNil)
{
p->forward[k].delta--;
}
else
{
p->forward[k].delta = 0;
}
}
ret = q->value;
NSZoneFree(l->zone,q);
/* if header points to nil, decrement the list level */
while (l->header->forward[m].next == GSISLNil && m > 0 )
{
l->header->forward[m].delta = 0;
m--;
}
l->level = m;
l->count--;
return ret;
}
GSISLValueType GSISLItemAtIndex(GSISList l, unsigned index)
{
int k;
unsigned depth = 0;
GSISLNode p,q;
k = l->level;
#if GSISL_CACHE_OPT
if (l->indexCache[k] < index)
{
p = l->nodeCache[k];
depth = l->indexCache[k];
}
else
{
p = l->header;
}
#else
p = l->header;
#endif
do
{
while (q = p->forward[k].next,
q != GSISLNil && depth + p->forward[k].delta < index + 1)
{
depth += p->forward[k].delta;
p = q;
}
#if GSISL_CACHE_OPT
l->nodeCache[k] = p;
l->indexCache[k] = depth;
#endif
} while(--k >= 0);
return(q->value);
}
GSISLValueType
GSISLReplaceItemAtIndex(GSISList l, GSISLValueType newVal, unsigned index)
{
int k;
unsigned depth = 0;
GSISLNode p,q;
GSISLValueType ret;
k = l->level;
#if GSISL_CACHE_OPT
if (l->indexCache[k] < index)
{
p = l->nodeCache[k];
depth = l->indexCache[k];
}
else
{
p = l->header;
}
#else
p = l->header;
#endif
do
{
while (q = p->forward[k].next,
q != GSISLNil && depth + p->forward[k].delta < index + 1)
{
depth += p->forward[k].delta;
p = q;
}
#if GSISL_CACHE_OPT
l->indexCache[k] = depth;
l->nodeCache[k] = p;
#endif
} while(--k >= 0);
ret = q->value;
q->value = newVal;
return ret;
}

View file

@ -1,440 +0,0 @@
#if !defined(INCLUDED_GSLINKEDLIST)
#define INCLUDED_GSLINKEDLIST 1
/**
Copyright (C) 2010 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: September 2010
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 GSLinkedList;
/** GSListLink provides simple doubly linked list functionality to
* avoid the need to constantly re-invent it (as the OpenStep/Cocoa
* APIs do not provide this). The emphasis is on speed of operation
* so instance variables are directly accessible and inline functions
* are provided to manipulate them (without error cehcking), but you
* can/should avoid these direct access features unless speed is
* really critical.<br />
* A list may either be 'normal' ... (where the head/tail ends of
* the list have a nil pointer to the previous/next link) or 'circular'
* in which case the list is not terminated. <br />
* The GSListLink item carries a minimal payload of a single item which
* is retained by the link.<br />
* The GSListLink owner is an optional pointer to an object which 'owns'
* the link ... a GSLinkedList instance may use this to check link
* ownership when manipulating links.
*/
@interface GSListLink : NSObject
{
@public
GSListLink *next; // Not retained
GSListLink *previous; // Not retained
GSLinkedList *owner; // Not retained
NSObject *item;
}
/** Initialise the receiver as a link for a circular linked list.
*/
- (id) initCircular;
/** Return the item in the link.
*/
- (NSObject*) item;
/** Return the next item in the list containing the receiver,
* or nil if there are no more items.
*/
- (GSListLink*) next;
/** Return the list which owns the receiver or nil if the receiver is
* not in a list.
*/
- (GSLinkedList*) owner;
/** Return the previous item in the list containing the receiver,
* or nil if there are no more items.
*/
- (GSListLink*) previous;
/** Set an item value by retaining it and releasing any previous value.
*/
- (void) setItem: (NSObject*)anItem;
@end
/** Inserts link after at in a circular list.<br />
* Arguments must not be nil and link must not be in a list
* (ie its next and previous pointers must point to itsself).
*/
static inline void
GSLinkCircularInsertAfter(GSListLink *link, GSListLink *at)
{
link->next = at->next;
link->previous = at;
link->next->previous = link;
link->previous->next = link;
}
/** Inserts link before at in a circular list.<br />
* Arguments must not be nil and link must not be in a list
* (ie its next and previous pointers must point to itsself).
*/
static inline void
GSLinkCircularInsertBefore(GSListLink *link, GSListLink *at)
{
link->next = at;
link->previous = at->previous;
link->next->previous = link;
link->previous->next = link;
}
/** Removes link from any list it is in.<br />
* The link argument must not be nil.
*/
static inline void
GSLinkCircularRemove(GSListLink *link)
{
link->next->previous = link->previous;
link->previous->next = link->next;
link->next = link->previous = link;
}
/** Inserts link after at in a normal list.<br />
* Arguments must not be nil and link must not be in a list
* (ie its next and previous pointers must both be nil).
*/
static inline void
GSLinkInsertAfter(GSListLink *link, GSListLink *at)
{
if (nil != at->next)
{
at->next->previous = link;
}
link->previous = at;
at->next = link;
}
/** Inserts link before at in a normal list.<br />
* Arguments must not be nil and link must not be in a list
* (ie its next and previous pointers must both be nil).
*/
static inline void
GSLinkInsertBefore(GSListLink *link, GSListLink *at)
{
if (nil != at->previous)
{
at->previous->next = link;
}
link->next = at;
at->previous = link;
}
/** Removes link from the list it is in.<br />
* The link argument must not be nil.
*/
static inline void
GSLinkRemove(GSListLink *link)
{
if (nil != link->next)
{
link->next->previous = link->previous;
}
if (nil != link->previous)
{
link->previous->next = link->next;
}
link->next = link->previous = nil;
}
/** GSLinkedList manages a list of GSListLink objects.<br />
* The notional direction of the list is from head to tail. So the head
* is considered to be the first link in the list and tail is considered
* to be the last (head is before tail, tail is after head).
*/
@interface GSLinkedList : NSObject
{
@public
GSListLink *head; // First link in the list.
GSListLink *tail; // Last link in the list.
NSUInteger count; // Number of links in the list.
}
/** Appends link at the end of the linked list managed by the receiver.<br />
* Retains the link.
*/
- (void) append: (GSListLink*)link;
/** Returns the number of links in the list.
*/
- (NSUInteger) count;
/** Remove all links from the list and release them all.
*/
- (void) empty;
/** Searches the linked list returning the link containing object or nil
* if it is not found.<br />
* The comparison is performed using the [NSObject-isEqual:] method
* of object.<br />
* If start is nil then the whole list is searched.<br />
* This method will <em>not</em> find links containing nil items.
*/
- (GSListLink*) findEqual: (NSObject*)object
from: (GSListLink*)start
back: (BOOL)aFlag;
/** Searches the linked list returning the link containing object or nil
* if it is not found.<br />
* If start is nil then the whole list is searched.<br />
* A direct pointer comparison is used to determine equality.
*/
- (GSListLink*) findIdentical: (NSObject*)object
from: (GSListLink*)start
back: (BOOL)aFlag;
/** Returns the first link in the list.
*/
- (GSListLink*) head;
/** Inserts other immediately after the receiver.<br />
* Retains the link.
*/
- (void) insert: (GSListLink*)link after: (GSListLink*)at;
/** Inserts other immediately before the receiver.<br />
* Retains the link.
*/
- (void) insert: (GSListLink*)link before: (GSListLink*)at;
/** Removes the link from the receiver.<br />
* releases the link.
*/
- (void) removeLink: (GSListLink*)link;
/** Returns the last link in the list.
*/
- (GSListLink*) tail;
@end
/** Searches from list to the end looking for the first link containing
* object (as determined by using object's [NSObject-isEqual:] method).<br />
* If back is YES, the search is in the direction from tail to head
* rather than the normal search from head to tail.<br />
* If from is nil the list is search from head or tail as appropriate
* to the direction in which it is searched.
*/
extern GSListLink*
GSLinkedListFindEqual(NSObject *object, GSLinkedList *list,
GSListLink *from, BOOL back);
/** Searches from list to the end looking for the first link containing
* object (as determined by direct pointer comparison).<br />
* If back is YES, the search is in the direction from tail to head
* rather than the normal search from head to tail.<br />
* If from is nil the list is search from head or tail as appropriate
* to the direction in which it is searched.
*/
extern GSListLink*
GSLinkedListFindIdentical(NSObject *object, GSLinkedList *list,
GSListLink *from, BOOL back);
/** Returns the first (head) object in the list.
*/
static inline id
GSLinkedListFirstObject(GSLinkedList *list)
{
if (nil == list->head)
return nil;
return list->head->item;
}
/** Inserts link immediately after at.<br />
* If at is nil, inserts at the end of the list (link becomes tail).<br />
* Updates the head, tail and count variables of list.<br />
* Does not retain link.
*/
extern void
GSLinkedListInsertAfter(GSListLink *link, GSLinkedList *list, GSListLink *at);
/** Inserts link immediately before at.<br />
* If at is nil, inserts at the start of the list (link becomes head).<br />
* Updates the head, tail and count variables of list.<br />
* Does not retain link.
*/
extern void
GSLinkedListInsertBefore(GSListLink *link, GSLinkedList *list, GSListLink *at);
/** Returns the last (tail) object in the list.
*/
static inline id
GSLinkedListLastObject(GSLinkedList *list)
{
if (nil == list->tail)
return nil;
return list->tail->item;
}
/** Moves the link to the head of the list (makes it the first object)
* if it is not already there.
*/
extern void
GSLinkedListMoveToHead(GSListLink *link, GSLinkedList *list);
/** Moves the link to the tail of the list (makes it the last object)
* if it is not already there.
*/
extern void
GSLinkedListMoveToTail(GSListLink *link, GSLinkedList *list);
/** Removes link from the list.<br />
* Updates the head, tail and count variables of list.<br />
* Does not release link.
*/
extern void
GSLinkedListRemove(GSListLink *link, GSLinkedList *list);
/** This class extends GSLinkedList by providing storage for unused links
* and re-using those links when a new link is needed.<br />
* This avoids the overhead of allocating/deallocating links and provides
* an API more like a mutable array.
*/
@interface GSLinkStore : GSLinkedList
{
@public
Class linkClass; /** The class used for links */
GSListLink *free; /** The unused links */
}
/** Creates an instance of a store to be used to create links using the
* specified class (must be a subclass of GSListLink). If the class is
* nil then GSListLink is used.
*/
+ (GSLinkStore*) storeFor: (Class)theLinkClass;
/** Adds an object at the tail of the list (calls -insertObject:after:),
* making it the last object in the list.<br />
* Returns the list link that the object is stored in.
*/
- (GSListLink*) addObject: (id)anObject;
/** Returns the first (head) object in the list or nil if the list is empty.
*/
- (id) firstObject;
/** Inserts anObject immediately after the specified link. If at is nil
* the object is inserted at the end of the list (as tail).<br />
* Returns the list link that the object is stored in.
*/
- (GSListLink*) insertObject: (id)anObject after: (GSListLink*)at;
/** Inserts anObject immediately before the specified link. If at is nil
* the object is inserted at the start of the list (as head).<br />
* Returns the list link that the object is stored in.
*/
- (GSListLink*) insertObject: (id)anObject before: (GSListLink*)at;
/** Returns the last (tail) object in the list or nil if the list is empty.
*/
- (id) lastObject;
/** Removes any unused links from the list (to release the memory they
* occupied).
*/
- (void) purge;
/** Removes the first (head) object from the list (or does nothing if the list
* is empty).
*/
- (void) removeFirstObject;
/** Removes the last (tail) object from the list (or does nothing if the list
* is empty).
*/
- (void) removeLastObject;
/** Removes the object in the specified link.
*/
- (void) removeObjectAt: (GSListLink*)at;
/** Removes the object at the specified position.
*/
- (void) removeObjectAtIndex: (NSUInteger)index;
@end
/** Returns a link to the free list of the store. The link must either have
* been provided by GSLinkStoreProvideLink() or have been removed from the
* store list using GSLinkRemove().
*/
static inline void
GSLinkStoreConsumeLink(GSLinkStore *list, GSListLink *link)
{
link->next = list->free;
list->free = link;
}
/** Fetches a link from the free list of the store (allocating if necessary).
* The link is still nominally owned by the store and must be inserted into
* the list or returned to the free list.
*/
static inline GSListLink*
GSLinkStoreProvideLink(GSLinkStore *list)
{
GSListLink *link = list->free;
if (nil == link)
{
link = [list->linkClass new];
}
else
{
list->free = link->next;
link->next = nil;
}
return link;
}
/** Adds the object to the list after the specified link.<br />
* Calls GSLinkedListInsertAfter().<br />
* Returns the list link that the object is stored in.
*/
extern GSListLink*
GSLinkStoreInsertObjectAfter(
NSObject *anObject, GSLinkStore *list, GSListLink *at);
/** Adds the object to the list before the specified link.<br />
* Calls GSLinkedListInsertBefore().<br />
* Returns the list link that the object is stored in.
*/
extern GSListLink*
GSLinkStoreInsertObjectBefore(
NSObject *anObject, GSLinkStore *list, GSListLink *at);
/** Removes the object held in the specified link.<br />
* If at is nil or is not owned by the list, this does nothing.
*/
extern void
GSLinkStoreRemoveObjectAt(GSLinkStore *list, GSListLink *at);
#endif

View file

@ -1,655 +0,0 @@
/**
Copyright (C) 2010 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: September 2010
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/NSString.h>
#import <Foundation/NSException.h>
#import "GSLinkedList.h"
#if !defined (GNUSTEP)
#import "GNUstep.h"
#endif
@implementation GSListLink
- (void) dealloc
{
NSAssert(nil == owner, NSInternalInconsistencyException);
[item release];
DEALLOC;
}
- (id) initCircular
{
if ((self = [super init]) != nil)
{
next = previous = self;
}
return self;
}
- (NSObject*) item
{
return item;
}
- (GSListLink*) next
{
if (next == self)
{
return nil;
}
return next;
}
- (GSLinkedList*) owner
{
return owner;
}
- (GSListLink*) previous
{
if (previous == self)
{
return nil;
}
return previous;
}
- (void) setItem: (NSObject*)anItem
{
id o = item;
item = [anItem retain];
RELEASE(o);
}
@end
@implementation GSLinkedList
- (void) append: (GSListLink*)link
{
if (nil == link)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] nil argument",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
if (self == link->owner)
{
if (link != tail)
{
GSLinkedListRemove(link, self);
GSLinkedListInsertAfter(link, self, tail);
}
}
else
{
if (nil != link->owner || nil != link->next || nil != link->previous)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] other link is still in a list",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
GSLinkedListInsertAfter(link, self, tail);
RETAIN(link);
}
}
- (NSUInteger) count
{
return count;
}
- (void) dealloc
{
[self empty];
DEALLOC;
}
- (void) empty
{
GSListLink *link;
while (nil != (link = head))
{
head = link->next;
link->owner = nil;
link->next = link->previous = nil;
RELEASE(link);
}
tail = nil;
count = 0;
}
- (GSListLink*) findEqual: (NSObject*)object
from: (GSListLink*)start
back: (BOOL)aFlag
{
if (nil != start && start->owner != self)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] start link is not in this list",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
return GSLinkedListFindEqual(object, self, start, aFlag);
}
- (GSListLink*) findIdentical: (NSObject*)object
from: (GSListLink*)start
back: (BOOL)aFlag
{
if (nil != start && start->owner != self)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] start link is not in this list",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
return GSLinkedListFindIdentical(object, self, start, aFlag);
}
- (GSListLink*) head
{
return head;
}
- (void) insert: (GSListLink*)link after: (GSListLink*)at
{
if (nil == link)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] nil link argument",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
if (nil == at)
{
at = tail;
}
if (at->owner != self)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] 'at' link is not in this list",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
if (at == link)
{
return;
}
if (link->owner == self)
{
GSLinkedListRemove(link, self);
GSLinkedListInsertAfter(link, self, at);
}
else
{
if (nil != link->owner || nil != link->next || nil != link->previous)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] other link is still in a list",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
GSLinkedListInsertAfter(link, self, at);
RETAIN(link);
}
}
- (void) insert: (GSListLink*)link before: (GSListLink*)at
{
if (nil == link)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] nil link argument",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
if (nil == at)
{
at = head;
}
if (at->owner != self)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] 'at' link is not in this list",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
if (at == link)
{
return;
}
if (link->owner == self)
{
GSLinkedListRemove(link, self);
GSLinkedListInsertBefore(link, self, at);
}
else
{
if (nil != link->owner || nil != link->next || nil != link->previous)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] other link is still in a list",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
GSLinkedListInsertBefore(link, self, at);
RETAIN(link);
}
}
- (void) removeLink: (GSListLink*)link
{
if (nil == link)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] nil link argument",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
if (link->owner != self)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] link is not in this list",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
GSLinkedListRemove(link, self);
RETAIN(link);
}
- (GSListLink*) tail
{
return tail;
}
@end
GSListLink*
GSLinkedListFindEqual(NSObject *object, GSLinkedList *list,
GSListLink *from, BOOL back)
{
if (nil == object)
{
return GSLinkedListFindIdentical(object, list, from, back);
}
else
{
BOOL (*imp)(id, SEL, id);
imp = (BOOL(*)(id,SEL,id))[object methodForSelector: @selector(isEqual:)];
if (YES == back)
{
if (nil == from)
{
from = list->tail;
}
while (nil != from)
{
if (YES == (*imp)(object, @selector(isEqual:), from->item))
{
return from;
}
from = from->previous;
}
}
else
{
if (nil == from)
{
from = list->head;
}
while (nil != from)
{
if (YES == (*imp)(object, @selector(isEqual:), from->item))
{
return from;
}
from = from->next;
}
}
}
return nil;
}
GSListLink*
GSLinkedListFindIdentical(NSObject *object, GSLinkedList *list,
GSListLink *from, BOOL back)
{
if (YES == back)
{
if (nil == from)
{
from = list->tail;
}
while (nil != from)
{
if (object == from->item)
{
return from;
}
from = from->previous;
}
}
else
{
if (nil == from)
{
from = list->head;
}
while (nil != from)
{
if (object == from->item)
{
return from;
}
from = from->next;
}
}
return nil;
}
void
GSLinkedListInsertAfter(GSListLink *link, GSLinkedList *list, GSListLink *at)
{
if (nil == list->head)
{
list->head = list->tail = link;
}
else
{
if (nil == at)
{
at = list->tail;
}
link->next = at ? at->next : nil;
if (nil == link->next)
{
list->tail = link;
}
else
{
link->next->previous = link;
}
if (at)
{
at->next = link;
}
link->previous = at;
}
link->owner = list;
list->count++;
}
void
GSLinkedListInsertBefore(GSListLink *link, GSLinkedList *list, GSListLink *at)
{
if (nil == list->head)
{
list->head = list->tail = link;
}
else
{
if (nil == at)
{
at = list->head;
}
link->previous = at->previous;
if (nil == link->previous)
{
list->head = link;
}
else
{
link->previous->next = link;
}
at->previous = link;
link->next = at;
}
link->owner = list;
list->count++;
}
void
GSLinkedListRemove(GSListLink *link, GSLinkedList *list)
{
if (list->head == link)
{
list->head = link->next;
if (nil != list->head)
{
list->head->previous = nil;
}
}
else
{
link->previous->next = link->next;
}
if (list->tail == link)
{
list->tail = link->previous;
if (nil != list->tail)
{
list->tail->next = nil;
}
}
else if (nil != link->next)
{
link->next->previous = link->previous;
}
link->next = link->previous = nil;
link->owner = nil;
list->count--;
}
extern void
GSLinkedListMoveToHead(GSListLink *link, GSLinkedList *list)
{
if (link != list->head)
{
if (link == list->tail)
{
list->tail = link->previous;
list->tail->next = nil;
}
else
{
link->next->previous = link->previous;
link->previous->next = link->next;
}
link->next = list->head;
link->previous = nil;
list->head->previous = link;
list->head = link;
}
}
extern void
GSLinkedListMoveToTail(GSListLink *link, GSLinkedList *list)
{
if (link != list->tail)
{
if (link == list->head)
{
list->head = link->next;
list->head->previous = nil;
}
else
{
link->next->previous = link->previous;
link->previous->next = link->next;
}
link->next = nil;
link->previous = list->tail;
list->tail->next = link;
list->tail = link;
}
}
@implementation GSLinkStore
+ (GSLinkStore*) storeFor: (Class)theLinkClass
{
Class c = [GSListLink class];
GSLinkStore *s;
if (Nil == theLinkClass)
{
theLinkClass = c;
}
NSAssert([theLinkClass isSubclassOfClass: c], NSInvalidArgumentException);
s = [self new];
s->linkClass = theLinkClass;
return AUTORELEASE(s);
}
- (GSListLink*) addObject: (id)anObject
{
return GSLinkStoreInsertObjectAfter(anObject, self, tail);
}
- (void) dealloc
{
[self empty];
[self purge];
DEALLOC
}
- (void) empty
{
while (nil != head)
{
GSLinkStoreRemoveObjectAt(self, head);
}
}
- (id) firstObject
{
return GSLinkedListFirstObject(self);
}
- (id) init
{
if (nil != (self = [super init]))
{
linkClass = [GSListLink class];
}
return self;
}
- (GSListLink*) insertObject: (id)anObject after: (GSListLink*)at
{
return GSLinkStoreInsertObjectAfter(anObject, self, at);
}
- (GSListLink*) insertObject: (id)anObject before: (GSListLink*)at
{
return GSLinkStoreInsertObjectBefore(anObject, self, at);
}
- (id) lastObject
{
return GSLinkedListLastObject(self);
}
- (void) purge
{
while (nil != free)
{
GSListLink *link = free;
free = link->next;
link->next = nil;
RELEASE(link);
}
}
- (void) removeFirstObject
{
if (nil != head)
{
GSLinkStoreRemoveObjectAt(self, head);
}
}
- (void) removeLastObject
{
if (nil != tail)
{
GSLinkStoreRemoveObjectAt(self, tail);
}
}
- (void) removeObjectAt: (GSListLink*)at
{
GSLinkStoreRemoveObjectAt(self, at);
}
- (void) removeObjectAtIndex: (NSUInteger)index
{
GSListLink *link = head;
if (index >= count)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] index too large",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
while (index-- > 0)
{
link = link->next;
}
GSLinkStoreRemoveObjectAt(self, link);
}
@end
GSListLink*
GSLinkStoreInsertObjectAfter(
NSObject *anObject, GSLinkStore *list, GSListLink *at)
{
GSListLink *link = GSLinkStoreProvideLink(list);
link->item = RETAIN(anObject);
GSLinkedListInsertAfter(link, list, (nil == at) ? list->tail : at);
return link;
}
GSListLink*
GSLinkStoreInsertObjectBefore(
NSObject *anObject, GSLinkStore *list, GSListLink *at)
{
GSListLink *link = GSLinkStoreProvideLink(list);
link->item = RETAIN(anObject);
GSLinkedListInsertBefore(link, list, (nil == at) ? list->head : at);
return link;
}
void
GSLinkStoreRemoveObjectAt(GSLinkStore *list, GSListLink *at)
{
if (nil != at && at->owner == list)
{
GSLinkedListRemove(at, list);
RELEASE(at->item);
at->item = nil;
GSLinkStoreConsumeLink(list, at);
}
}

View file

@ -1,79 +0,0 @@
/**
Copyright (C) 2006 Free Software Foundation, Inc.
Written by: Matt Rice <ratmice@yahoo.com>
Date: 2006
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/NSArray.h>
/**
<p>An NSMutableArray category to provide an NSMutableArray instance
which uses a skip list variant for it's underlying data structure.
</p>
<p>While a skip list is typically sorted and represents a dictionary.
the indexed skip list is sorted by index and maintains deltas to represent
the distance between linked nodes.
</p>
<p>The underlying data structure looks much like the figure below:
</p>
<example>
index -> HEAD 1 2 3 4 5 6 TAIL
5| ---------------------> # ------> #
3| -----------> 2 ------> # ------> #
1| -> 1 -> 1 -> 1 -> 1 -> 1 -> # -> #
</example>
<p>Where the numbers represent how many indexes it is to the next node
of the appropriate level. The bottom level always points to the next node.</p>
<p>Finding a specific index starts at the top level, until the current
depth + the next nodes delta is larger than wanted index, then it goes down
1 level, and repeats until it finds the wanted index.
</p>
<p>Addition and removal of indexes requires an update of the deltas of nodes
which begin before, and end after the wanted index,
these are the places where it goes down a level.
</p>
<p>The rationale behind it was where a linked list based mutable array will
quickly add and remove elements, it may perform poorly at accessing any
random index (because it must traverse the entire list to get to the index).
</p>
<p>While a C array based mutable array will perform good at random index
access it may perform poorly at adding and removing indexes
(because it must move all items after the altered index).
</p>
<p>While a GSSkipMutableArray may not outperform a linked list or a
C array based mutable array at their specific strengths, it attempts
to not suffer from either of their weaknesses, at the cost of additional
memory overhead.
</p>
*/
@interface NSMutableArray (GSSkipMutableArray)
/**
* Creates and returns an autoreleased NSMutableArray implemented as a
* skip list for rapid insertion/deletion within very large arrays.
*/
+ (NSMutableArray*) skipArray;
@end

View file

@ -1,329 +0,0 @@
/**
Copyright (C) 2006 Free Software Foundation, Inc.
Written by: Matt Rice <ratmice@yahoo.com>
Date: 2006
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.
*/
#include <inttypes.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSException.h>
#import <Foundation/NSValue.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSString.h>
#import "GSSkipMutableArray.h"
#import "GSIndexedSkipList.h"
#if !defined (GNUSTEP) && (MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4)
typedef unsigned int NSUInteger;
#endif
static Class abstractClass = 0;
static Class concreteClass = 0;
@interface GSSkipMutableArray : NSMutableArray
@end
@interface GSConcreteSkipArray : GSSkipMutableArray
{
GSISList l;
}
- (GSISList) _list;
@end
@implementation NSMutableArray (GSSkipMutableArray)
+ (NSMutableArray*) skipArray
{
return [GSSkipMutableArray array];
}
@end
@implementation GSSkipMutableArray
+ (id) allocWithZone: (NSZone*)z
{
if (self == abstractClass)
{
return [concreteClass allocWithZone: z];
}
return [super allocWithZone: z];
}
+ (void) initialize
{
if (abstractClass == 0)
{
abstractClass = [GSSkipMutableArray class];
concreteClass = [GSConcreteSkipArray class];
}
}
@end
@interface GSConcreteSkipArrayEnumerator : NSEnumerator
{
GSISLNode node;
}
@end
@implementation GSConcreteSkipArrayEnumerator
- (id) initWithArray: (NSArray *)arr
{
if (![arr isKindOfClass: [GSConcreteSkipArray class]])
{
[[NSException exceptionWithName: NSInternalInconsistencyException
reason: @"not a GSConcreteSkipArray"
userInfo: nil] raise];
}
self = [super init];
node = [(GSConcreteSkipArray *)arr _list]->header->forward[0].next;
return self;
}
- (id) nextObject
{
id foo = node->value;
if (node == GSISLNil)
return nil;
node = node->forward[0].next;
return foo;
}
@end
@implementation GSConcreteSkipArray
- (GSISList) _list
{
return l;
}
- (void) _raiseRangeExceptionWithIndex: (NSUInteger)index from: (SEL)sel
{
NSDictionary *info;
NSException *exception;
NSString *reason;
info = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithUnsignedInt: index], @"Index",
[NSNumber numberWithUnsignedInt: l->count], @"Count",
self, @"Array", nil, nil];
reason = [NSString stringWithFormat: @"Index %"PRIuPTR
@" is out of range %d (in '%@')",
index, l->count, NSStringFromSelector(sel)];
exception = [NSException exceptionWithName: NSRangeException
reason: reason
userInfo: info];
[exception raise];
}
+ (void) initialize
{
GSISLInitialize();
}
- (id) initWithObjects: (const id[])objects count: (NSUInteger) count
{
int i;
self = [super init];
if (!self) return nil;
l = GSISLInitList([self zone]);
for (i = 0; i < count; i++)
{
GSISLInsertItemAtIndex(l, [objects[i] retain], i);
}
return self;
}
- (id) init
{
self = [super init];
if (!self) return nil;
l = GSISLInitList([self zone]);
return self;
}
- (void) dealloc
{
GSISLNode p,q;
p = l->header->forward[0].next;
while (p != GSISLNil)
{
q = p->forward[0].next;
[p->value release];
NSZoneFree(l->zone,p);
p = q;
}
NSZoneFree(l->zone, l->header);
NSZoneFree(l->zone, l);
[super dealloc];
}
- (void) insertObject: (id)object atIndex: (NSUInteger)index
{
if (index > l->count)
{
[self _raiseRangeExceptionWithIndex: index from: _cmd];
}
GSISLInsertItemAtIndex(l, [object retain], index);
}
- (id) objectAtIndex: (NSUInteger)index
{
if (index >= l->count)
{
[self _raiseRangeExceptionWithIndex: index from: _cmd];
}
return GSISLItemAtIndex(l, index);
}
- (void) removeObjectAtIndex: (NSUInteger)index
{
if (index >= l->count)
{
[self _raiseRangeExceptionWithIndex: index from: _cmd];
}
[GSISLRemoveItemAtIndex(l, index) release];
}
- (void) addObject: (id)obj
{
GSISLInsertItemAtIndex(l, [obj retain], l->count);
}
- (NSUInteger) count
{
return l->count;
}
- (void) replaceObjectAtIndex: (NSUInteger)index withObject: (id)obj
{
[GSISLReplaceItemAtIndex(l, [obj retain], index) release];
}
- (NSEnumerator*) objectEnumerator
{
id e;
e = [GSConcreteSkipArrayEnumerator
allocWithZone: NSDefaultMallocZone()];
e = [e initWithArray: self];
return [e autorelease];
}
/* returns an in an NSString suitable for running through graphviz,
* with the graph named 'graphName'
*/
- (NSString *) _makeGraphOfInternalLayoutNamed: (NSString *)graphName
{
GSISLNode p;
NSUInteger k, i;
NSMutableDictionary *values;
NSMutableArray *edges;
NSMutableString *graph;
NSArray *tmp;
graph = [[NSMutableString alloc] initWithCapacity: 1024];
[graph appendString:
[NSString stringWithFormat: @"digraph %@ {\n", graphName]];
[graph appendString: @"graph [rankdir = LR];\n"];
[graph appendString: @"node [shape = record];\n"];
values = [[NSMutableDictionary alloc] init];
edges = [[NSMutableArray alloc] init];
[values setObject:
[NSMutableString stringWithFormat:
@"\"%p\" [label = \"%p (NIL) |{ <delta0> 0 | <forward0> }",
GSISLNil, GSISLNil]
forKey: [NSString stringWithFormat: @"%p", GSISLNil]];
for (k = 0; k < l->level + 1; k++)
{
for (p = l->header; p != GSISLNil; p = p->forward[k].next)
{
NSString *value;
NSMutableString *foo;
value = [NSString stringWithFormat: @"%p", p];
foo = [values objectForKey: value];
if (foo == nil)
{
foo = [[NSMutableString alloc] init];
[foo appendString:
[NSString stringWithFormat:
@"\"%p\" [label = \"%p%@ |{ <delta%"PRIuPTR
@"> %i | <forward%"PRIuPTR"> }",
p, p, p == l->header ? @"(HEADER)" : @"", k,
p->forward[k].delta, k]];
if (p != GSISLNil)
[edges addObject:
[NSString stringWithFormat:
@"\"%p\": forward%"PRIuPTR" -> \"%p\": delta%"PRIuPTR";\n",
p, k, p->forward[k].next,
p->forward[k].next == GSISLNil ? 0 : k]];
[values setObject: foo forKey: value];
[foo release];
}
else
{
[foo appendString:
[NSString stringWithFormat:
@"|{ <delta%"PRIuPTR"> %i | <forward%"PRIuPTR"> }",
k, p->forward[k].delta, k]];
if (p != GSISLNil)
[edges addObject:
[NSString stringWithFormat:
@"\"%p\": forward%"PRIuPTR" -> \"%p\": delta%"PRIuPTR";\n",
p, k, p->forward[k].next,
p->forward[k].next == GSISLNil ? 0 : k]];
[values setObject: foo forKey: value];
}
}
}
tmp = [values allKeys];
for (i = 0; i < [tmp count]; i++)
{
[graph appendString: [values objectForKey: [tmp objectAtIndex: i]]];
[graph appendString: @"\"];\n"];
}
for (i = 0; i < [edges count]; i++)
{
[graph appendString: [edges objectAtIndex: i]];
}
[graph appendString: @"}\n"];
[values release];
[edges release];
return [graph autorelease];
}
@end

View file

@ -1,147 +0,0 @@
#if !defined(INCLUDED_GSTHREADPOOL)
#define INCLUDED_GSTHREADPOOL 1
/**
Copyright (C) 2010 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: September 2010
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 GSLinkedList;
@class NSDate;
@class NSRecursiveLock;
/** This class provides a thread pool for performing methods
* of objects in parallel in other threads.<br />
* This is similar to the NSOperationQueue class but is a
* lightweight alternative designed to operate rather faster
* though with slightly decreased functionality ... for instance
* there is no dependency checking supported.
*/
@interface GSThreadPool : NSObject
{
NSRecursiveLock *poolLock;
NSString *poolName;
unsigned created;
BOOL shutdown;
BOOL suspended;
NSUInteger maxThreads;
GSLinkedList *idle;
GSLinkedList *live;
NSUInteger maxOperations;
GSLinkedList *operations;
GSLinkedList *unused;
NSUInteger processed;
}
/** Returns an instance intended for sharing between sections of code which
* wish to make use of threading by performing operations in other threads,
* but which don't mind operations being interleaved with those belonging to
* other sections of code.<br />
* Always returns the same instance whenever the method is called.
*/
+ (GSThreadPool*) sharedPool;
/** Waits until the pool of operations is empty (and idle) or until the
* specified timestamp. Returns YES if the pool was emptied, NO otherwise.
*/
- (BOOL) drain: (NSDate*)before;
/** Removes all operations which have not yet started, returning a count
* of the abandoned operations.
*/
- (NSUInteger) flush;
/** Returns human resdable pool statistics.
*/
- (NSString*) info;
/** Returns YES if no operations are waiting to be performed, NO otherwise.
*/
- (BOOL) isEmpty;
/** Returns YES if NO operations are in progress, NO otherwise.
*/
- (BOOL) isIdle;
/** Returns YES if startup of new operations is suspended, NO otherwise.
*/
- (BOOL) isSuspended;
/** Returns the currently configured maximum number of operations which
* may be scheduled at any one time.
*/
- (NSUInteger) maxOperations;
/** Returns the currently configured maximum number of threads in the pool.
*/
- (NSUInteger) maxThreads;
/** Returns the name of the pool as set using the -setPoolName: method.
*/
- (NSString*) poolName;
/** Reverses the effect of -suspend.
*/
- (void) resume;
/** Adds the object to the queue for which operations should be performed.<br />
* You may add an object more than once, but that may result in the operation
* being performed simultaneously in more than one thread.<br />
* If the pool is configured with zero threads or the queue of operations is
* full, this method will simply perform the operation immediately.<br />
* The operation will be performed in a context where there is an exception
* handler set to trap exceptions, and an autorelease pool to deal with
* autoreleased objects.
*/
- (void) scheduleSelector: (SEL)aSelector
onReceiver: (NSObject*)aReceiver
withObject: (NSObject*)anArgument;
/** Specify the number of operations which may be waiting.<br />
* Default is 100.<br />
* Setting a value of zero ensures that operations are performed
* immediately rather than being queued.
*/
- (void) setOperations: (NSUInteger)max;
/** Sets the pool name, used as a prefix for thread names.
*/
- (void) setPoolName: (NSString*)aName;
/** Specify the maximum number of threads in the pool (the actual number
* used may be lower than this value).<br />
* Default is 2.<br />
* The pool creates threads on demand up to the specified limit (or a lower
* limit if dictated by system resources) but will not destroy idle threads
* unless the limit is subsequently released.<br />
* Setting a value of zero means that operations are performed in the
* main thread. In this case -drain: will be used (with a 30 second limit)
* followed by -flush to ensure that the queue is emptied before the threads
* are shut down.
*/
- (void) setThreads: (NSUInteger)max;
/** Turns off startup of new operations.
*/
- (void) suspend;
@end
#endif

View file

@ -1,595 +0,0 @@
#include <inttypes.h>
#import "GSLinkedList.h"
#import "GSThreadPool.h"
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSDate.h>
#import <Foundation/NSLock.h>
#import <Foundation/NSThread.h>
#import <Foundation/NSString.h>
#import <Foundation/NSException.h>
#if !defined (GNUSTEP)
#import "GNUstep.h"
#endif
@class GSThreadPool;
@interface GSOperation : GSListLink
{
@public
SEL sel;
NSObject *arg;
}
@end
@implementation GSOperation
- (void) dealloc
{
[arg release];
[super dealloc];
}
@end
@interface GSThreadLink : GSListLink
{
@public
GSThreadPool *pool; // Not retained
NSConditionLock *lock;
GSOperation *op;
}
@end
@implementation GSThreadLink
- (void) dealloc
{
[lock release];
[super dealloc];
}
- (id) init
{
if ((self = [super init]) != nil)
{
lock = [[NSConditionLock alloc] initWithCondition: 0];
}
return self;
}
@end
@interface GSThreadPool (Internal)
- (void) _any;
- (void) _dead: (GSThreadLink*)link;
- (BOOL) _idle: (GSThreadLink*)link;
- (BOOL) _more: (GSThreadLink*)link;
- (void) _run: (GSThreadLink*)link;
@end
@implementation GSThreadPool
static GSThreadPool *shared = nil;
+ (void) initialize
{
if ([GSThreadPool class] == self && nil == shared)
{
shared = [self new];
}
}
+ (GSThreadPool*) sharedPool
{
return shared;
}
- (void) dealloc
{
GSThreadLink *link;
if (self == shared)
{
[self retain];
[NSException raise: NSInternalInconsistencyException
format: @"[GSThreadPool-dealloc] attempt to deallocate shared pool"];
}
[poolLock lock];
[operations release];
operations = nil;
[unused release];
unused = nil;
if (nil != idle)
{
while (nil != (link = (GSThreadLink*)idle->head))
{
GSLinkedListRemove(link, idle);
[link->lock lock];
[link->lock unlockWithCondition: 1];
}
[idle release];
idle = nil;
}
if (nil != live)
{
while (nil != (link = (GSThreadLink*)live->head))
{
GSLinkedListRemove(link, live);
link->pool = nil;
}
[live release];
live = nil;
}
[poolName release];
[poolLock unlock];
[poolLock release];
[super dealloc];
}
- (NSString*) description
{
NSString *result = [self info];
[poolLock lock];
result = [NSString stringWithFormat: @"%@ %@ %@",
[super description], poolName, result];
[poolLock unlock];
return result;
}
- (BOOL) drain: (NSDate*)before
{
BOOL result = ([self isEmpty] && [self isIdle]) ? YES : NO;
while (NO == result && [before timeIntervalSinceNow] > 0.0)
{
#if !defined (GNUSTEP) && (MAC_OS_X_VERSION_MAX_ALLOWED<=MAC_OS_X_VERSION_10_4)
NSDate *when;
when = [[NSDate alloc] initWithTimeIntervalSinceNow: 0.1];
[NSThread sleepUntilDate: when];
[when release];
#else
[NSThread sleepForTimeInterval: 0.1];
#endif
result = ([self isEmpty] && [self isIdle]) ? YES : NO;
}
return result;
}
- (NSUInteger) flush
{
NSUInteger counter;
[poolLock lock];
counter = operations->count;
[operations empty];
[poolLock unlock];
return counter;
}
- (id) init
{
if ((self = [super init]) != nil)
{
poolLock = [NSRecursiveLock new];
poolName = @"GSThreadPool";
idle = [GSLinkedList new];
live = [GSLinkedList new];
operations = [GSLinkedList new];
unused = [GSLinkedList new];
[self setOperations: 100];
[self setThreads: 2];
}
return self;
}
- (NSString*) info
{
NSString *result;
[poolLock lock];
result = [NSString stringWithFormat:
@"queue: %"PRIuPTR"(%"PRIuPTR")"
@" threads: %"PRIuPTR"(%"PRIuPTR")"
@" active: %"PRIuPTR" processed: %"PRIuPTR""
@" suspended: %s",
operations->count, maxOperations,
idle->count + live->count, maxThreads, live->count, processed,
(suspended ? "yes" : "no")];
[poolLock unlock];
return result;
}
- (BOOL) isEmpty
{
return (0 == operations->count) ? YES : NO;
}
- (BOOL) isIdle
{
return (0 == live->count) ? YES : NO;
}
- (BOOL) isSuspended
{
return suspended;
}
- (NSUInteger) maxOperations
{
return maxOperations;
}
- (NSUInteger) maxThreads
{
return maxThreads;
}
- (NSString*) poolName
{
NSString *n;
[poolLock lock];
n = RETAIN(poolName);
[poolLock unlock];
return AUTORELEASE(n);
}
- (void) resume
{
[poolLock lock];
if (YES == suspended)
{
suspended = NO;
/* No longer suspended ... start as many operations as we have idle
* threads available for.
*/
[self _any];
}
[poolLock unlock];
}
- (void) scheduleSelector: (SEL)aSelector
onReceiver: (NSObject*)aReceiver
withObject: (NSObject*)anArgument
{
if (0 == aSelector)
{
[NSException raise: NSInvalidArgumentException
format: @"Null selector"];
}
if (nil == aReceiver)
{
[NSException raise: NSInvalidArgumentException
format: @"Nil receiver"];
}
[poolLock lock];
if (operations->count < maxOperations && maxThreads > 0)
{
GSOperation *op = (GSOperation*)unused->head;
if (nil == op)
{
op = [GSOperation new]; // Need a new one
}
else
{
GSLinkedListRemove(op, unused); // Re-use an old one
}
[op setItem: aReceiver];
op->sel = aSelector;
op->arg = [anArgument retain];
GSLinkedListInsertAfter(op, operations, operations->tail);
[self _any];
[poolLock unlock];
}
else
{
NSAutoreleasePool *arp;
[poolLock unlock];
NS_DURING
{
arp = [NSAutoreleasePool new];
[aReceiver performSelector: aSelector withObject: anArgument];
[arp release];
}
NS_HANDLER
{
arp = [NSAutoreleasePool new];
NSLog(@"[%@-%@] %@",
NSStringFromClass([aReceiver class]),
NSStringFromSelector(aSelector),
localException);
[arp release];
}
NS_ENDHANDLER
}
}
- (void) setOperations: (NSUInteger)max
{
maxOperations = max;
}
- (void) setPoolName: (NSString*)aName
{
NSString *s = nil;
if (aName)
{
s = AUTORELEASE([aName copy]);
NSAssert([s isKindOfClass: [NSString class]], NSInvalidArgumentException);
}
[poolLock lock];
ASSIGN(poolName, s);
[poolLock unlock];
}
- (void) setThreads: (NSUInteger)max
{
[poolLock lock];
if (max != maxThreads)
{
maxThreads = max;
if (0 == maxThreads)
{
[poolLock unlock];
if (NO == [self drain: [NSDate dateWithTimeIntervalSinceNow: 30.0]])
{
[self flush];
}
[poolLock lock];
}
while (maxThreads < idle->count + live->count && idle->count > 0)
{
GSThreadLink *link = (GSThreadLink*)idle->head;
/* Remove thread link from the idle list, then start up the
* thread using the condition lock ... the thread will see
* that it has no operation to work with and will terminate
* itsself and release the link.
*/
GSLinkedListRemove(link, idle);
[link->lock lock];
[link->lock unlockWithCondition: 1];
}
[self _any];
}
[poolLock unlock];
}
- (void) suspend
{
[poolLock lock];
suspended = YES;
[poolLock unlock];
}
@end
@implementation GSThreadPool (Internal)
/* This method expects the global lock to already be held.
*/
- (void) _any
{
if (NO == suspended)
{
GSOperation *op;
while (nil != (op = (GSOperation*)operations->head))
{
GSThreadLink *link = (GSThreadLink*)idle->head;
if (nil == link)
{
if (maxThreads > idle->count + live->count)
{
NSThread *thread;
NSString *name;
/* Create a new link, add it to the idle list, and start the
* thread which will work with it.
*/
link = [GSThreadLink new];
link->pool = self;
GSLinkedListInsertAfter(link, idle, idle->tail);
#if !defined (GNUSTEP) && (MAC_OS_X_VERSION_MAX_ALLOWED<=MAC_OS_X_VERSION_10_4)
/* With the old thread API we can't get an NSThread object
* until after the thread has started ... so we start the
* thread and then wait for the new thread to have set the
* link item up properly.
*/
[NSThread detachNewThreadSelector: @selector(_run:)
toTarget: self
withObject: link];
while (nil == link->item)
{
NSDate *when;
when = [[NSDate alloc]
initWithTimeIntervalSinceNow: 0.001];
[NSThread sleepUntilDate: when];
[when release];
}
#else
/* New thread API ... create thread object, set it in the
* link, then start the thread.
*/
thread = [[NSThread alloc] initWithTarget: self
selector: @selector(_run:)
object: link];
if (nil == (name = poolName))
{
name = @"GSThreadPool";
}
name = [NSString stringWithFormat: @"%@-%u",
name, ++created];
[thread setName: name];
[link setItem: thread];
[thread start];
[thread release]; // Retained by link
#endif
}
else
{
break; // No idle thread to perform operation
}
}
GSLinkedListRemove(op, operations);
GSLinkedListRemove(link, idle);
GSLinkedListInsertAfter(link, live, live->tail);
link->op = op;
[link->lock lock];
[link->lock unlockWithCondition: 1];
}
}
}
- (void) _dead: (GSThreadLink*)link
{
[poolLock lock];
if (link->owner != nil)
{
GSLinkedListRemove(link, link->owner);
}
[poolLock unlock];
}
/* Make the thread link idle ... returns YES on success, NO if the thread
* should actually terminate instead.
*/
- (BOOL) _idle: (GSThreadLink*)link
{
BOOL madeIdle = YES;
[poolLock lock];
if (link->owner != nil)
{
GSLinkedListRemove(link, link->owner);
}
if (idle->count + live->count > maxThreads)
{
madeIdle = NO; // Made dead instead
}
else
{
GSLinkedListInsertAfter(link, idle, idle->tail);
}
[poolLock unlock];
return madeIdle;
}
/* If there are more operations waiting for work, move the first one from the
* operations queue into the supplied thread link.<br />
* In any case, remove the old operation.
*/
- (BOOL) _more: (GSThreadLink*)link
{
GSOperation *op = link->op;
BOOL more = NO;
[poolLock lock];
processed++;
if (unused->count < maxOperations)
{
if (nil != op->arg)
{
[op->arg release];
op->arg = nil;
}
[op setItem: nil];
GSLinkedListInsertAfter(op, unused, unused->tail);
}
else
{
[op release];
}
link->op = (GSOperation*)operations->head;
if (nil != link->op)
{
GSLinkedListRemove(link->op, operations);
more = YES;
}
[poolLock unlock];
return more;
}
- (void) _run: (GSThreadLink*)link
{
NSAutoreleasePool *arp;
#if !defined (GNUSTEP) && (MAC_OS_X_VERSION_MAX_ALLOWED<=MAC_OS_X_VERSION_10_4)
/* With the older thread API we must set up the link item *after* the
* thread starts. With the new API this is not needed as we can set
* things up and then start the thread.
*/
[link setItem: [NSThread currentThread]];
#endif
for (;;)
{
GSOperation *op;
[link->lock lockWhenCondition: 1];
//NSLog(@"locked");
op = link->op;
if (nil == op)
{
//NSLog(@"nil op");
break;
}
else
{
[link->lock unlockWithCondition: 0];
//NSLog(@"unlock");
while (nil != op)
{
NS_DURING
{
arp = [NSAutoreleasePool new];
[op->item performSelector: op->sel withObject: op->arg];
[arp release];
}
NS_HANDLER
{
arp = [NSAutoreleasePool new];
NSLog(@"[%@-%@] %@",
NSStringFromClass([op->item class]),
NSStringFromSelector(op->sel),
localException);
[arp release];
}
NS_ENDHANDLER
if (NO == [link->pool _more: link])
{
//NSLog(@"no more");
op = nil;
}
else
{
//NSLog(@"more");
op = link->op;
}
}
if (NO == [link->pool _idle: link]) // Make this idle
{
//NSLog(@"no idle");
break; // Thread should exit rather than be idle
}
}
}
arp = [NSAutoreleasePool new];
[link->pool _dead: link];
NSLog(@"Thread for %@ terminated.", self);
[arp release];
[NSThread exit]; // Will release 'link'
}
@end

View file

@ -1,5 +1,5 @@
/**
Copyright (C) 2005-2008 Free Software Foundation, Inc.
Copyright (C) 2005 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: October 2005
@ -7,16 +7,16 @@
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
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
version 2 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.
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
You should have received a copy of the GNU Library 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.
@ -26,45 +26,15 @@
#ifndef INCLUDED_GSThroughput_H
#define INCLUDED_GSThroughput_H
#import <Foundation/NSObject.h>
@class NSArray;
@class NSString;
extern NSString * const GSThroughputNotification;
extern NSString * const GSThroughputCountKey;
extern NSString * const GSThroughputMaximumKey;
extern NSString * const GSThroughputMinimumKey;
extern NSString * const GSThroughputTimeKey;
extern NSString * const GSThroughputTotalKey;
#include <Foundation/NSObject.h>
#include <Foundation/NSArray.h>
/**
* <p>The GSThroughput class is used maintain statistics about the number
* of events or the duration of operations in your software.
* </p>
* <p>For performance reasons, the class avoids locking and you must ensure
* The GSThroughput class is used maintain statistics about the number
* of events or the duration of operations in your software.<br />
* For performance reasons, the class avoids locking and you must ensure
* that an instance of the class is only ever used by a single thread
* (the one in which it was created). You are responsible for ensuring
* that a run loop runs in each thread in which you use an instance, so that
* stats can be updated for that thread every second.
* </p>
* <p>You create an instance of the class for each event/operation that you
* are interested in monitoring, and you call the -add: or -addDuration:
* method to record events.<br />
* For duration logging, you may also use the -startDuration: and
* -endDuration methods to handle adding of the amount of time taken between
* the two calls.
* </p>
* <p>To dump a record of the gathered statistics, you may call the
* -description method of an instance or the class +description method
* to dump statistics for all instances in the current thread.<br />
* If you need to gather a record for all the threads you use, you must
* generate a dump in each thread and combine the results.
* </p>
* <p>To be notified of statistics at the end of each minute, you may call
* the -enableNotifications: method for an instance. The notifications are
* generated in the thread that instance belongs to.
* </p>
* (the one in which it was created).
*/
@interface GSThroughput : NSObject
{
@ -72,30 +42,23 @@ extern NSString * const GSThroughputTotalKey;
}
/**
* Return all the current throughput measuring objects in the current thread.
* NB. This does not return instances from other threads.
* Return all the current throughput measuring objects in the current thread...
*/
+ (NSArray*) allInstances;
/**
* Return a report on all GSThroughput instances in the current thread...<br />
* This calls the [GSThroughput-description] method of the individual instances
* to get a report on each one.<br />
* The results are ordered alphabetically by name of the instances (an
* instance without a name is treated as having an empty string as a name).
* Return a report on all GSThroughput instances in the current thread...
* calls the [GSThroughput-description] method of the individual instances
* to get a report on each one.
*/
+ (NSString*) description;
/**
* Instructs the monitoring system to use a timer at the start of each second
* for keeping its idea of the current time up to date. This timer is used
* to call the +tick method in the current thread.<br />
* by all instances associated with the current thread.<br />
* Passing a value of NO for aFlag will turn off the timer for the current
* thread.<br />
* For the timer to work, the thread's runloop must be running.<br />
* Keeping the notion of the current time up to date is important for
* instances configured to record stats broken down over a number of periods,
* since the periodic breakdown must be adjusted each second.
* thread.
*/
+ (void) setTick: (BOOL)aFlag;
@ -103,33 +66,17 @@ extern NSString * const GSThroughputTotalKey;
* Updates the monitoring system's notion of the current time for all
* instances associated with the current thread.<br />
* This should be called at the start of each second (or more often) if
* you want an accurate breakdown of monitoring by the second.<br />
* If you don't want to call this yourself, you can call +setTick: to
* have it called automatically.<br />
* If you are not using any instances of the class configured to maintain
* a breakdown of stats by periods, you do not need to call this method.
* you want accurate monitoring by the second.
*/
+ (void) tick;
/**
* Add to the count of the number of transactions for the receiver.<br />
* Add to the count of the number of transactions in the current second.<br />
* You may use this method only if the receiver was initialised with
* duration logging turned off.
*/
- (void) add: (unsigned)count;
/**
* Adds a record for multiple events of the specified
* <em>total</em> duration.<br />
* This is useful where you know a lot of similar events have completed
* in a particular period of time, but can't afford to measure the
* duration of the individual events because the timing overheads
* would be too great.<br />
* You may use this method only if the receiver was initialised with
* duration logging turned on.
*/
- (void) add: (unsigned)count duration: (NSTimeInterval)length;
/**
* Adds a record for a single event of the specified duration.<br />
* You may use this method only if the receiver was initialised with
@ -138,59 +85,18 @@ extern NSString * const GSThroughputTotalKey;
- (void) addDuration: (NSTimeInterval)length;
/**
* Returns a string describing the status of the receiver.<br />
* For an instance configured to maintain a periodic breakdown of stats,
* this reports information for the current second, all seconds in the
* current minute, all minutes in the current period, and all periods
* in the configured number of periods.<br />
* For an instance configured with no periodic breakdown, this produces
* a short summary of the total count of events and, where durations are used,
* the maximum, minimum and average duration of events.
* Returns a string describing the status of the receiver for debug/reporting.
*/
- (NSString*) description;
/** Sets a flag to say whether the receiver will send GSThroughputNotification
* at the end of each minute to provide information about statistics.<br />
* The method returnes the previous setting. The initial setting is NO.<br />
* The notification object is the reciever, and the user info dictionary
* contains some or all of the following keys depending on how the receiver
* was configured:
* <deflist>
* <term>GSThroughputCountKey</term>
* <desc>The number of events recorded (unsigned integer number)</desc>
* <term>GSThroughputMaximumKey</term>
* <desc>The maximum event duration (double floating point number)</desc>
* <term>GSThroughputMinimumKey</term>
* <desc>The minimum event duration (double floating point number)
* or -1.0 if no events occurred during the minute.</desc>
* <term>GSThroughputTimeKey</term>
* <desc>The time of the start of the minute (an NSDate)</desc>
* <term>GSThroughputTotalKey</term>
* <desc>The sum of event durations (double floating point number)</desc>
* </deflist>
*/
- (BOOL) enableNotifications: (BOOL)flag;
/**
* Ends duration recording for the current event started by a matching
* call to the -startDuration: method.<br />
* Calls to this method without a matching call to -startDuration: are
* quietly ignored. This is useful if you wish to time a function or
* method by starting/ending timing before/after calling it, but also
* want the function/method to be able to end timing of itsself before
* it calls another function/method.
* You may use this method only if the receiver was initialised with
* duration logging turned on.
*/
- (void) endDuration;
/**
* Acts like -endDuration but records the duration as a total for
* count events (if count is zero then this ends the interval started
* by the corresponding -startDuration: call, but nothing is logged).<br />
* This can be used when recording multiple events where the overhead of
* timing each event individually would be too great.
*/
- (void) endDuration: (unsigned)count;
/**
* Initialises the receiver for duration logging (in the current thread only)
* for fifteen minute periods over the last twentyfour hours.
@ -198,28 +104,16 @@ extern NSString * const GSThroughputTotalKey;
- (id) init;
/** <init />
* <p>Initialises the receiver to maintain stats (for the current thread only)
* Initialises the receiver to maintain stats (for the current thread only)
* over a particular time range, specifying whether duration statistics are
* to be maintained, or just event/transaction counts.
* </p>
* <p>If the specified numberOfPeriods or minutesPerPeriod is zero, only a
* running total is maintained rather than a per-second breakdown for the
* current minute and per minute breakdown for the current period and
* period breakdown for the number of periods.
* </p>
* <p>If all instances in a thread are initialised with numberOfPeriods or
* minutesPerPeriod of zero, the +tick method does not need to be called and
* +setTick: should not be used.
* </p>
*/
- (id) initWithDurations: (BOOL)aFlag
forPeriods: (unsigned)numberOfPeriods
ofLength: (unsigned)minutesPerPeriod;
/**
* Return the name of this instance (as set using -setName:).<br />
* This is used in the -description method and for ordering instances
* in the +description method.
* Return the name of this instance (as set using -setName:)
*/
- (NSString*) name;
@ -236,10 +130,7 @@ extern NSString * const GSThroughputTotalKey;
* continues to exist up to the point where -endDuration is called,
* as the receiver will not retain it.<br />
* You may use this method only if the receiver was initialised with
* duration logging turned on.<br />
* Use of this method if the reciever does not support duration logging
* or if the method has already been called without a matching call to
* -endDuration will cause an exception to be raised.
* duration logging turned on.
*/
- (void) startDuration: (NSString*)name;

File diff suppressed because it is too large Load diff

View file

@ -7,16 +7,16 @@
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
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
version 2 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.
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
You should have received a copy of the GNU Library 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.
@ -26,7 +26,7 @@
#ifndef INCLUDED_GSTicker_H
#define INCLUDED_GSTicker_H
@class NSDate;
#include <Foundation/NSDate.h>
/**
* Returns the timestamp of the most recent call to GSTickerTimeNow().
@ -41,7 +41,7 @@ extern NSTimeInterval GSTickerTimeLast();
extern NSTimeInterval GSTickerTimeNow();
/**
* This returns the timestamp from which GSTicker was first used.
* This returns the timestamp from which GSTickerTimeNow() was first called.
*/
extern NSTimeInterval GSTickerTimeStart();

View file

@ -9,39 +9,36 @@
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
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
version 2 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.
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
You should have received a copy of the GNU Library 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.
$Date$ $Revision$
*/
#import <Foundation/NSArray.h>
#import <Foundation/NSDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSException.h>
#import <Foundation/NSThread.h>
#import <Foundation/NSString.h>
#import <Foundation/NSTimer.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSDate.h>
#include <Foundation/NSThread.h>
#include <Foundation/NSTimer.h>
#import "GSTicker.h"
#include "GSTicker.h"
static Class NSDateClass = 0;
static NSDate *startDate = nil;
static SEL tiSel = 0;
static NSTimeInterval (*tiImp)(Class,SEL) = 0;
static volatile NSTimeInterval baseTime = 0;
static volatile NSTimeInterval lastTime = 0;
static NSTimeInterval baseTime = 0;
static NSTimeInterval lastTime = 0;
static NSDate *startDate = nil;
@interface GSTickerObservation : NSObject
{
@ -66,15 +63,8 @@ static volatile NSTimeInterval lastTime = 0;
@interface GSTickerThread : NSObject
{
@public
/* NB. We retain theTimer rather than depending on the run loop to do it.
* This is because tis object is typically deallocated on thread exist,
* so the thread's run loop may be deallocated before this object is
* deallocated, and we want to be sure the timer is not already deallocated
* when we invalidate it.
*/
NSTimer *theTimer;
NSMutableArray *observers;
unsigned last;
}
@end
@ -82,25 +72,20 @@ static volatile NSTimeInterval lastTime = 0;
- (void) dealloc
{
[theTimer invalidate];
[theTimer release];
theTimer = nil;
[observers release];
observers = nil;
DESTROY(observers);
[super dealloc];
}
- (id) init
{
if (nil != (self = [super init]))
{
NSTimeInterval ti = GSTickerTimeNow();
NSTimeInterval ti = GSTickerTimeNow();
observers = [NSMutableArray new];
theTimer = [[NSTimer scheduledTimerWithTimeInterval: ti - (int)ti
target: [GSTicker class]
selector: @selector(_tick:)
userInfo: self
repeats: NO] retain];
}
observers = [NSMutableArray new];
theTimer = [NSTimer scheduledTimerWithTimeInterval: ti - (int)ti
target: [GSTicker class]
selector: @selector(_tick:)
userInfo: self
repeats: NO];
return self;
}
@end
@ -118,7 +103,7 @@ inline NSTimeInterval GSTickerTimeStart()
{
if (baseTime == 0)
{
[GSTicker class];
return GSTickerTimeNow();
}
return baseTime;
}
@ -135,7 +120,12 @@ NSTimeInterval GSTickerTimeNow()
{
if (baseTime == 0)
{
[GSTicker class];
NSDateClass = [NSDate class];
tiSel = @selector(timeIntervalSinceReferenceDate);
tiImp
= (NSTimeInterval (*)(Class,SEL))[NSDateClass methodForSelector: tiSel];
baseTime = lastTime = (*tiImp)(NSDateClass, tiSel);
return baseTime;
}
else
{
@ -151,26 +141,12 @@ NSTimeInterval GSTickerTimeNow()
baseTime -= (lastTime - now);
}
lastTime = now;
return lastTime;
}
return lastTime;
}
@implementation GSTicker
+ (void) initialize
{
if (0 == baseTime)
{
NSDateClass = [NSDate class];
tiSel = @selector(timeIntervalSinceReferenceDate);
tiImp
= (NSTimeInterval (*)(Class,SEL))[NSDateClass methodForSelector: tiSel];
baseTime = lastTime = (*tiImp)(NSDateClass, tiSel);
startDate = [[NSDateClass alloc]
initWithTimeIntervalSinceReferenceDate: baseTime];
}
}
+ (NSDate*) last
{
return [NSDateClass dateWithTimeIntervalSinceReferenceDate:
@ -179,7 +155,6 @@ NSTimeInterval GSTickerTimeNow()
+ (void) newSecond: (id)userInfo
{
return;
}
+ (NSDate*) now
@ -202,7 +177,7 @@ NSTimeInterval GSTickerTimeNow()
tt = [GSTickerThread new];
[[[NSThread currentThread] threadDictionary]
setObject: tt forKey: @"GSTickerThread"];
[tt release];
RELEASE(tt);
}
count = [tt->observers count];
while (count-- > 0)
@ -218,7 +193,7 @@ NSTimeInterval GSTickerTimeNow()
to->observer = anObject;
to->userInfo = userInfo;
[tt->observers addObject: to];
[to release];
RELEASE(to);
}
+ (NSDate*) start
@ -274,7 +249,6 @@ NSTimeInterval GSTickerTimeNow()
- (void) newSecond: (id)userInfo
{
return;
}
- (NSDate*) now
@ -285,6 +259,12 @@ NSTimeInterval GSTickerTimeNow()
- (NSDate*) start
{
if (startDate == nil)
{
startDate = [NSDateClass alloc];
startDate = [startDate initWithTimeIntervalSinceReferenceDate:
GSTickerTimeStart()];
}
return startDate;
}
@ -313,42 +293,23 @@ NSTimeInterval GSTickerTimeNow()
if (tt != nil && [tt->observers count] > 0)
{
NSTimeInterval ti;
NSArray *a = [tt->observers copy];
[tt->theTimer invalidate];
[tt->theTimer release];
tt->theTimer = nil;
if ([tt->observers count] > 0)
{
NSArray *a;
unsigned tick;
GSTickerTimeNow();
tick = GSTickerTimeTick();
if (tick != tt->last)
{
tt->last = tick;
a = [tt->observers copy];
NS_DURING
{
[a makeObjectsPerformSelector: @selector(fire:)
withObject: tt->observers];
}
NS_HANDLER
{
NSLog(@"Problem firing ticker observers: %@", localException);
}
NS_ENDHANDLER
[a release];
}
}
if (tt->theTimer != t)
{
[tt->theTimer invalidate];
tt->theTimer = nil;
}
[a makeObjectsPerformSelector: @selector(fire:)
withObject: tt->observers];
RELEASE(a);
ti = GSTickerTimeNow();
tt->theTimer = [[NSTimer scheduledTimerWithTimeInterval: ti - (int)ti
target: self
selector: @selector(_tick:)
userInfo: tt
repeats: NO] retain];
tt->theTimer = [NSTimer scheduledTimerWithTimeInterval: ti - (int)ti
target: self
selector: @selector(_tick:)
userInfo: tt
repeats: NO];
}
else
{

View file

@ -1,80 +0,0 @@
#if !defined(INCLUDED_GSUNIQUED)
#define INCLUDED_GSUNIQUED 1
/**
Copyright (C) 2014 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: April 2014
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 used to unique other objects.<br />
* <p>The point of this class is to lower the memory footprint and speed
* up comparisons (pointer equality) in cases where an application
* stores multiple copies of the same object in various maps.<br />
* Since uniquing is performed by storing an immutable copy of the
* original object in a map until there are no further references
* to that object, it's pointless to use this uniquing unless the
* application would be storing at least two copies of the object.<br />
* Also, since this is thread-safe there is a lock management
* overhead wherever a uniqued object is released, so performance
* gains are to be expected only if the uniqued object has a
* relatively long lifetime and is tested for equality with other
* instances frequently.<br />
* In short, use with care; while uniquing can have a big performance
* advantage for some programs, this is actually quite rare.
* </p>
* <p>The internal implementation of the uniquing works by taking
* immutable copies of the objects to be uniqued, storing those copies
* in a hash table, and swizzling their class pointers to a sub-class
* which will automatically remove the instance from the hash table
* before it is deallocated.<br />
* Access to the hash table is protected by locks so that uniqued
* objects may be used freely in multiple threads.<br />
* The name of the subclass used is the name of the original class
* with 'GSUniqued' added as a prefix.
* </p>
*/
@interface GSUniqued : NSObject
/** This method returns a copy of its argument, uniqued so that other
* such copies of equal objects will be the same instance.<br />
* The argument must respond to -copyWithZone: by returning an instance
* of class of immutable objects (ie where the -hash and -isEqual:
* methods are stable for that instance).
*/
+ (id) copyUniqued: (id<NSObject,NSCopying>)anObject;
@end
/** Category for uniquing any copyable object.<br />
* NB. This must only be used by classes for which -copyWithZone:
* produces an instance of an immutable class.
*/
@interface NSObject (GSUniqued)
/** This method returns a copy of the receiver uniqued so that other
* such copies of equal objects content will be the same instance.
*/
- (id) copyUniqued;
@end
#endif

View file

@ -1,213 +0,0 @@
/**
Copyright (C) 2014 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: April 2014
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/NSDictionary.h>
#import <Foundation/NSException.h>
#import <Foundation/NSHashTable.h>
#import <Foundation/NSLock.h>
#import <Foundation/NSObjCRuntime.h>
#import <Foundation/NSSet.h>
#import <GNUstepBase/GSObjCRuntime.h>
#import "GSUniqued.h"
static Class GSUniquedClass = Nil;
static NSLock *uniquedObjectsLock;
static IMP iLock;
static IMP iUnlock;
static NSHashTable *uniquedObjects;
static NSLock *classLock;
static NSMutableDictionary *classMap;
/* Deallocate a uniqued object ... we must remove it from the uniqued
* objects table and then call the real -dealloc method.
*/
static void
uDealloc(id self, SEL _cmd)
{
Class c;
IMP i;
NSHashRemove(uniquedObjects, self);
c = object_getClass(self);
c = class_getSuperclass(c);
i = class_getMethodImplementation(c, _cmd);
(*i)(self, _cmd);
}
/* Release a uniqued object ... we must obtain a lock in case the uniqued
* objects table has to be modified by removal of this instance on
* deallocation.
*/
static void
uRelease(id self, SEL _cmd)
{
Class c;
IMP i;
c = object_getClass(self);
c = class_getSuperclass(c);
i = class_getMethodImplementation(c, _cmd);
(*iLock)(uniquedObjectsLock, @selector(lock));
(*i)(self, _cmd);
(*iUnlock)(uniquedObjectsLock, @selector(unlock));
}
@implementation GSUniqued
static Class NSObjectClass;
+ (void) initialize
{
if (Nil == GSUniquedClass)
{
classLock = [NSLock new];
classMap = [NSMutableDictionary new];
uniquedObjectsLock = [NSLock new];
iLock = [uniquedObjectsLock methodForSelector: @selector(lock)];
iUnlock = [uniquedObjectsLock methodForSelector: @selector(unlock)];
uniquedObjects = NSCreateHashTable(
NSNonRetainedObjectHashCallBacks, 10000);
NSObjectClass = [NSObject class];
GSUniquedClass = [GSUniqued class];
}
}
+ (id) allocWithZone: (NSZone*)z
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to allocate instance of GSUniqued"];
return nil;
}
+ (id) copyUniqued: (id<NSObject,NSCopying>)anObject
{
NSObject *found;
NSAssert(nil != anObject, NSInvalidArgumentException);
(*iLock)(uniquedObjectsLock, @selector(lock));
found = [(NSObject*)NSHashGet(uniquedObjects, anObject) retain];
(*iUnlock)(uniquedObjectsLock, @selector(unlock));
if (nil == found)
{
NSObject *aCopy;
Class c;
Class u;
aCopy = [anObject copyWithZone: NSDefaultMallocZone()];
if (aCopy == anObject)
{
/* Unable to make a copy ... that probably means the object
* is already unique and we can just return it.
*/
if (NO == [aCopy isKindOfClass: [NSString class]])
{
return aCopy;
}
else
{
NSMutableString *m;
/* We have different subclasses of NSString and we can't swizzle
* a constant string, so in that case we need to work with another
* type of string.
*/
m = [aCopy mutableCopy];
[aCopy release];
aCopy = [m copy];
[m release];
}
}
c = object_getClass(aCopy);
[classLock lock];
u = [classMap objectForKey: (id<NSCopying>)c];
if (Nil == u)
{
const char *cn = class_getName(c);
char name[strlen(cn) + 20];
Method method;
sprintf(name, "GSUniqued%s", cn);
u = objc_allocateClassPair(c, name, 0);
method = class_getInstanceMethod(NSObjectClass,
@selector(dealloc));
class_addMethod(u, @selector(dealloc),
(IMP)uDealloc, method_getTypeEncoding(method));
method = class_getInstanceMethod(NSObjectClass,
@selector(release));
class_addMethod(u, @selector(release),
(IMP)uRelease, method_getTypeEncoding(method));
method = class_getInstanceMethod(GSUniquedClass,
@selector(copyUniqued));
class_addMethod(u, @selector(copyUniqued),
class_getMethodImplementation(GSUniquedClass,
@selector(copyUniqued)),
method_getTypeEncoding(method));
objc_registerClassPair(u);
[classMap setObject: u forKey: (id<NSCopying>)c];
}
[classLock unlock];
(*iLock)(uniquedObjectsLock, @selector(lock));
found = [(NSObject*)NSHashGet(uniquedObjects, anObject) retain];
if (nil == found)
{
found = aCopy;
#if defined(GNUSTEP)
GSClassSwizzle(found, u);
#else
object_setClass(found, u);
#endif
NSHashInsert(uniquedObjects, found);
}
else
{
[aCopy release]; // Already uniqued by another thread
}
(*iUnlock)(uniquedObjectsLock, @selector(unlock));
}
return found;
}
- (id) copyUniqued
{
return [self retain];
}
@end
@implementation NSObject (GSUniqued)
- (id) copyUniqued
{
if (Nil == GSUniquedClass) [GSUniqued class];
return [GSUniquedClass copyUniqued: (id<NSObject,NSCopying>)self];
}
@end

View file

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>org.gnustep.performance</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View file

@ -1,52 +0,0 @@
/** Declaration of extension methods for base additions
Copyright (C) 2003-2010 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
and: Adam Fedor <fedor@gnu.org>
This file is part of the GNUstep Base 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 2 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
Library 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., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
*/
#import <Foundation/NSObject.h>
#if !defined (GNUSTEP)
#import "GNUstep.h"
#endif
@class NSHashTable;
@interface NSObject(MemoryFootprint)
/* This method returns the memory usage of the receiver, excluding any
* objects already present in the exclude table.<br />
* The argument is a hash table configured to hold non-retained pointer
* objects and is used to inform the receiver that its size should not
* be counted again if it's already in the table.<br />
* The NSObject implementation returns zero if the receiver is in the
* table, but otherwise adds itself to the table and returns its memory
* footprint (the sum of all of its instance variables, but not any
* memory pointed to by those variables).<br />
* Subclasses should override this method by calling the superclass
* implementation, and either return the result (if it was zero) or
* return that value plus the sizes of any memory owned by the receiver
* (eg found by calling the same method on objects pointed to by the
* receiver's instance variables).
*/
- (NSUInteger) sizeInBytesExcluding: (NSHashTable*)exclude;
@end

View file

@ -1,42 +0,0 @@
/* Implementation of extension methods to base additions
Copyright (C) 2010 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
This file is part of the GNUstep Base 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 2 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
Library 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., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
*/
#import <Foundation/NSHashTable.h>
@implementation NSObject (MemoryFootprint)
+ (NSUInteger) sizeInBytesExcluding: (NSHashTable*)exclude
{
return 0;
}
- (NSUInteger) sizeInBytesExcluding: (NSHashTable*)exclude
{
if (0 == NSHashGet(exclude, self))
{
NSHashInsert(exclude, self);
return class_getInstanceSize(object_getClass(self));
}
return 0;
}
@end

View file

@ -1,5 +1,5 @@
/*
JIGS import file for the GNUstep Performance Library.
JIGS include file for the GNUstep Performance Library.
Copyright (C) 2005 Free Software Foundation, Inc.
@ -9,28 +9,21 @@
This file is part of the GNUstep Performance Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
version 2 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.
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.
*/
#import "GSCache.h"
#import "GSFIFO.h"
#import "GSIOThreadPool.h"
#import "GSLinkedList.h"
#import "GSThreadPool.h"
#import "GSThroughput.h"
#import "GSTicker.h"
#import "GSSkipMutableArray.h"
#import "GSUniqued.h"
#include "GSCache.h"
#include "GSThroughput.h"
#include "GSTicker.h"

View file

@ -1,395 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
85872EC11284CFC700B4601E /* GSCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 85872EB01284CFC700B4601E /* GSCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
85872EC21284CFC700B4601E /* GSCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 85872EB11284CFC700B4601E /* GSCache.m */; };
85872EC31284CFC700B4601E /* GSIndexedSkipList.h in Headers */ = {isa = PBXBuildFile; fileRef = 85872EB21284CFC700B4601E /* GSIndexedSkipList.h */; settings = {ATTRIBUTES = (Public, ); }; };
85872EC41284CFC700B4601E /* GSIndexedSkipList.m in Sources */ = {isa = PBXBuildFile; fileRef = 85872EB31284CFC700B4601E /* GSIndexedSkipList.m */; };
85872EC51284CFC700B4601E /* GSIOThreadPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 85872EB41284CFC700B4601E /* GSIOThreadPool.h */; settings = {ATTRIBUTES = (Public, ); }; };
85872EC61284CFC700B4601E /* GSIOThreadPool.m in Sources */ = {isa = PBXBuildFile; fileRef = 85872EB51284CFC700B4601E /* GSIOThreadPool.m */; };
85872EC71284CFC700B4601E /* GSLinkedList.h in Headers */ = {isa = PBXBuildFile; fileRef = 85872EB61284CFC700B4601E /* GSLinkedList.h */; settings = {ATTRIBUTES = (Public, ); }; };
85872EC81284CFC700B4601E /* GSLinkedList.m in Sources */ = {isa = PBXBuildFile; fileRef = 85872EB71284CFC700B4601E /* GSLinkedList.m */; };
85872EC91284CFC700B4601E /* GSSkipMutableArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 85872EB81284CFC700B4601E /* GSSkipMutableArray.h */; settings = {ATTRIBUTES = (Public, ); }; };
85872ECA1284CFC700B4601E /* GSSkipMutableArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 85872EB91284CFC700B4601E /* GSSkipMutableArray.m */; };
85872ECB1284CFC700B4601E /* GSThreadPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 85872EBA1284CFC700B4601E /* GSThreadPool.h */; settings = {ATTRIBUTES = (Public, ); }; };
85872ECC1284CFC700B4601E /* GSThreadPool.m in Sources */ = {isa = PBXBuildFile; fileRef = 85872EBB1284CFC700B4601E /* GSThreadPool.m */; };
85872ECD1284CFC700B4601E /* GSThroughput.h in Headers */ = {isa = PBXBuildFile; fileRef = 85872EBC1284CFC700B4601E /* GSThroughput.h */; settings = {ATTRIBUTES = (Public, ); }; };
85872ECE1284CFC700B4601E /* GSThroughput.m in Sources */ = {isa = PBXBuildFile; fileRef = 85872EBD1284CFC700B4601E /* GSThroughput.m */; };
85872ECF1284CFC700B4601E /* GSTicker.h in Headers */ = {isa = PBXBuildFile; fileRef = 85872EBE1284CFC700B4601E /* GSTicker.h */; settings = {ATTRIBUTES = (Public, ); }; };
85872ED01284CFC700B4601E /* GSTicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 85872EBF1284CFC700B4601E /* GSTicker.m */; };
85872ED11284CFC700B4601E /* Performance.h in Headers */ = {isa = PBXBuildFile; fileRef = 85872EC01284CFC700B4601E /* Performance.h */; settings = {ATTRIBUTES = (Public, ); }; };
85B7DBDC1C034FBA00AF3090 /* NSObject+GSExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 85B7DBDA1C034FBA00AF3090 /* NSObject+GSExtensions.h */; };
85B7DBDD1C034FBA00AF3090 /* NSObject+GSExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 85B7DBDB1C034FBA00AF3090 /* NSObject+GSExtensions.m */; };
85B7DC031C036A0900AF3090 /* GSFIFO.h in Headers */ = {isa = PBXBuildFile; fileRef = 85B7DC011C036A0900AF3090 /* GSFIFO.h */; };
85B7DC041C036A0900AF3090 /* GSFIFO.m in Sources */ = {isa = PBXBuildFile; fileRef = 85B7DC021C036A0900AF3090 /* GSFIFO.m */; };
85DA4C012C41D1A900FC0E77 /* GNUstep.h in Headers */ = {isa = PBXBuildFile; fileRef = 85DA4C002C41D1A900FC0E77 /* GNUstep.h */; };
8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; };
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
0867D69BFE84028FC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
089C1667FE841158C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
85872EB01284CFC700B4601E /* GSCache.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSCache.h; sourceTree = "<group>"; };
85872EB11284CFC700B4601E /* GSCache.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSCache.m; sourceTree = "<group>"; };
85872EB21284CFC700B4601E /* GSIndexedSkipList.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSIndexedSkipList.h; sourceTree = "<group>"; };
85872EB31284CFC700B4601E /* GSIndexedSkipList.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSIndexedSkipList.m; sourceTree = "<group>"; };
85872EB41284CFC700B4601E /* GSIOThreadPool.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSIOThreadPool.h; sourceTree = "<group>"; };
85872EB51284CFC700B4601E /* GSIOThreadPool.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSIOThreadPool.m; sourceTree = "<group>"; };
85872EB61284CFC700B4601E /* GSLinkedList.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSLinkedList.h; sourceTree = "<group>"; };
85872EB71284CFC700B4601E /* GSLinkedList.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSLinkedList.m; sourceTree = "<group>"; };
85872EB81284CFC700B4601E /* GSSkipMutableArray.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSSkipMutableArray.h; sourceTree = "<group>"; };
85872EB91284CFC700B4601E /* GSSkipMutableArray.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSSkipMutableArray.m; sourceTree = "<group>"; };
85872EBA1284CFC700B4601E /* GSThreadPool.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSThreadPool.h; sourceTree = "<group>"; };
85872EBB1284CFC700B4601E /* GSThreadPool.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSThreadPool.m; sourceTree = "<group>"; };
85872EBC1284CFC700B4601E /* GSThroughput.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSThroughput.h; sourceTree = "<group>"; };
85872EBD1284CFC700B4601E /* GSThroughput.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSThroughput.m; sourceTree = "<group>"; };
85872EBE1284CFC700B4601E /* GSTicker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSTicker.h; sourceTree = "<group>"; };
85872EBF1284CFC700B4601E /* GSTicker.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSTicker.m; sourceTree = "<group>"; };
85872EC01284CFC700B4601E /* Performance.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Performance.h; sourceTree = "<group>"; };
8591FBED1A13422800923420 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ChangeLog; sourceTree = "<group>"; };
85B560B3128C6E47003BAF08 /* Performance.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Performance.framework; sourceTree = BUILT_PRODUCTS_DIR; };
85B7DBDA1C034FBA00AF3090 /* NSObject+GSExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+GSExtensions.h"; sourceTree = "<group>"; };
85B7DBDB1C034FBA00AF3090 /* NSObject+GSExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+GSExtensions.m"; sourceTree = "<group>"; };
85B7DC011C036A0900AF3090 /* GSFIFO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GSFIFO.h; sourceTree = "<group>"; };
85B7DC021C036A0900AF3090 /* GSFIFO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GSFIFO.m; sourceTree = "<group>"; };
85DA4C002C41D1A900FC0E77 /* GNUstep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GNUstep.h; sourceTree = "<group>"; };
8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
D2F7E79907B2D74100F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
8DC2EF560486A6940098B216 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
034768DFFF38A50411DB9C8B /* Products */ = {
isa = PBXGroup;
children = (
85B560B3128C6E47003BAF08 /* Performance.framework */,
);
name = Products;
sourceTree = "<group>";
};
0867D691FE84028FC02AAC07 /* Performance */ = {
isa = PBXGroup;
children = (
08FB77AEFE84172EC02AAC07 /* Classes */,
32C88DFF0371C24200C91783 /* Other Sources */,
089C1665FE841158C02AAC07 /* Resources */,
0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */,
034768DFFF38A50411DB9C8B /* Products */,
);
name = Performance;
sourceTree = "<group>";
};
0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */ = {
isa = PBXGroup;
children = (
1058C7B0FEA5585E11CA2CBB /* Linked Frameworks */,
1058C7B2FEA5585E11CA2CBB /* Other Frameworks */,
);
name = "External Frameworks and Libraries";
sourceTree = "<group>";
};
089C1665FE841158C02AAC07 /* Resources */ = {
isa = PBXGroup;
children = (
8591FBED1A13422800923420 /* ChangeLog */,
8DC2EF5A0486A6940098B216 /* Info.plist */,
089C1666FE841158C02AAC07 /* InfoPlist.strings */,
);
name = Resources;
sourceTree = "<group>";
};
08FB77AEFE84172EC02AAC07 /* Classes */ = {
isa = PBXGroup;
children = (
85B7DC011C036A0900AF3090 /* GSFIFO.h */,
85B7DC021C036A0900AF3090 /* GSFIFO.m */,
85872EB01284CFC700B4601E /* GSCache.h */,
85872EB11284CFC700B4601E /* GSCache.m */,
85872EB21284CFC700B4601E /* GSIndexedSkipList.h */,
85872EB31284CFC700B4601E /* GSIndexedSkipList.m */,
85872EB41284CFC700B4601E /* GSIOThreadPool.h */,
85872EB51284CFC700B4601E /* GSIOThreadPool.m */,
85872EB61284CFC700B4601E /* GSLinkedList.h */,
85872EB71284CFC700B4601E /* GSLinkedList.m */,
85872EB81284CFC700B4601E /* GSSkipMutableArray.h */,
85872EB91284CFC700B4601E /* GSSkipMutableArray.m */,
85872EBA1284CFC700B4601E /* GSThreadPool.h */,
85872EBB1284CFC700B4601E /* GSThreadPool.m */,
85872EBC1284CFC700B4601E /* GSThroughput.h */,
85872EBD1284CFC700B4601E /* GSThroughput.m */,
85872EBE1284CFC700B4601E /* GSTicker.h */,
85872EBF1284CFC700B4601E /* GSTicker.m */,
85872EC01284CFC700B4601E /* Performance.h */,
85B7DBDA1C034FBA00AF3090 /* NSObject+GSExtensions.h */,
85B7DBDB1C034FBA00AF3090 /* NSObject+GSExtensions.m */,
85DA4C002C41D1A900FC0E77 /* GNUstep.h */,
);
name = Classes;
sourceTree = "<group>";
};
1058C7B0FEA5585E11CA2CBB /* Linked Frameworks */ = {
isa = PBXGroup;
children = (
1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */,
);
name = "Linked Frameworks";
sourceTree = "<group>";
};
1058C7B2FEA5585E11CA2CBB /* Other Frameworks */ = {
isa = PBXGroup;
children = (
0867D6A5FE840307C02AAC07 /* AppKit.framework */,
D2F7E79907B2D74100F64583 /* CoreData.framework */,
0867D69BFE84028FC02AAC07 /* Foundation.framework */,
);
name = "Other Frameworks";
sourceTree = "<group>";
};
32C88DFF0371C24200C91783 /* Other Sources */ = {
isa = PBXGroup;
children = (
);
name = "Other Sources";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
8DC2EF500486A6940098B216 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
85872EC11284CFC700B4601E /* GSCache.h in Headers */,
85872EC31284CFC700B4601E /* GSIndexedSkipList.h in Headers */,
85872EC51284CFC700B4601E /* GSIOThreadPool.h in Headers */,
85872EC71284CFC700B4601E /* GSLinkedList.h in Headers */,
85872EC91284CFC700B4601E /* GSSkipMutableArray.h in Headers */,
85DA4C012C41D1A900FC0E77 /* GNUstep.h in Headers */,
85872ECB1284CFC700B4601E /* GSThreadPool.h in Headers */,
85872ECD1284CFC700B4601E /* GSThroughput.h in Headers */,
85872ECF1284CFC700B4601E /* GSTicker.h in Headers */,
85872ED11284CFC700B4601E /* Performance.h in Headers */,
85B7DBDC1C034FBA00AF3090 /* NSObject+GSExtensions.h in Headers */,
85B7DC031C036A0900AF3090 /* GSFIFO.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
8DC2EF4F0486A6940098B216 /* Performance */ = {
isa = PBXNativeTarget;
buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Performance" */;
buildPhases = (
8DC2EF500486A6940098B216 /* Headers */,
8DC2EF520486A6940098B216 /* Resources */,
8DC2EF540486A6940098B216 /* Sources */,
8DC2EF560486A6940098B216 /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Performance;
productInstallPath = "$(HOME)/Library/Frameworks";
productName = Performance;
productReference = 85B560B3128C6E47003BAF08 /* Performance.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
0867D690FE84028FC02AAC07 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0510;
ORGANIZATIONNAME = gnustep.org;
};
buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Performance" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 1;
knownRegions = (
English,
Japanese,
French,
German,
);
mainGroup = 0867D691FE84028FC02AAC07 /* Performance */;
productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
8DC2EF4F0486A6940098B216 /* Performance */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
8DC2EF520486A6940098B216 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
8DC2EF540486A6940098B216 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
85872EC21284CFC700B4601E /* GSCache.m in Sources */,
85872EC41284CFC700B4601E /* GSIndexedSkipList.m in Sources */,
85872EC61284CFC700B4601E /* GSIOThreadPool.m in Sources */,
85872EC81284CFC700B4601E /* GSLinkedList.m in Sources */,
85872ECA1284CFC700B4601E /* GSSkipMutableArray.m in Sources */,
85872ECC1284CFC700B4601E /* GSThreadPool.m in Sources */,
85872ECE1284CFC700B4601E /* GSThroughput.m in Sources */,
85872ED01284CFC700B4601E /* GSTicker.m in Sources */,
85B7DBDD1C034FBA00AF3090 /* NSObject+GSExtensions.m in Sources */,
85B7DC041C036A0900AF3090 /* GSFIFO.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
089C1666FE841158C02AAC07 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
089C1667FE841158C02AAC07 /* English */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
1DEB91AE08733DA50010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
GCC_CW_ASM_SYNTAX = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_CPP_EXCEPTIONS = YES;
GCC_ENABLE_CPP_RTTI = YES;
GCC_ENABLE_EXCEPTIONS = YES;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_ENABLE_PASCAL_STRINGS = NO;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = NO;
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(HOME)/Library/Frameworks";
PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = YES;
PRODUCT_NAME = Performance;
SDKROOT = macosx;
WRAPPER_EXTENSION = framework;
ZERO_LINK = YES;
};
name = Debug;
};
1DEB91AF08733DA50010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
GCC_ENABLE_CPP_EXCEPTIONS = YES;
GCC_ENABLE_CPP_RTTI = YES;
GCC_ENABLE_EXCEPTIONS = YES;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_MODEL_TUNING = G5;
GCC_PRECOMPILE_PREFIX_HEADER = NO;
GCC_PREFIX_HEADER = "";
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(HOME)/Library/Frameworks";
PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO;
PRODUCT_NAME = Performance;
SDKROOT = macosx;
WRAPPER_EXTENSION = framework;
};
name = Release;
};
1DEB91B208733DA50010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
ONLY_ACTIVE_ARCH = YES;
PREBINDING = NO;
PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = YES;
PRODUCT_NAME = Performance;
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
SYMROOT = "$(PROJECT_DIR)/build";
};
name = Debug;
};
1DEB91B308733DA50010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_MODEL_TUNING = "";
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
PREBINDING = NO;
PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = YES;
PRODUCT_NAME = Performance;
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
SYMROOT = "$(PROJECT_DIR)/build";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Performance" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1DEB91AE08733DA50010E9CD /* Debug */,
1DEB91AF08733DA50010E9CD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Performance" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1DEB91B208733DA50010E9CD /* Debug */,
1DEB91B308733DA50010E9CD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 0867D690FE84028FC02AAC07 /* Project object */;
}

56
RELEASE_NOTES Normal file
View file

@ -0,0 +1,56 @@
This is the initial release of the 'performance' library (version 0.1.0)
Scope
====
This library is a collection of classes intended to be used to help
improve the performance of GNUstep and Cocoa applications. The scope
of the library is therefore -
1. Subclasses of standard Cocoa classes which are optimised for particular
uses.
2. Classes to perform tasks which can improve application performance
by mechanisms not covered by existing classes.
3. Classes to monitor/analyse performance issues so you can tell what
needs to be optimised.
Platforms
=========
GNUstep gnu/linux
GNUstep windows/mingw32
Cocoa ... reported to work.
Contents
========
GSTicker ... functions for efficient timing where great accuracy is not
required (eg. nearest second is OK), but the overhead of getting the system
time many times per second would be a problem. This code is used by the
GSThroughput class.
GSThroughput ... class to maintain throughput/transaction count information
to enable monitoring of application performance over long periods of time.
GSCache ... a least-recently-used cache with cahcne size limits controlled
by the number of objects in the cache or the amount of memory used by the
cache or both. This class is used by the SQLClient library to handle
in-memory caching of information retrieved from databases.
Location
========
Available from the gnustep subversion repository.
Browse at http://svn.gna.org/viewcvs/gnustep/libs/performance/tags/0.1.0
Download with:
svn co svn://svn.gna.org/svn/gnustep/libs/performance/tags/0.1.0 performance
Bugs
====
Please report bugs to https://savannah.gnu.org/bugs/?group=gnustep selecting
'Libraries' as the catagory. NB. please check to see if a bug has already
been reported before submitting a new one.