mirror of
https://github.com/gnustep/libs-performance.git
synced 2025-04-21 00:41:15 +00:00
Compare commits
3 commits
Author | SHA1 | Date | |
---|---|---|---|
|
31d3e95305 | ||
|
ec8b8ec81b | ||
|
7c994f42e2 |
31 changed files with 595 additions and 7840 deletions
166
COPYING.LIB
166
COPYING.LIB
|
@ -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
383
ChangeLog
|
@ -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.
67
GNUmakefile
67
GNUmakefile
|
@ -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
184
GNUstep.h
|
@ -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
165
GSCache.h
|
@ -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
676
GSCache.m
|
@ -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
421
GSFIFO.h
|
@ -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
|
152
GSIOThreadPool.h
152
GSIOThreadPool.h
|
@ -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
|
426
GSIOThreadPool.m
426
GSIOThreadPool.m
|
@ -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
|
||||
|
|
@ -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);
|
||||
|
|
@ -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;
|
||||
}
|
440
GSLinkedList.h
440
GSLinkedList.h
|
@ -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
|
655
GSLinkedList.m
655
GSLinkedList.m
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
147
GSThreadPool.h
147
GSThreadPool.h
|
@ -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
|
595
GSThreadPool.m
595
GSThreadPool.m
|
@ -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
|
||||
|
159
GSThroughput.h
159
GSThroughput.h
|
@ -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;
|
||||
|
||||
|
|
809
GSThroughput.m
809
GSThroughput.m
File diff suppressed because it is too large
Load diff
12
GSTicker.h
12
GSTicker.h
|
@ -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();
|
||||
|
||||
|
|
139
GSTicker.m
139
GSTicker.m
|
@ -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
|
||||
{
|
||||
|
|
80
GSUniqued.h
80
GSUniqued.h
|
@ -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
|
||||
|
213
GSUniqued.m
213
GSUniqued.m
|
@ -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
|
||||
|
26
Info.plist
26
Info.plist
|
@ -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>
|
|
@ -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
|
|
@ -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
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
56
RELEASE_NOTES
Normal 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.
|
||||
|
||||
|
Loading…
Reference in a new issue