pax_global_header00006660000000000000000000000064143602626620014521gustar00rootroot0000000000000052 comment=5cc01b1ef40d2c50c74bf16efe496ee3e737c4d6 Performance-0.6.0/000077500000000000000000000000001436026266200137655ustar00rootroot00000000000000Performance-0.6.0/COPYING.LIB000066400000000000000000000167301436026266200154340ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. 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. Performance-0.6.0/ChangeLog000066400000000000000000000250361436026266200155450ustar00rootroot000000000000002023-01-13 Richard Frith-Macdonald * GNUmakefile: bump version for 0.6.0 release 2022-07-06 Richard Frith-Macdonald * 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 * 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 * GSIOThreadPool.m: Make the code use NSThread's designated initialiser so that GSIOThread can be subclassed safely. 2017-07-13 Richard Frith-Macdonald * 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 * GSFIFO.m: Add method for writing a whole block of data to a FIFO in one go. 2015-07-29 Richard Frith-Macdonald * 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 * 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 * GSFIFO.m: Implement -sizeInBytesExcluding: 2015-07-15 Richard Frith-Macdonald * 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 * GSFIFO.m: Fix a potential race condition in -peekObject. 2015-05-05 Niels Grewe * GSFIFO.[hm]: Add methods to peek at the top/front object in the FIFO without removing it. 2015-04-28 Niels Grewe * 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 * GSFIFO.[hm]: add methods for working with objects and handling retain/release to treat a FIFO as a container. 2014-11-12 Riccardo Mottola * GSThreadPool.m * GSSkipMutableArray.m * GSCache.m Import inttypes.h for pointer formatting 2014-11-12 Riccardo Mottola * GSThreadPool.h Forward-declare NSRecursiveLock, not NSLock 2014-07-11 Yavor Doganov * GNUmakefile (LIBRARIES_DEPEND_UPON): Define. 2014-04-26 Richard Frith-Macdonald * 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 * GSFIFO.m: Fix calculation of the timeout for cooperating get/put. 2013-08-21 Richard Frith-Macdonald * 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 * 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 * GSThreadPool.h, GSCache.m shutup clang warnings 2013-02-03 Richard Frith-Macdonald * 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 * GSSkipMutableArray.m: Change -initWithObjects:count: declaration to match the superclass one in gnustep-base trunk. 2011-10-25 Richard Frith-Macdonald * 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 * GSCache.m: Try to fool clang/llvm to avoid useless warning/error. 2011-08-24 Richard Frith-Macdonald * GSFIFO.h: * GSFIFO.m: Use NSCondition to support multiple producer/consumer on FIFO. 2011-06-11 Richard Frith-Macdonald * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * GSIOThreadPool.h: * GSIOThreadPool.m: Add new methed class for pooling threads to handle I/O and timers. 2010-09-30 Richard Frith-Macdonald * GSThreadPool.h: * GSThreadPool.m: Add new methed to return a shared pool. 2010-09-29 Richard Frith-Macdonald * 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 * 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 * 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 * GSSkipMutableArray.m: Fix uninitialised variable. 2008-05-30 Richard Frith-Macdonald * 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 * 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 * 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 * 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 Update to LGPL3 2007-05-30 Richard Frith-Macdonald * GSTicker.m: Ensure tick is current before calling -newSecond: 2007-04-01 Richard Frith-Macdonald * 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 * GSMutableSkipArray.m: Fix bug when deallocating an empty list. 2006-11-11 Richard Frith-Macdonald * 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 * GNUmakefile: update version * GSThroughput.m: Allow use to keep running totals of stats 2006-09-27 Richard Frith-Macdonald * 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 * GSSkipMutableArray.[hm]: New NSMutableArray subclass. * GSIndexedSkipList.[hm]: Underlying C implementation. 2006-06-07 Richard Frith-Macdonald * GSCache.h: * GSCache.m: New convenience method to cache an item until a date. 2006-01-11 Nicola Pero * GNUmakefile.wrapper.objc.preamble: New file that fixes the "/usr/bin/ld: cannot find -lPerformance" error when linking Java wrappers. 2005-10-28 Richard Frith-Macdonald New Performance library (0.1) Performance-0.6.0/English.lproj/000077500000000000000000000000001436026266200165035ustar00rootroot00000000000000Performance-0.6.0/English.lproj/InfoPlist.strings000066400000000000000000000002701436026266200220240ustar00rootroot00000000000000þÿ/* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© GNUstep, 2010"; Performance-0.6.0/GNUmakefile000066400000000000000000000040311436026266200160350ustar00rootroot00000000000000 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) TEST_TOOL_NAME= LIBRARY_NAME=Performance DOCUMENT_NAME=Performance 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 \ # Optional Java wrappers for the library JAVA_WRAPPER_NAME = Performance Performance_HEADER_FILES_INSTALL_DIR = Performance -include GNUmakefile.preamble include $(GNUSTEP_MAKEFILES)/library.make # If JIGS is installed, automatically generate Java wrappers as well. # Because of the '-', should not complain if java-wrapper.make can't be # found ... simply skip generation of java wrappers in that case. -include $(GNUSTEP_MAKEFILES)/java-wrapper.make include $(GNUSTEP_MAKEFILES)/test-tool.make include $(GNUSTEP_MAKEFILES)/documentation.make -include GNUmakefile.postamble Performance-0.6.0/GNUmakefile.postamble000066400000000000000000000015261436026266200200300ustar00rootroot00000000000000# # Makefile.postamble # # Project specific makefile rules # # Uncomment the targets you want. # The double colons (::) are important, do not make them single colons # otherwise the normal makefile rules will not be performed. # # Things to do before compiling # before-all:: # Things to do after compiling # after-all:: # Things to do before installing # before-install:: # Things to do after installing # after-install:: # Things to do before uninstalling # before-uninstall:: # Things to do after uninstalling # after-uninstall:: # Things to do before cleaning # before-clean:: # Things to do after cleaning # after-clean:: # Things to do before distcleaning # before-distclean:: # Things to do after distcleaning # after-distclean:: # Things to do before checking # before-check:: # Things to do after checking # after-check:: Performance-0.6.0/GNUmakefile.preamble000066400000000000000000000017451436026266200176340ustar00rootroot00000000000000# # Makefile.preamble # # Project specific makefile variables, and additional # # Do not put any Makefile rules in this file, instead they should # be put into Makefile.postamble. # # # Flags dealing with compiling and linking # # Additional flags to pass to the preprocessor # ADDITIONAL_CPPFLAGS += # Additional flags to pass to the Objective-C compiler # ADDITIONAL_OBJCFLAGS += # Additional flags to pass to the C compiler # ADDITIONAL_CFLAGS += # Additional include directories the compiler should search # ADDITIONAL_INCLUDE_DIRS += # Additional LDFLAGS to pass to the linker # ADDITIONAL_LDFLAGS += # Additional library directories the linker should search # ADDITIONAL_LIB_DIRS += # Additional libraries when linking tools # ADDITIONAL_TOOL_LIBS += # Additional libraries when linking applications # ADDITIONAL_GUI_LIBS += # # Flags dealing with installing and uninstalling # # Additional directories to be created during installation # ADDITIONAL_INSTALL_DIRS += Performance-0.6.0/GNUmakefile.wrapper.objc.preamble000066400000000000000000000001411436026266200222140ustar00rootroot00000000000000ADDITIONAL_INCLUDE_DIRS += -I../../../Headers/ ADDITIONAL_LIB_DIRS += -L../../$(GNUSTEP_OBJ_DIR) Performance-0.6.0/GSCache.h000066400000000000000000000254561436026266200154070ustar00rootroot00000000000000/** Copyright (C) 2005 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: October 2005 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. $Date$ $Revision$ */ #ifndef INCLUDED_GSCache_H #define INCLUDED_GSCache_H #import @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.
* 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 * the application is running.
* When the cache is full, old objects are removed to make room for new * ones on a least-recently-used basis.
* Cache sizes may be limited by the number of objects in the cache, * or by the memory used by the cache, or both. Calculation of the * size of items in the cache is relatively expensive, so caches are * only limited by number of objects in the default case.
* Objects stored in the cache may be given a limited lifetime, * in which case an attempt to fetch an expired object * from the cache will cause it to be removed from the cache instead * (subject to control by the delegate).
* 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.
* For object sizing we use the -sizeInBytesExcluding: method, which is * declared in the GNUstep-base additions library headers as follows:
* - (NSUInteger) sizeInBytesExcluding: (NSHashTable*)exclude;
* 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.
* NB. GSCache currently does not support subclassing ... use it as is * or extend it via categories, but do not try to add instance variables. */ @interface GSCache : NSObject { } /** * Return all the current cache instances... useful if you want to do * something to all cache instances in your process. */ + (NSArray*) allInstances; /** * Return a report on all GSCache instances ... calls the [GSCache-description] * method of the individual cache instances to get a report on each one. */ + (NSString*) description; /** * Return the count of objects currently in the cache. */ - (unsigned) currentObjects; /** * Return the total size of the objects currently in the cache.
* 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.
* Also, this figure does not consider memmory used by the cache itself * or by the keys, only the memory used by the objects cached. */ - (NSUInteger) currentSize; /** * Return the delegate object previously set using the -setDelegate: method. */ - (id) delegate; /** * Returns a string describing the status of the receiver for debug/reporting. */ - (NSString*) description; /** * Return the default lifetime for items set in the cache.
* A value of zero means that items are not purged based on lifetime. */ - (unsigned) lifetime; /** * Return the maximum number of items in the cache.
* A value of zero means there is no limit. */ - (unsigned) maxObjects; /** * Return the maximum total size of items in the cache.
* A value of zero means there is no limit. */ - (NSUInteger) maxSize; /** * Return the name of this instance (as set using -setName:forConfiguration:) */ - (NSString*) name; /** * Return the cached value for the specified key, or nil if there * is no value in the cache. */ - (id) objectForKey: (id)aKey; /** * Remove all items whose lifetimes have passed * (if lifetimes are in use for the cache).
*/ - (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).
* 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.
* The delegate object is not retained.
* 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). */ - (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 expired items are still removed when an attempt to * retrieve them is made. */ - (void) setLifetime: (unsigned)max; /** * Sets the maximum number of objects in the cache. If this is non-zero * then an attempt to set an object in a full cache will result in the * least recently used item in the cache being removed. */ - (void) setMaxObjects: (unsigned)max; /** * Sets the maximum total size for objects in the cache. If this is non-zero * 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; /** * Sets the name of this instance and whether the instance is to be * configured using information from the user defaults system.
* If useDefaults is YES, values from the user defaults system will be * used to override the -setLifetime: -setMaxObjects: and -setMaxSize: * methods.
* 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. */ - (void) setName: (NSString*)name; /** * Sets (or replaces) the cached value for the specified key.
* The value of anObject may be nil to remove any cached object * for aKey. */ - (void) setObject: (id)anObject forKey: (id)aKey; /** * 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.
* The value of anObject may be nil to remove any cached object * for aKey. */ - (void) setObject: (id)anObject forKey: (id)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.
* 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).
* This will, if a lifetime is set (see the -setLifetime: method) * first purge all expired objects from the cache, then * (if necessary) remove objects from the cache until the number * of objects and size of cache meet the limits specified.
* If the objects argument is zero then all objects are removed from * the cache.
* The size argument is used only if a maximum size is set * for the cache. */ - (void) shrinkObjects: (unsigned)objects andSize: (NSUInteger)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. */ @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.
* 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.
* 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 * in the cache.
* This is called when an attempt is made to access the cached value * for aKey and the object is found in the cache but it is no longer * valid (has expired).
* If the method returns YES, then anObject will not be removed as it * normally would. This allows the delegate to change the cached item * or refresh it.
* For instance, the delegate could replace the object * in the cache before returning YES in order to update the cached value * when its lifetime has expired.
* 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. */ - (BOOL) shouldKeepItem: (id)anObject withKey: (id)aKey lifetime: (unsigned)lifetime after: (unsigned)delay; @end #endif Performance-0.6.0/GSCache.m000066400000000000000000000504371436026266200154110ustar00rootroot00000000000000/* -*-objc-*- */ /** Implementation of GSCache for GNUStep Copyright (C) 2005 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: October 2005 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. $Date$ $Revision$ */ #include #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import "GSCache.h" #import "GSTicker.h" #if !defined(GNUSTEP) #include #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 @interface GSCacheItem : NSObject { @public GSCacheItem *next; GSCacheItem *prev; unsigned life; unsigned warn; unsigned when; NSUInteger size; id key; id object; } + (GSCacheItem*) newWithObject: (id)anObject forKey: (id)aKey; @end @implementation GSCacheItem + (GSCacheItem*) newWithObject: (id)anObject forKey: (id)aKey { GSCacheItem *i; i = (GSCacheItem*)NSAllocateObject(self, 0, NSDefaultMallocZone()); i->object = [anObject retain]; 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; } @end @implementation GSCache static NSHashTable *allCaches = 0; static NSRecursiveLock *allCachesLock = nil; static int itemOffset = 0; 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 lifetime; unsigned maxObjects; NSUInteger maxSize; unsigned hits; unsigned misses; NSMapTable *contents; GSCacheItem *first; NSString *name; NSHashTable *exclude; NSRecursiveLock *lock; BOOL useDefaults; } Item; #define my ((Item*)((void*)self + itemOffset)) /* * Add item to linked list starting at *first */ static void appendItem(GSCacheItem *item, GSCacheItem **first) { if (*first == nil) { item->next = item->prev = item; *first = item; } else { (*first)->prev->next = item; item->prev = (*first)->prev; (*first)->prev = item; item->next = *first; } } /* * Remove item from linked list starting at *first */ static void removeItem(GSCacheItem *item, GSCacheItem **first) { if (*first == item) { if (item->next == item) { *first = nil; } else { *first = item->next; } } item->next->prev = item->prev; item->prev->next = item->next; item->prev = item->next = item; } + (NSArray*) allInstances { NSArray *a; [allCachesLock lock]; a = NSAllHashTableObjects(allCaches); [allCachesLock unlock]; return a; } + (id) alloc { return [self allocWithZone: NSDefaultMallocZone()]; } + (id) allocWithZone: (NSZone*)z { GSCache *c; c = (GSCache*)NSAllocateObject(self, sizeof(Item), z); return c; } + (NSString*) description { NSMutableString *ms; NSHashEnumerator e; GSCache *c; ms = [NSMutableString stringWithString: [super description]]; [allCachesLock lock]; e = NSEnumerateHashTable(allCaches); while ((c = (GSCache*)NSNextHashEnumeratorItem(&e)) != nil) { [ms appendFormat: @"\n%@", [c description]]; } NSEndHashTableEnumeration(&e); [allCachesLock unlock]; return ms; } + (void) initialize { if (allCaches == 0) { itemOffset = class_getInstanceSize(self); allCaches = NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0); GSTickerTimeNow(); } } - (unsigned) currentObjects { return my->currentObjects; } - (NSUInteger) currentSize { return my->currentSize; } - (void) dealloc { if (my->useDefaults) { [[NSNotificationCenter defaultCenter] removeObserver: self name: NSUserDefaultsDidChangeNotification object: nil]; } [allCachesLock lock]; NSHashRemove(allCaches, (void*)self); [allCachesLock unlock]; if (my->contents != 0) { [self shrinkObjects: 0 andSize: 0]; NSFreeMapTable(my->contents); } [my->exclude release]; [my->name release]; [my->lock release]; [super dealloc]; } - (id) delegate { return my->delegate; } - (NSString*) description { NSString *n; [my->lock lock]; n = my->name; if (n == nil) { n = [super description]; } n = [NSString stringWithFormat: @" %@\n" @" Items: %u(%u)\n" @" Size: %"PRIuPTR"(%"PRIuPTR")\n" @" Life: %u\n" @" Hit: %u\n" @" Miss: %u\n", n, my->currentObjects, my->maxObjects, my->currentSize, my->maxSize, 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]; } return self; } - (unsigned) lifetime { return my->lifetime; } - (unsigned) maxObjects { return my->maxObjects; } - (NSUInteger) maxSize { return my->maxSize; } - (NSString*) name { NSString *n; [my->lock lock]; n = [my->name retain]; [my->lock unlock]; return [n autorelease]; } - (id) objectForKey: (id)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) { GSCacheItem *orig = [item retain]; [my->lock unlock]; NS_DURING { keep = (*(my->replace))(my->delegate, @selector(shouldKeepItem:withKey:lifetime:after:), item->object, aKey, item->life, when - item->when); } NS_HANDLER { [my->lock unlock]; [localException raise]; } NS_ENDHANDLER [my->lock lock]; if (keep == YES) { 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; } } [orig release]; } if (keep == NO) { removeItem(item, &my->first); my->currentObjects--; if (my->maxSize > 0) { my->currentSize -= item->size; } 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]; NS_DURING { (*(my->refresh))(my->delegate, @selector(mayRefreshItem:withKey:lifetime:after:), item->object, aKey, item->life, when - item->when); } NS_HANDLER { [my->lock unlock]; [localException raise]; } NS_ENDHANDLER [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]; } - (void) purge { if (my->contents != 0) { unsigned when = GSTickerTimeTick(); NSMapEnumerator e; GSCacheItem *i; id k; [my->lock lock]; e = NSEnumerateMapTable(my->contents); while (NSNextMapEnumeratorPair(&e, (void**)&k, (void**)&i) != 0) { if (i->when > 0 && i->when < when) { removeItem(i, &my->first); my->currentObjects--; if (my->maxSize > 0) { my->currentSize -= i->size; } NSMapRemove(my->contents, (void*)i->key); } } 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 (NSDecrementExtraRefCountWasZero(self)) { NSHashRemove(allCaches, (void*)self); [allCachesLock unlock]; [self dealloc]; } else { [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]; } [my->lock unlock]; } - (void) setMaxSize: (NSUInteger)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; if (nil == my->exclude) { my->exclude = NSCreateHashTable(NSNonOwnedPointerHashCallBacks, 0); } 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; } NSEndMapTableEnumeration(&e); my->currentSize = size; } else if (max == 0) { my->currentSize = 0; } my->maxSize = max; if (my->currentSize > my->maxSize) { [self shrinkObjects: my->maxObjects 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]; } - (void) setObject: (id)anObject forKey: (id)aKey { [self setObject: anObject forKey: aKey lifetime: my->lifetime]; } - (void) setObject: (id)anObject forKey: (id)aKey lifetime: (unsigned)lifetime { GSCacheItem *item; unsigned maxObjects; NSUInteger maxSize; unsigned addObjects = (anObject == nil ? 0 : 1); NSUInteger addSize = 0; [my->lock lock]; maxObjects = my->maxObjects; maxSize = my->maxSize; item = (GSCacheItem*)NSMapGet(my->contents, aKey); if (item != nil) { removeItem(item, &my->first); my->currentObjects--; if (my->maxSize > 0) { my->currentSize -= item->size; } NSMapRemove(my->contents, (void*)aKey); } if (addObjects > 0 && (maxSize > 0 || maxObjects > 0)) { if (maxSize > 0) { if (nil == my->exclude) { my->exclude = NSCreateHashTable(NSNonOwnedPointerHashCallBacks, 0); } addSize = [anObject sizeInBytesExcluding: my->exclude]; [my->exclude removeAllObjects]; if (addSize > maxSize) { addObjects = 0; // Object too big to cache. } } } if (addObjects > 0) { /* * Make room for new object. */ [self shrinkObjects: maxObjects - addObjects andSize: maxSize - addSize]; item = [GSCacheItem newWithObject: anObject forKey: aKey]; if (lifetime > 0) { unsigned tick = GSTickerTimeTick(); item->when = tick + lifetime; item->warn = tick + lifetime / 2; } 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]; } } - (void) shrinkObjects: (unsigned)objects andSize: (NSUInteger)size { NSUInteger newSize; unsigned newObjects; [my->lock lock]; newSize = [self currentSize]; newObjects = [self currentObjects]; if (newObjects > objects || (my->maxSize > 0 && newSize > size)) { [self purge]; newSize = [self currentSize]; newObjects = [self currentObjects]; while (newObjects > objects || (my->maxSize > 0 && newSize > size)) { GSCacheItem *item; item = my->first; removeItem(item, &my->first); newObjects--; if (my->maxSize > 0) { newSize -= item->size; } NSMapRemove(my->contents, (void*)item->key); } 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 { if (YES == my->useDefaults) { 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; } [my->lock unlock]; } NS_HANDLER { my->useDefaults = YES; [my->lock unlock]; [localException raise]; } NS_ENDHANDLER } @end Performance-0.6.0/GSFIFO.h000066400000000000000000000400451436026266200151160ustar00rootroot00000000000000#if !defined(INCLUDED_GSFIFO) #define INCLUDED_GSFIFO 1 /** Copyright (C) 2011 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald 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 #import @class NSArray; @class NSCondition; @class NSNumber; @class NSString; @class NSThread; /** GSFIFO manages a first-in-first-out queue of items.
* Items in the queue are NOT 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.
* 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.
* 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.
* 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).
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* Raises an exception if the FIFO is configured * with a timeout and it is exceeded.
* 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.
* The returned item/object, is not autoreleased.
* Implemented using -get:count:shouldBlock: */ - (NSObject*) getObjectRetained NS_RETURNS_RETAINED; /** * Initialises the receiver with the specified capacity (buffer size).
* The capacity must lie in the range from one to a hundred million, otherwise * the receiver is deallocated and this method returns nil.
* 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.
* 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.
* If the multiProducer or multiConsumer flag is YES, the FIFO is * configured to support multiple producer/consumer threads using locking.
* 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.
* 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.
* 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.
* The GSFIFOCapacityNNN default specifies the capacity for the FIFO, and * if missing a capacity of 1000 is assumed.
* The GSFIFOGranularityNNN integer is zero by default.
* The GSFIFOTimeoutNNN integer is zero by default.
* The GSFIFOSingleConsumerNNN boolean is NO by default.
* The GSFIFOSingleProducerNNN boolean is NO by default.
* The GSFIFOBoundariesNNN array is missing by default.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* Implemented using -put:count:shouldBlock: */ - (void) putObjectConsumed: (NSObject*) NS_CONSUMED item; /** Return any available statistics for the receiver.
*/ - (NSString*) stats; /** Return statistics on get operations for the receiver.
* 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.
* 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.
* 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).
* 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.
Calling this method does * not 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). *
Calling this method does not 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.
* 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.
* Implemented using -put:count:shouldBlock: */ - (BOOL) tryPutObject: (NSObject*)item; @end /** Function to efficiently get an item from a fast FIFO.
* Returns NULL if the FIFO is empty.
* 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.
* 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.
* Returns YES on success, NO on failure (FIFO is full).
* 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.
* 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 Performance-0.6.0/GSFIFO.m000066400000000000000000000557541436026266200151400ustar00rootroot00000000000000/** Copyright (C) 2011 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: April 2011 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #import "GSFIFO.h" #import #import #import #import #import #import #import #import #import #if !defined(GNUSTEP) #import "NSObject+GSExtensions.h" #endif #include @implementation GSFIFO static NSLock *classLock = nil; static NSMapTable *allFIFOs = nil; static NSArray *defaultBoundaries = nil; static Class NSDateClass = 0; static SEL tiSel = 0; static NSTimeInterval (*tiImp)(Class, SEL) = 0; #define NOW ((*tiImp)(NSDateClass, tiSel)) #define START if (boundsCount > 0) ti = NOW; #define ENDGET if (boundsCount > 0 && ti > 0.0) {\ ti = NOW - ti; getWaitTotal += ti; \ stats(ti, boundsCount, waitBoundaries, getWaitCounts); } #define ENDPUT if (boundsCount > 0 && ti > 0.0) {\ ti = NOW - ti; putWaitTotal += ti; \ stats(ti, boundsCount, waitBoundaries, putWaitCounts); } static void stats(NSTimeInterval ti, uint32_t max, NSTimeInterval *bounds, uint64_t *bands) { if (ti > bounds[max - 1]) { bands[max]++; } else { uint32_t min = 0; uint32_t pos = max / 2; while (max > min) { if (ti <= bounds[pos]) { max = pos; } else { min = pos + 1; } pos = (max + min)/2; } bands[pos]++; } } + (void) initialize { if (nil == defaultBoundaries) { classLock = [NSLock new]; allFIFOs = NSCreateMapTable(NSObjectMapKeyCallBacks, NSNonRetainedObjectMapValueCallBacks, 0); defaultBoundaries = [[NSArray alloc] initWithObjects: [NSNumber numberWithDouble: 0.1], [NSNumber numberWithDouble: 0.2], [NSNumber numberWithDouble: 0.5], [NSNumber numberWithDouble: 1.0], [NSNumber numberWithDouble: 2.0], [NSNumber numberWithDouble: 5.0], [NSNumber numberWithDouble: 10.0], [NSNumber numberWithDouble: 20.0], [NSNumber numberWithDouble: 50.0], nil]; NSDateClass = [NSDate class]; tiSel = @selector(timeIntervalSinceReferenceDate); tiImp = (NSTimeInterval (*)(Class,SEL))[NSDateClass methodForSelector: tiSel]; } } + (NSString*) stats { NSMutableString *m = [NSMutableString stringWithCapacity: 1024]; NSString *k; GSFIFO *f; NSMapEnumerator e; [classLock lock]; e = NSEnumerateMapTable(allFIFOs); while (NSNextMapEnumeratorPair(&e, (void**)&k, (void**)&f) != 0) { [m appendString: [f stats]]; } NSEndMapTableEnumeration(&e); [classLock unlock]; return m; } - (unsigned) _cooperatingGet: (void**)buf count: (unsigned)count shouldBlock: (BOOL)block before: (NSDate*)before { NSTimeInterval ti; unsigned index; BOOL wasFull; [condition lock]; if (_head - _tail == 0) { emptyCount++; _getTryFailure++; if (NO == block) { [condition unlock]; return 0; } START if ((0 == timeout) && (before == nil)) { while (_head - _tail == 0) { [condition wait]; } } else { NSDate *d = nil; NSDate *effective; [before retain]; if (timeout != 0) { d = [[NSDateClass alloc] initWithTimeIntervalSinceNow: timeout / 1000.0f]; } if ((d != nil) && (before != nil)) { effective = [d earlierDate: before]; } else if (d != nil) { effective = d; } else { effective = before; } while (_head - _tail == 0) { if (NO == [condition waitUntilDate: effective]) { [d release]; [before release]; ENDGET [condition broadcast]; [condition unlock]; if (before != effective) { [NSException raise: NSGenericException format: @"Timeout waiting for new data in FIFO"]; } else { return 0; } } } [d release]; [before release]; ENDGET } } else { _getTrySuccess++; } if (_head - _tail == _capacity) { wasFull = YES; } else { wasFull = NO; } for (index = 0; index < count && (_head - _tail) != 0; index++) { buf[index] = _items[_tail % _capacity]; _tail++; } if (YES == wasFull) { [condition broadcast]; } [condition unlock]; return index; } - (void*) _cooperatingPeek { [condition lock]; if ((_head - _tail) == 0) { // We do not need to signal the condition because // nothing about the qeuue did change [condition unlock]; return NULL; } void *ptr = _items[_tail % _capacity]; [condition unlock]; return ptr; } - (id) _cooperatingPeekObject { [condition lock]; if ((_head - _tail) == 0) { // We do not need to signal the condition because // nothing about the qeuue did change [condition unlock]; return nil; } id obj = [[(id)_items[_tail % _capacity] retain] autorelease]; [condition unlock]; return obj; } - (void*) peek { if (condition != nil) { return [self _cooperatingPeek]; } if (_head - _tail == 0) { return NULL; } return _items[_tail % _capacity]; } - (NSObject*) peekObject { if (condition != nil) { return [self _cooperatingPeekObject]; } if (_head - _tail == 0) { return nil; } return [[(id)_items[_tail % _capacity] retain] autorelease]; } - (unsigned) _cooperatingPut: (void**)buf count: (unsigned)count shouldBlock: (BOOL)block { NSTimeInterval ti; unsigned index; BOOL wasEmpty; [condition lock]; if (_head - _tail == _capacity) { _putTryFailure++; fullCount++; if (NO == block) { [condition unlock]; return 0; } START if (0 == timeout) { while (_head - _tail == _capacity) { [condition wait]; } } else { NSDate *d; d = [[NSDateClass alloc] initWithTimeIntervalSinceNow: timeout / 1000.0f]; while (_head - _tail == _capacity) { if (NO == [condition waitUntilDate: d]) { [d release]; ENDPUT [condition broadcast]; [condition unlock]; [NSException raise: NSGenericException format: @"Timeout waiting for space in FIFO"]; } } [d release]; } ENDPUT } else { _putTrySuccess++; } if (_head - _tail == 0) { wasEmpty = YES; } else { wasEmpty = NO; } for (index = 0; index < count && (_head - _tail < _capacity); index++) { _items[_head % _capacity] = buf[index]; _head++; } if (YES == wasEmpty) { [condition broadcast]; } [condition unlock]; return index; } - (void) putAll: (void**)buf count: (unsigned)count shouldRetain: (BOOL)rtn { NSTimeInterval ti; unsigned index; BOOL wasEmpty; NSAssert(nil != condition, NSGenericException); NSAssert(count <= _capacity, NSInvalidArgumentException); [condition lock]; if (_head - _tail < count) { if (_head - _tail == _capacity) { _putTryFailure++; fullCount++; } START if (0 == timeout) { while (_head - _tail < count) { [condition wait]; } } else { NSDate *d; d = [[NSDateClass alloc] initWithTimeIntervalSinceNow: timeout / 1000.0f]; while (_head - _tail < count) { if (NO == [condition waitUntilDate: d]) { [d release]; ENDPUT [condition broadcast]; [condition unlock]; [NSException raise: NSGenericException format: @"Timeout waiting for space in FIFO"]; } } [d release]; } ENDPUT } else { _putTrySuccess++; } if (_head - _tail == 0) { wasEmpty = YES; } else { wasEmpty = NO; } for (index = 0; index < count; index++) { _items[_head % _capacity] = buf[index]; _head++; if (YES == rtn) { RETAIN((NSObject*)buf[index]); } } if (YES == wasEmpty) { [condition broadcast]; } [condition unlock]; } - (void) dealloc { [classLock lock]; if (NSMapGet(allFIFOs, name) == self) { NSMapRemove(allFIFOs, name); } [classLock unlock]; [name release]; [condition release]; if (0 != _items) { NSZoneFree(NSDefaultMallocZone(), _items); } if (0 != waitBoundaries) { NSZoneFree(NSDefaultMallocZone(), waitBoundaries); } if (0 != getWaitCounts) { NSZoneFree(NSDefaultMallocZone(), getWaitCounts); } if (0 != putWaitCounts) { NSZoneFree(NSDefaultMallocZone(), putWaitCounts); } [super dealloc]; } - (NSUInteger) count { return (NSUInteger)(_head - _tail); } - (NSString*) description { return [NSString stringWithFormat: @"%@ (%@) capacity:%"PRIu32" lockless:%c" @" get:%"PRIu64" put:%"PRIu64" empty:%"PRIu64" full:%"PRIu64"", [super description], name, _capacity, ((nil == condition) ? 'Y' : 'N'), _tail, _head, emptyCount, fullCount]; } - (unsigned) get: (void**)buf count: (unsigned)count shouldBlock: (BOOL)block before: (NSDate*)date { unsigned index; NSTimeInterval ti; NSTimeInterval sum; NSTimeInterval waitLength; uint32_t old; uint32_t fib; if (0 == count) return 0; if (nil != condition) { return [self _cooperatingGet: buf count: count shouldBlock: block before: date]; } waitLength = [date timeIntervalSinceNow]; if (nil == getThread) { getThread = [NSThread currentThread]; } if (_head > _tail) { for (index = 0; index < count && _head > _tail; index++) { buf[index] = _items[_tail % _capacity]; _tail++; _getTrySuccess++; } return index; } _getTryFailure++; emptyCount++; if (NO == block) { return 0; } START old = 0; fib = 1; sum = 0.0; while (_head <= _tail) { uint32_t tmp; NSTimeInterval dly; if (timeout > 0 && sum * 1000 > timeout) { ENDGET [NSException raise: NSGenericException format: @"Timeout waiting for new data in FIFO"]; } else if (date != nil && sum > waitLength) { ENDGET return 0; } tmp = fib + old; old = fib; fib = tmp; if (granularity > 0 && fib > granularity) { fib = granularity; } dly = ((NSTimeInterval)fib) / 1000.0; [NSThread sleepForTimeInterval: dly]; sum += dly; } ENDGET for (index = 0; index < count && _head > _tail; index++) { buf[index] = _items[_tail % _capacity]; _tail++; } return index; } - (unsigned) get: (void**)buf count: (unsigned)count shouldBlock: (BOOL)block { return [self get: buf count: count shouldBlock: block before: nil]; } - (unsigned) getObjects: (NSObject**)buf count: (unsigned)count shouldBlock: (BOOL)block before: (NSDate*)date { unsigned result; unsigned index; index = result = [self get: (void**)buf count: count shouldBlock: block before: date]; while (index-- > 0) { [buf[index] autorelease]; } return result; } - (unsigned) getObjects: (NSObject**)buf count: (unsigned)count shouldBlock: (BOOL)block { return [self getObjects: buf count: count shouldBlock: block before: nil]; } - (void*) get { void *item = 0; while (0 == [self get: &item count: 1 shouldBlock: YES]) ; return item; } - (NSObject*) getObject { void *item = 0; while (0 == [self get: &item count: 1 shouldBlock: YES]) ; return [(NSObject*)item autorelease]; } - (NSObject*) getObjectRetained NS_RETURNS_RETAINED { void *item = 0; while (0 == [self get: &item count: 1 shouldBlock: YES]) ; return (NSObject*)item; } - (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 { if (c < 1 || c > 100000000) { [self release]; return nil; } _capacity = c; granularity = g; timeout = t; _items = (void*)NSAllocateCollectable(c * sizeof(void*), NSScannedOption); if (YES == mp || YES == mc) { condition = [NSCondition new]; } name = [n copy]; if (nil == a) { a = defaultBoundaries; } if ((c = [a count]) > 0) { NSTimeInterval l; NSNumber *n; waitBoundaries = (NSTimeInterval*)NSAllocateCollectable(c * sizeof(NSTimeInterval), 0); boundsCount = c++; getWaitCounts = (uint64_t*)NSAllocateCollectable(c * sizeof(uint64_t), 0); putWaitCounts = (uint64_t*)NSAllocateCollectable(c * sizeof(uint64_t), 0); n = [a lastObject]; if (NO == [n isKindOfClass: [NSNumber class]] || (l = [n doubleValue]) <= 0.0) { [self release]; [NSException raise: NSInvalidArgumentException format: @"Bad boundaries"]; } c = boundsCount; waitBoundaries[--c] = l; while (c-- > 0) { NSTimeInterval t; n = [a objectAtIndex: c]; if (NO == [n isKindOfClass: [NSNumber class]] || (t = [n doubleValue]) <= 0.0 || t >= l) { [self release]; [NSException raise: NSInvalidArgumentException format: @"Bad boundaries"]; } waitBoundaries[c] = t; l = t; } } [classLock lock]; if (nil != NSMapGet(allFIFOs, n)) { [classLock unlock]; [self release]; [NSException raise: NSInvalidArgumentException format: @"GSFIFO ... name (%@) already in use", n]; } NSMapInsert(allFIFOs, name, self); [classLock unlock]; return self; } - (id) initWithCapacity: (uint32_t)c name: (NSString*)n { NSUserDefaults *defs = [NSUserDefaults standardUserDefaults]; NSString *key; NSInteger i; uint16_t g; uint16_t t; BOOL mc; BOOL mp; NSArray *b; key = [NSString stringWithFormat: @"GSFIFOCapacity%@", n]; if (nil == [defs objectForKey: key]) key = @"GSFIFOCapacity"; i = [defs integerForKey: key]; if (i > 0) { c = i; } key = [NSString stringWithFormat: @"GSFIFOGranularity%@", n]; if (nil == [defs objectForKey: key]) key = @"GSFIFOGranularity"; g = [defs integerForKey: key]; key = [NSString stringWithFormat: @"GSFIFOTimeout%@", n]; if (nil == [defs objectForKey: key]) key = @"GSFIFOTimeout"; t = [defs integerForKey: key]; key = [NSString stringWithFormat: @"GSFIFOSingleConsumer%@", n]; if (nil == [defs objectForKey: key]) key = @"GSFIFOSingleConsumer"; mc = (YES == [defs boolForKey: key]) ? NO : YES; key = [NSString stringWithFormat: @"GSFIFOSingleProducer%@", n]; if (nil == [defs objectForKey: key]) key = @"GSFIFOSingleProducer"; mp = (YES == [defs boolForKey: key]) ? NO : YES; key = [NSString stringWithFormat: @"GSFIFOBoundaries%@", n]; if (nil == [defs objectForKey: key]) key = @"GSFIFOBoundaries"; b = [defs arrayForKey: key]; return [self initWithCapacity: c granularity: g timeout: t multiProducer: mp multiConsumer: mc boundaries: b name: n]; } - (id) initWithName: (NSString*)n { return [self initWithCapacity: 10000 name: n]; } - (unsigned) put: (void**)buf count: (unsigned)count shouldBlock: (BOOL)block { NSTimeInterval sum; unsigned index; NSTimeInterval ti = 0.0; uint32_t old; uint32_t fib; if (0 == count) { return 0; } if (nil != condition) { return [self _cooperatingPut: buf count: count shouldBlock: block]; } if (nil == putThread) { putThread = [NSThread currentThread]; } if (_head - _tail < _capacity) { for (index = 0; index < count && _head - _tail < _capacity; index++) { _items[_head % _capacity] = buf[index]; _head++; } _putTrySuccess++; return index; } _putTryFailure++; fullCount++; if (NO == block) { return 0; } START old = 0; fib = 1; sum = 0.0; while (_head - _tail >= _capacity) { uint32_t tmp; NSTimeInterval dly; if (timeout > 0 && sum * 1000 > timeout) { ENDPUT [NSException raise: NSGenericException format: @"Timeout waiting for space in FIFO"]; } tmp = fib + old; old = fib; fib = tmp; if (granularity > 0 && fib > granularity) { fib = granularity; } dly = ((NSTimeInterval)fib) / 1000.0; [NSThread sleepForTimeInterval: dly]; sum += dly; } ENDPUT for (index = 0; index < count && _head - _tail < _capacity; index++) { _items[_head % _capacity] = buf[index]; _head++; } return index; } - (unsigned) putObjects: (NSObject**)buf count: (unsigned)count shouldBlock: (BOOL)block { unsigned result; unsigned index; /* NB we must retain objects *before* putting them in the FIFO since * another thread may grab them immediately. * That means, if we fail to put all of the objects, we must release * any that were left over. */ for (index = 0; index < count; index++) { [buf[index] retain]; } result = [self put: (void**)buf count: count shouldBlock: block]; while (count-- > result) { [buf[count] release]; } return result; } - (void) put: (void*)item { while (0 == [self put: (void**)&item count: 1 shouldBlock: YES]) ; } - (void) putObject: (NSObject*)item { [item retain]; while (0 == [self put: (void**)&item count: 1 shouldBlock: YES]) ; } - (void) putObjectConsumed: (NSObject*) NS_CONSUMED item { while (0 == [self put: (void**)&item count: 1 shouldBlock: YES]) ; } - (void) _getStats: (NSMutableString*)s { [s appendFormat: @" empty:%"PRIu64" failures:%"PRIu64" successes:%"PRIu64"\n", emptyCount, _getTryFailure, _getTrySuccess]; if (boundsCount > 0) { unsigned i; [s appendFormat: @" blocked total:%g average:%g perfail:%g\n", getWaitTotal, (_getTryFailure+_getTrySuccess > 0) ? getWaitTotal / (_getTryFailure+_getTrySuccess) : 0.0, (_getTryFailure > 0) ? getWaitTotal / _getTryFailure : 0.0]; for (i = 0; i < boundsCount; i++) { [s appendFormat: @" up to %g: %"PRIu64"\n", waitBoundaries[i], getWaitCounts[i]]; } [s appendFormat: @" above %g: %"PRIu64"\n", waitBoundaries[boundsCount-1], getWaitCounts[boundsCount]]; } } - (void) _putStats: (NSMutableString*)s { [s appendFormat: @" full:%"PRIu64" failures:%"PRIu64" successes:%"PRIu64"\n", fullCount, _putTryFailure, _putTrySuccess]; if (boundsCount > 0) { unsigned i; [s appendFormat: @" blocked total:%g average:%g perfail:%g\n", putWaitTotal, (_putTryFailure+_putTrySuccess > 0) ? putWaitTotal / (_putTryFailure+_putTrySuccess) : 0.0, (_putTryFailure > 0) ? putWaitTotal / _putTryFailure : 0.0]; for (i = 0; i < boundsCount; i++) { [s appendFormat: @" up to %g: %"PRIu64"\n", waitBoundaries[i], putWaitCounts[i]]; } [s appendFormat: @" above %g: %"PRIu64"\n", waitBoundaries[boundsCount-1], putWaitCounts[boundsCount]]; } } - (NSString*) stats { NSMutableString *s = [NSMutableString stringWithCapacity: 100]; [s appendFormat: @"%@ (%@) capacity:%"PRIu32" lockless:%c\n", [super description], name, _capacity, ((nil == condition) ? 'Y' : 'N')]; if (nil != condition || [NSThread currentThread] == getThread) { [condition lock]; [self _getStats: s]; [condition unlock]; } if (nil != condition || [NSThread currentThread] == putThread) { [condition lock]; [self _putStats: s]; [condition unlock]; } return s; } - (NSString*) statsGet { NSMutableString *s = [NSMutableString stringWithCapacity: 100]; if (nil == condition) { if ([NSThread currentThread] != getThread) { if (nil == getThread) { getThread = [NSThread currentThread]; } else { [NSException raise: NSInternalInconsistencyException format: @"[%@-%@] called from wrong thread for %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), name]; } } } [condition lock]; [s appendFormat: @"%@ (%@)\n", [super description], name]; [self _getStats: s]; [condition unlock]; return s; } - (NSString*) statsPut { NSMutableString *s = [NSMutableString stringWithCapacity: 100]; if (nil == condition) { if ([NSThread currentThread] != putThread) { if (nil == putThread) { putThread = [NSThread currentThread]; } else { [NSException raise: NSInternalInconsistencyException format: @"[%@-%@] called from wrong thread for %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), name]; } } } [condition lock]; [s appendFormat: @"%@ (%@)\n", [super description], name]; [self _putStats: s]; [condition unlock]; return s; } - (void*) tryGet { void *item = nil; [self get: &item count: 1 shouldBlock: NO]; return item; } - (NSObject*) tryGetObject { void *item = nil; [self get: &item count: 1 shouldBlock: NO]; return [(NSObject*)item autorelease]; } - (BOOL) tryPut: (void*)item { if (1 == [self put: &item count: 1 shouldBlock: NO]) { return YES; } return NO; } - (BOOL) tryPutObject: (NSObject*)item { [item retain]; if (1 == [self put: (void**)&item count: 1 shouldBlock: NO]) { return YES; } [item release]; return NO; } - (NSUInteger)sizeInBytesExcluding: (NSHashTable*)excluding { NSUInteger size = 0; if (0 == (size = [super sizeInBytesExcluding: excluding])) { return 0; } return size + (_capacity * sizeof(void)) // item storage + (boundsCount * sizeof(NSTimeInterval)) // boundaries + (2 * (boundsCount + 1) * sizeof(uint64_t)) // get and put counts + [condition sizeInBytesExcluding: excluding] + [name sizeInBytesExcluding: excluding] + [putThread sizeInBytesExcluding: excluding] + [getThread sizeInBytesExcluding: excluding]; } @end Performance-0.6.0/GSIOThreadPool.h000066400000000000000000000133111436026266200166600ustar00rootroot00000000000000#if !defined(INCLUDED_GSIOTHREADPOOL) #define INCLUDED_GSIOTHREADPOOL 1 /** Copyright (C) 2010 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald 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 #import #if !defined (GNUSTEP) && (MAC_OS_X_VERSION_MAX_ALLOWED<=MAC_OS_X_VERSION_10_4) typedef unsigned int NSUInteger; #endif @class NSMutableArray; @class NSTimer; /** This is the class for threads in the pool.
* 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).
*/ @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).
* 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).
* Does nothing, provided for subclasses to override. */ - (void) shutdown; /** Called when the thread is started up (before the run loop starts).
* 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.
* 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.
* 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.
* Always returns the same instance whenever the method is called.
* 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.
* This method selectes the least used thread in the pool (ie the * one with the lowest acquire count).
* 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.
* 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).
* Default is 0 (no thread pooling in use).
* 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.
* 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 Performance-0.6.0/GSIOThreadPool.m000066400000000000000000000213411436026266200166670ustar00rootroot00000000000000/** Copyright (C) 2010 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald 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 #import #import #import #import #import #import #import #import "GSIOThreadPool.h" /* 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. */ static GSIOThread * best(NSMutableArray *a) { NSUInteger c = [a count]; NSUInteger l = NSNotFound; GSIOThread *t = nil; 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); 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 Performance-0.6.0/GSIndexedSkipList.h000066400000000000000000000044231436026266200174360ustar00rootroot00000000000000/** Copyright (C) 2006 Free Software Foundation, Inc. Written by: Matt Rice 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 #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); Performance-0.6.0/GSIndexedSkipList.m000066400000000000000000000175171436026266200174530ustar00rootroot00000000000000/** Copyright (C) 2006 Free Software Foundation, Inc. Written by: Matt Rice 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 #include #include #include #include #import "GSIndexedSkipList.h" /* This no longer seems to be needed/correct #if defined(__MINGW32__) #include // 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; } Performance-0.6.0/GSLinkedList.h000066400000000000000000000312041436026266200164320ustar00rootroot00000000000000#if !defined(INCLUDED_GSLINKEDLIST) #define INCLUDED_GSLINKEDLIST 1 /** Copyright (C) 2010 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald 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 #if !defined (GNUSTEP) && (MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4) typedef unsigned int NSUInteger; #endif @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.
* 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.
* The GSListLink item carries a minimal payload of a single item which * is retained by the link.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* The comparison is performed using the [NSObject-isEqual:] method * of object.
* If start is nil then the whole list is searched.
* This method will not 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.
* If start is nil then the whole list is searched.
* 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.
* Retains the link. */ - (void) insert: (GSListLink*)link after: (GSListLink*)at; /** Inserts other immediately before the receiver.
* Retains the link. */ - (void) insert: (GSListLink*)link before: (GSListLink*)at; /** Removes the link from the receiver.
* 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).
* If back is YES, the search is in the direction from tail to head * rather than the normal search from head to tail.
* 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).
* If back is YES, the search is in the direction from tail to head * rather than the normal search from head to tail.
* 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.
* If at is nil, inserts at the end of the list (link becomes tail).
* Updates the head, tail and count variables of list.
* Does not retain link. */ extern void GSLinkedListInsertAfter(GSListLink *link, GSLinkedList *list, GSListLink *at); /** Inserts link immediately before at.
* If at is nil, inserts at the start of the list (link becomes head).
* Updates the head, tail and count variables of list.
* 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.
* Updates the head, tail and count variables of list.
* 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.
* 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.
* 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).
* 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).
* 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 static inline void GSLinkStoreConsumeLink(GSLinkStore *list, GSListLink NS_CONSUMED *link) { link->next = list->free; list->free = link; } static inline GSListLink* NS_RETURNS_RETAINED 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.
* Calls GSLinkedListInsertAfter().
* 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.
* Calls GSLinkedListInsertBefore().
* 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.
* If at is nil or is not owned by the list, this does nothing. */ extern void GSLinkStoreRemoveObjectAt(GSLinkStore *list, GSListLink *at); #endif Performance-0.6.0/GSLinkedList.m000066400000000000000000000306621436026266200164460ustar00rootroot00000000000000/** Copyright (C) 2010 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald 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 #import #import "GSLinkedList.h" @implementation GSListLink - (void) dealloc { NSAssert(nil == owner, NSInternalInconsistencyException); [item release]; [super 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]; [o release]; } @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); [link retain]; } } - (NSUInteger) count { return count; } - (void) dealloc { [self empty]; [super dealloc]; } - (void) empty { GSListLink *link; while (nil != (link = head)) { head = link->next; link->owner = nil; link->next = link->previous = nil; [link release]; } 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); [link retain]; } } - (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); [link retain]; } } - (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); [link release]; } - (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); } } Performance-0.6.0/GSSkipMutableArray.h000066400000000000000000000060031436026266200176060ustar00rootroot00000000000000/** Copyright (C) 2006 Free Software Foundation, Inc. Written by: Matt Rice 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 /**

An NSMutableArray category to provide an NSMutableArray instance which uses a skip list variant for it's underlying data structure.

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.

The underlying data structure looks much like the figure below:

index -> HEAD 1 2 3 4 5 6 TAIL 5| ---------------------> # ------> # 3| -----------> 2 ------> # ------> # 1| -> 1 -> 1 -> 1 -> 1 -> 1 -> # -> #

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.

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.

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.

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).

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).

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.

*/ @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 Performance-0.6.0/GSSkipMutableArray.m000066400000000000000000000171121436026266200176160ustar00rootroot00000000000000/** Copyright (C) 2006 Free Software Foundation, Inc. Written by: Matt Rice 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 #import #import #import #import #import #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) |{ 0 | }", 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%@ |{ %i | }", 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: @"|{ %i | }", 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 Performance-0.6.0/GSThreadPool.h000066400000000000000000000115121436026266200164310ustar00rootroot00000000000000#if !defined(INCLUDED_GSTHREADPOOL) #define INCLUDED_GSTHREADPOOL 1 /** Copyright (C) 2010 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald 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 @class GSLinkedList; @class NSDate; @class NSRecursiveLock; /** This class provides a thread pool for performing methods * of objects in parallel in other threads.
* 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.
* Always returns the same instance whenever the method is called. */ + (GSThreadPool*) sharedPool; /** Waits until the pool of operations is empty 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 humnan 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.
* You may add an object more than once, but that may result in the operation * being performed simultaneously in more than one thread.
* If the pool is configured with zero threads or the queue of operations is * full, this method will simply perform the operation immediately.
* 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.
* Default is 100.
* 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).
* Default is 2.
* 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.
* 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 Performance-0.6.0/GSThreadPool.m000066400000000000000000000273031436026266200164430ustar00rootroot00000000000000#include #import "GSLinkedList.h" #import "GSThreadPool.h" #import #import #import #import #import #import @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]; 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]; } 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.
* 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 Performance-0.6.0/GSThroughput.h000066400000000000000000000234521436026266200165470ustar00rootroot00000000000000/** Copyright (C) 2005-2008 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: October 2005 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. $Date$ $Revision$ */ #ifndef INCLUDED_GSThroughput_H #define INCLUDED_GSThroughput_H #import @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; /** *

The GSThroughput class is used maintain statistics about the number * of events or the duration of operations in your software. *

*

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. *

*

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.
* 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. *

*

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.
* 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. *

*

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. *

*/ @interface GSThroughput : NSObject { void *_data; } /** * Return all the current throughput measuring objects in the current thread. * NB. This does not return instances from other threads. */ + (NSArray*) allInstances; /** * Return a report on all GSThroughput instances in the current thread...
* This calls the [GSThroughput-description] method of the individual instances * to get a report on each one.
* 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). */ + (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.
* Passing a value of NO for aFlag will turn off the timer for the current * thread.
* For the timer to work, the thread's runloop must be running.
* 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. */ + (void) setTick: (BOOL)aFlag; /** * Updates the monitoring system's notion of the current time for all * instances associated with the current thread.
* This should be called at the start of each second (or more often) if * you want an accurate breakdown of monitoring by the second.
* If you don't want to call this yourself, you can call +setTick: to * have it called automatically.
* 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. */ + (void) tick; /** * Add to the count of the number of transactions for the receiver.
* 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 * total duration.
* 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.
* 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.
* You may use this method only if the receiver was initialised with * duration logging turned on. */ - (void) addDuration: (NSTimeInterval)length; /** * Returns a string describing the status of the receiver.
* 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.
* 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. */ - (NSString*) description; /** Sets a flag to say whether the receiver will send GSThroughputNotification * at the end of each minute to provide information about statistics.
* The method returnes the previous setting. The initial setting is NO.
* 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: * * GSThroughputCountKey * The number of events recorded (unsigned integer number) * GSThroughputMaximumKey * The maximum event duration (double floating point number) * GSThroughputMinimumKey * The minimum event duration (double floating point number) * or -1.0 if no events occurred during the minute. * GSThroughputTimeKey * The time of the start of the minute (an NSDate) * GSThroughputTotalKey * The sum of event durations (double floating point number) * */ - (BOOL) enableNotifications: (BOOL)flag; /** * Ends duration recording for the current event started by a matching * call to the -startDuration: method.
* 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. */ - (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).
* 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. */ - (id) init; /** *

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. *

*

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. *

*

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. *

*/ - (id) initWithDurations: (BOOL)aFlag forPeriods: (unsigned)numberOfPeriods ofLength: (unsigned)minutesPerPeriod; /** * Return the name of this instance (as set using -setName:).
* This is used in the -description method and for ordering instances * in the +description method. */ - (NSString*) name; /** * Sets the name of this instance. */ - (void) setName: (NSString*)name; /** * Starts recording the duration of an event. This must be followed by * a matching call to the -endDuration method.
* The name argument is used to identify the location of the call for * debugging/logging purposes, and you must ensure that the string * continues to exist up to the point where -endDuration is called, * as the receiver will not retain it.
* You may use this method only if the receiver was initialised with * duration logging turned on.
* 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. */ - (void) startDuration: (NSString*)name; @end #endif Performance-0.6.0/GSThroughput.m000066400000000000000000000615431436026266200165570ustar00rootroot00000000000000/* -*-objc-*- */ /** Implementation of GSThroughput for GNUStep Copyright (C) 2005 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: October 2005 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. $Date$ $Revision$ */ #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import "GSThroughput.h" #import "GSTicker.h" NSString * const GSThroughputNotification = @"GSThroughputNotification"; NSString * const GSThroughputCountKey = @"Count"; NSString * const GSThroughputMaximumKey = @"Maximum"; NSString * const GSThroughputMinimumKey = @"Maximum"; NSString * const GSThroughputTimeKey = @"Time"; NSString * const GSThroughputTotalKey = @"Total"; #define MAXDURATION 24.0*60.0*60.0 @class GSThroughputThread; static Class NSDateClass = 0; static SEL tiSel = 0; static NSTimeInterval (*tiImp)(Class,SEL) = 0; typedef struct { unsigned cnt; // Number of events. unsigned tick; // Start time } CountInfo; typedef struct { unsigned cnt; // Number of events. NSTimeInterval max; // Longest duration NSTimeInterval min; // Shortest duration NSTimeInterval sum; // Total (sum of durations for event) unsigned tick; // Start time } DurationInfo; typedef struct { void *seconds; void *minutes; void *periods; void *total; BOOL supportDurations; BOOL notify; unsigned numberOfPeriods; unsigned minutesPerPeriod; unsigned second; unsigned minute; unsigned period; unsigned last; // last tick used NSTimeInterval started; // When duration logging started. NSString *event; // Name of current event NSString *name; // Name of this instance GSThroughputThread *thread; // Thread info } Item; #define my ((Item*)_data) #define cseconds ((CountInfo*)my->seconds) #define cminutes ((CountInfo*)my->minutes) #define cperiods ((CountInfo*)my->periods) #define dseconds ((DurationInfo*)my->seconds) #define dminutes ((DurationInfo*)my->minutes) #define dperiods ((DurationInfo*)my->periods) @interface GSThroughputThread : NSObject { @public NSHashTable *instances; } @end @interface GSThroughput (Private) + (GSThroughputThread*) _threadInfo; + (void) newSecond: (GSThroughputThread*)t; - (void) _detach; - (void) _update; @end @implementation GSThroughputThread - (void) dealloc { if (instances != 0) { NSHashEnumerator e; GSThroughput *i; e = NSEnumerateHashTable(instances); while ((i = (GSThroughput*)NSNextHashEnumeratorItem(&e)) != nil) { [i _detach]; } NSEndHashTableEnumeration(&e); NSFreeHashTable(instances); instances = 0; } [super dealloc]; } - (id) init { if (nil != (self = [super init])) { instances = NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0); } return self; } @end @implementation GSThroughput (Private) + (GSThroughputThread*) _threadInfo { GSThroughputThread *t; t = [[[NSThread currentThread] threadDictionary] objectForKey: @"GSThroughput"]; if (t == nil) { t = [GSThroughputThread new]; [[[NSThread currentThread] threadDictionary] setObject: t forKey: @"GSThroughput"]; [t release]; } return t; } + (void) newSecond: (GSThroughputThread*)t { NSHashEnumerator e; GSThroughput *i; e = NSEnumerateHashTable(t->instances); while ((i = (GSThroughput*)NSNextHashEnumeratorItem(&e)) != nil) { [i _update]; } NSEndHashTableEnumeration(&e); } - (void) _detach { my->thread = nil; } - (void) _update { NSTimeInterval base; unsigned tick; if (my->thread == nil) { return; } base = GSTickerTimeStart(); tick = GSTickerTimeTick(); if (my->numberOfPeriods > 0) { unsigned i; if (my->supportDurations == YES) { while (my->last < tick) { DurationInfo *info; if (my->second++ == 59) { info = &dminutes[my->minute]; for (i = 0; i < 60; i++) { DurationInfo *from = &dseconds[i]; info->cnt += from->cnt; if (from->min < info->min) { info->min = from->min; } if (from->max > info->max) { info->max = from->max; } info->sum += from->sum; } if (my->notify == YES && my->last > 59) { if (info->min >= MAXDURATION) { info->min = -1.0; } [[NSNotificationCenter defaultCenter] postNotificationName: GSThroughputNotification object: self userInfo: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithUnsignedInt: info->cnt], GSThroughputCountKey, [NSNumber numberWithDouble: info->max], GSThroughputMaximumKey, [NSNumber numberWithDouble: info->min], GSThroughputMinimumKey, [NSNumber numberWithDouble: info->sum], GSThroughputTotalKey, [NSDate dateWithTimeIntervalSinceReferenceDate: base + my->last - 60], GSThroughputTimeKey, nil]]; if (info->min < 0.0) { info->min = MAXDURATION; } } if (my->minute++ == my->minutesPerPeriod - 1) { info = &dperiods[my->period]; for (i = 0; i < my->minutesPerPeriod; i++) { DurationInfo *from = &dminutes[i]; info->cnt += from->cnt; if (from->min > 0.0 && from->min < info->min) { info->min = from->min; } if (from->max > info->max) { info->max = from->max; } info->sum += from->sum; } if (my->period++ == my->numberOfPeriods - 1) { my->period = 0; } info = &dperiods[my->period]; info->cnt = 0; info->max = 0.0; info->min = MAXDURATION; info->sum = 0.0; info->tick = my->last; my->minute = 0; } info = &dminutes[my->minute]; info->cnt = 0; info->max = 0.0; info->min = MAXDURATION; info->sum = 0.0; info->tick = my->last; my->second = 0; } info = &dseconds[my->second]; info->cnt = 0; info->max = 0.0; info->min = MAXDURATION; info->sum = 0.0; info->tick = my->last; my->last++; } } else { while (my->last < tick) { CountInfo *info; if (my->second++ == 59) { info = &cminutes[my->minute]; for (i = 0; i < 60; i++) { info->cnt += cseconds[i].cnt; } if (my->notify == YES && my->last > 59) { [[NSNotificationCenter defaultCenter] postNotificationName: GSThroughputNotification object: self userInfo: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithUnsignedInt: info->cnt], GSThroughputCountKey, [NSDate dateWithTimeIntervalSinceReferenceDate: base + my->last - 60], GSThroughputTimeKey, nil]]; } if (my->minute++ == my->minutesPerPeriod - 1) { info = &cperiods[my->period]; for (i = 0; i < my->minutesPerPeriod; i++) { info->cnt += cminutes[i].cnt; } if (my->period++ == my->numberOfPeriods - 1) { my->period = 0; } info = &cperiods[my->period]; info->cnt = 0; info->tick = my->last; my->minute = 0; } info = &cminutes[my->minute]; info->cnt = 0; info->tick = my->last; my->second = 0; } info = &cseconds[my->second]; info->cnt = 0; info->tick = my->last; my->last++; } } } else { while (my->last < tick) { if (my->second++ == 59) { my->second = 0; if (my->supportDurations == YES) { DurationInfo *info = &dseconds[1]; if (my->notify == YES && my->last > 59) { if (info->min == MAXDURATION) { info->min = -1.0; } [[NSNotificationCenter defaultCenter] postNotificationName: GSThroughputNotification object: self userInfo: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithUnsignedInt: info->cnt], GSThroughputCountKey, [NSNumber numberWithDouble: info->max], GSThroughputMaximumKey, [NSNumber numberWithDouble: info->min], GSThroughputMinimumKey, [NSNumber numberWithDouble: info->sum], GSThroughputTotalKey, [NSDate dateWithTimeIntervalSinceReferenceDate: base + my->last - 60], GSThroughputTimeKey, nil]]; } info->cnt = 0; info->max = 0.0; info->min = MAXDURATION; info->sum = 0.0; } else { CountInfo *info = &cseconds[1]; if (my->notify == YES && my->last > 59) { [[NSNotificationCenter defaultCenter] postNotificationName: GSThroughputNotification object: self userInfo: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithUnsignedInt: info->cnt], GSThroughputCountKey, [NSDate dateWithTimeIntervalSinceReferenceDate: base + my->last - 60], GSThroughputTimeKey, nil]]; } info->cnt = 0; } } my->last++; } } } @end @implementation GSThroughput + (NSArray*) allInstances { GSThroughputThread *t; NSArray *a; t = [[[NSThread currentThread] threadDictionary] objectForKey: @"GSThroughput"]; if (t == nil) { a = nil; } else { a = NSAllHashTableObjects(t->instances); } return a; } + (NSString*) description { GSThroughputThread *t; NSMutableString *ms; ms = [NSMutableString stringWithString: [super description]]; t = [[[NSThread currentThread] threadDictionary] objectForKey: @"GSThroughput"]; if (t != nil) { NSArray *a; NSEnumerator *e; GSThroughput *c; a = [NSAllHashTableObjects(t->instances) sortedArrayUsingSelector: @selector(compare:)]; e = [a objectEnumerator]; while ((c = (GSThroughput*)[e nextObject]) != nil) { [ms appendFormat: @"\n%@", [c description]]; } } return ms; } + (void) initialize { if (NSDateClass == 0) { NSDateClass = [NSDate class]; tiSel = @selector(timeIntervalSinceReferenceDate); tiImp = (NSTimeInterval (*)(Class,SEL))[NSDateClass methodForSelector: tiSel]; } } + (void) setTick: (BOOL)aFlag { if (aFlag == YES) { GSThroughputThread *t = [self _threadInfo]; [GSTicker registerObserver: (id)self userInfo: t]; } else { [GSTicker unregisterObserver: (id)self]; } } + (void) tick { [self newSecond: [self _threadInfo]]; } - (void) add: (unsigned)count { if (NO != my->supportDurations) { [NSException raise: NSInternalInconsistencyException format: @"-add: called when set for durations"]; } if (my->numberOfPeriods == 0) { cseconds[0].cnt += count; // Total cseconds[1].cnt += count; // Current minute } else { cseconds[my->second].cnt += count; } } - (void) add: (unsigned)count duration: (NSTimeInterval)length { if (YES != my->supportDurations) { [NSException raise: NSInternalInconsistencyException format: @"-add:duration: called when not set for durations"]; } if (count > 0) { NSTimeInterval total = length; unsigned from; unsigned to; length /= count; if (my->numberOfPeriods == 0) { from = 0; // total to = 1; // current minute } else { from = my->second; to = from; } while (from <= to) { DurationInfo *info = &dseconds[from++]; if (info->cnt == 0) { info->cnt = count; info->min = length; info->max = length; info->sum = total; } else { info->cnt += count; info->sum += total; if (length > info->max) { info->max = length; } if (length < info->min) { info->min = length; } } } } } - (void) addDuration: (NSTimeInterval)length { unsigned from; unsigned to; if (YES != my->supportDurations) { [NSException raise: NSInternalInconsistencyException format: @"-addDuration: called when not set for durations"]; } if (my->numberOfPeriods == 0) { from = 0; // Total to = 1; // Current minute } else { from = my->second; to = from; } while (from <= to) { DurationInfo *info = &dseconds[from++]; if (info->cnt++ == 0) { info->min = length; info->max = length; info->sum = length; } else { info->sum += length; if (length > info->max) { info->max = length; } if (length < info->min) { info->min = length; } } } } - (NSComparisonResult) compare: (id)other { if ([other isKindOfClass: [GSThroughput class]] == YES) { NSString *myName = [self name]; NSString *otherName = [other name]; if (myName == nil) { myName = @""; } if (otherName == nil) { otherName = @""; } return [myName compare: otherName]; } return NSOrderedAscending; } - (void) dealloc { if (_data) { if (my->seconds != 0) { NSZoneFree(NSDefaultMallocZone(), my->seconds); } [my->name release]; if (my->thread != nil) { NSHashRemove(my->thread->instances, (void*)self); my->thread = nil; } NSZoneFree(NSDefaultMallocZone(), _data); _data = 0; } [super dealloc]; } static void appendCountInfo(CountInfo *info, NSMutableString *m, NSTimeInterval base) { NSDate *d = [NSDate alloc]; d = [d initWithTimeIntervalSinceReferenceDate: info->tick + base]; [m appendFormat: @"%u, %@\n", info->cnt, d]; RELEASE(d); } static void appendDurationInfo(DurationInfo *info, NSMutableString *m, NSTimeInterval base) { NSDate *d = [NSDate alloc]; d = [d initWithTimeIntervalSinceReferenceDate: info->tick + base]; if (info->cnt) { [m appendFormat: @"%u, %g, %g, %g, %@\n", info->cnt, info->max, info->min, info->sum, d]; } else { [m appendFormat: @"0, -, -, -, %@\n", d]; } RELEASE(d); } - (NSString*) description { NSAutoreleasePool *pool = [NSAutoreleasePool new]; NSString *n = my->name; NSMutableString *m; unsigned i; if (n == nil) { n = [super description]; } m = [n mutableCopy]; if (my->thread != nil) { NSTimeInterval baseTime = GSTickerTimeStart(); unsigned tick; if (my->numberOfPeriods == 0) { if (my->supportDurations == YES) { DurationInfo *info = &dseconds[0]; [m appendFormat: @": cnt %u, max %g, min %g, avg %g", info->cnt, info->max, info->min == MAXDURATION ? 0.0 : info->min, info->cnt == 0 ? 0 : info->sum / info->cnt]; } else { CountInfo *info = &cseconds[0]; [m appendFormat: @": cnt %u", info->cnt]; } } else { if (my->supportDurations == YES) { [m appendString: @"\nSeconds in current minute:\n"]; if (my->second > 0) { tick = 0; for (i = 0; i < my->second; i++) { DurationInfo *info = &dseconds[i]; if (info->tick != tick) { tick = info->tick; appendDurationInfo(info, m, baseTime); } } } [m appendString: @"\nPrevious minutes in current period:\n"]; if (my->minute > 0) { tick = 0; for (i = 0; i < my->minute; i++) { DurationInfo *info = &dminutes[i]; if (info->tick != tick) { tick = info->tick; appendDurationInfo(info, m, baseTime); } } } [m appendString: @"\nPrevious periods:\n"]; if (my->period > 0) { tick = 0; /* Periods from last cycle */ for (i = my->period; i < my->numberOfPeriods; i++) { DurationInfo *info = &dperiods[i]; if (info->tick != tick) { tick = info->tick; appendDurationInfo(info, m, baseTime); } } /* Periods from current cycle */ for (i = 0; i < my->period; i++) { DurationInfo *info = &dperiods[i]; if (info->tick != tick) { tick = info->tick; appendDurationInfo(info, m, baseTime); } } } } else { [m appendString: @"\nSeconds in current minute:\n"]; if (my->second > 0) { tick = 0; for (i = 0; i < my->second; i++) { CountInfo *info = &cseconds[i]; if (info->tick != tick) { tick = info->tick; appendCountInfo(info, m, baseTime); } } } [m appendString: @"\nPrevious minutes in current period:\n"]; if (my->minute > 0) { tick = 0; for (i = 0; i < my->minute; i++) { CountInfo *info = &cminutes[i]; if (info->tick != tick) { tick = info->tick; appendCountInfo(info, m, baseTime); } } } [m appendString: @"\nPrevious periods:\n"]; if (my->period > 0) { tick = 0; /* Periods from last cycle */ for (i = my->period; i < my->numberOfPeriods; i++) { CountInfo *info = &cperiods[i]; if (info->tick != tick) { tick = info->tick; appendCountInfo(info, m, baseTime); } } /* Periods from current cycle */ for (i = 0; i < my->period; i++) { CountInfo *info = &cperiods[i]; if (info->tick != tick) { tick = info->tick; appendCountInfo(info, m, baseTime); } } } } } } [pool release]; return [m autorelease]; } - (void) endDuration { if (my->started > 0.0) { NSTimeInterval ti; ti = (*tiImp)(NSDateClass, tiSel) - my->started; my->event = nil; my->started = 0.0; [self addDuration: ti]; } } - (BOOL) enableNotifications: (BOOL)flag { BOOL old = my->notify; my->notify = flag; return old; } - (void) endDuration: (unsigned)count { if (my->started > 0.0) { [self add: count duration: (*tiImp)(NSDateClass, tiSel) - my->started]; my->event = nil; my->started = 0.0; } } - (id) init { return [self initWithDurations: YES forPeriods: 96 ofLength: 15]; } - (id) initWithDurations: (BOOL)aFlag forPeriods: (unsigned)numberOfPeriods ofLength: (unsigned)minutesPerPeriod { if (nil != (self = [super init])) { NSCalendarDate *c; unsigned i; _data = (Item*)NSZoneCalloc(NSDefaultMallocZone(), 1, sizeof(Item)); /* * Add this instance to the current thread. */ my->thread = [[self class] _threadInfo]; NSHashInsert(my->thread->instances, (void*)self); my->supportDurations = aFlag; my->notify = NO; my->last = GSTickerTimeTick(); c = [[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate: GSTickerTimeLast()]; my->second = [c secondOfMinute]; i = [c hourOfDay] * 60 + [c minuteOfHour]; if (numberOfPeriods < 1 || minutesPerPeriod < 1) { /* If we are not using periods of N minutes, we must just be keeping * a running total recorded second by second. */ my->numberOfPeriods = 0; my->minutesPerPeriod = 0; my->minute = i; my->period = 0; if (my->supportDurations == YES) { DurationInfo *ptr; ptr = (DurationInfo*)NSZoneCalloc (NSDefaultMallocZone(), 2, sizeof(DurationInfo)); my->seconds = ptr; my->minutes = 0; my->periods = 0; dseconds[0].tick = my->last; dseconds[0].max = 0; dseconds[0].min = MAXDURATION; dseconds[0].sum = 0; dseconds[0].cnt = 0; dseconds[1].tick = my->last; dseconds[1].max = 0; dseconds[1].min = 0; dseconds[1].sum = 0; dseconds[1].cnt = 0; } else { CountInfo *ptr; ptr = (CountInfo*)NSZoneCalloc (NSDefaultMallocZone(), 2, sizeof(CountInfo)); my->seconds = ptr; my->minutes = 0; my->periods = 0; cseconds[0].tick = my->last; cseconds[0].cnt = 0; cseconds[1].tick = my->last; cseconds[1].cnt = 0; } } else { my->numberOfPeriods = numberOfPeriods; my->minutesPerPeriod = minutesPerPeriod; my->minute = i % minutesPerPeriod; my->period = (i / minutesPerPeriod) % numberOfPeriods; i = 60 + minutesPerPeriod + numberOfPeriods; if (my->supportDurations == YES) { DurationInfo *ptr; ptr = (DurationInfo*)NSZoneCalloc (NSDefaultMallocZone(), i, sizeof(DurationInfo)); my->seconds = ptr; my->minutes = ptr + 60; my->periods = ptr + 60 + minutesPerPeriod; dseconds[my->second].tick = my->last; dminutes[my->minute].tick = my->last; dperiods[my->period].tick = my->last; for (i = 0; i < my->numberOfPeriods; i++) { dperiods[i].min = MAXDURATION; } for (i = 0; i < my->minutesPerPeriod; i++) { dminutes[i].min = MAXDURATION; } for (i = 0; i < 60; i++) { dseconds[i].min = MAXDURATION; } } else { CountInfo *ptr; ptr = (CountInfo*)NSZoneCalloc (NSDefaultMallocZone(), i, sizeof(CountInfo)); my->seconds = ptr; my->minutes = ptr + 60; my->periods = ptr + 60 + minutesPerPeriod; cseconds[my->second].tick = my->last; cminutes[my->minute].tick = my->last; cperiods[my->period].tick = my->last; } } [c release]; } return self; } - (NSString*) name { return my->name; } - (void) setName: (NSString*)name { [name retain]; [my->name release]; my->name = name; } - (void) startDuration: (NSString*)name { if (NO == my->supportDurations) { [NSException raise: NSInternalInconsistencyException format: @"-startDuration: for '%@' when not set for durations", name]; } if (0.0 != my->started) { [NSException raise: NSInternalInconsistencyException format: @"-startDuration: for '%@' when already started", name]; } if (my->event != nil) { [NSException raise: NSInternalInconsistencyException format: @"-startDuration: for '%@' nested inside '%@'", my->event, name]; } my->started = (*tiImp)(NSDateClass, tiSel); my->event = name; } @end Performance-0.6.0/GSTicker.h000066400000000000000000000116671436026266200156240ustar00rootroot00000000000000/** Copyright (C) 2005 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: Nov 2005 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. $Date$ $Revision$ */ #ifndef INCLUDED_GSTicker_H #define INCLUDED_GSTicker_H @class NSDate; /** * Returns the timestamp of the most recent call to GSTickerTimeNow(). */ extern NSTimeInterval GSTickerTimeLast(); /** * Convenience function to provide timing information quickly.
* This returns the current date/time, and stores the value for use * by the GSTickerTimeLast() function. */ extern NSTimeInterval GSTickerTimeNow(); /** * This returns the timestamp from which GSTicker was first used. */ extern NSTimeInterval GSTickerTimeStart(); /** * A convenience method to return the current clock 'tick' ... which is * the current second based on the time we started. This does not * check the current time, but relies on GSTickerTimeLast() returning an * up to date value (so if you need an accurate tick, you should ensure * that GSTickerTimeNow() is called at least once a second).
* The returned value is always greater than zero, and is basically * calculated as (GSTickerTimeLast() - GSTickerTimeStart() + 1).
* In the event that the system clock is reset into the past, the value * of GSTickerTimeStart() is automatically adjusted to ensure that the * result of a call to GSTickerTimeTick() is never less than the result * of any earlier call to the function. */ extern unsigned GSTickerTimeTick(); @protocol GSTicker /** * Sent to tell observers that the ticker has noticed that a new * second has occurred. The tick argument is the user information * provided in when registering the observer.
* This message is sent to each registered observer when the timeout * for the thread occurs. It is not guaranteed to be called every * second ... other processing in the thread may delay it. */ - (void) newSecond: (id)userInfo; @end /** * Wrapper round the more efficient ticker functions ... this class * provides convenience methods to get NSDate objects, and also * provides support for registering observers ofr second by second * ticks. */ @interface GSTicker : NSObject { } /** * Calls GSTickerTimeLast() and returns the result as an NSDate. */ + (NSDate*) last; /** * A dummy method ... does nothing, but allows the GSTicker class itsself * to act as an observer of regular timeouts.
* Thus, you may register the class as its own observer in order to set * up a timer to ensure that date/time information is updated at the * start of every second. */ + (void) newSecond: (id)userInfo; /** * Calls GSTickerTimeNow() and returns the result as an NSDate. */ + (NSDate*) now; /** * Registers an object to receive a [(GSTicker)-newSecond:] message * at the start of every second.
* Also starts a timer in the current thread (unless one is already * running) to notify registered objects of new seconds.
* The observer and the userInfo are retained by the ticker.
* Adding an observer more than once has no effect.
* NB. You must not add or remove observers inside the callback routine. */ + (void) registerObserver: (id)anObject userInfo: (id)userInfo; /** * Returns the result of GSTickerTimeStart() as an NSDate. */ + (NSDate*) start; /** * Calls GSTickerTimeTick() and returns the result. */ + (unsigned) tick; /** * Unregisters an observer previously set in the current thread using * the +registerObserver:userInfo: method.
* If all observers in a thread are removed, the timer for the thread * is cancelled at the start of the next second. */ + (void) unregisterObserver: (id)anObject; /** * Calls GSTickerTimeNow(); */ + (void) update; /** * Calls GSTickerTimeLast() and returns the result as an NSDate. */ - (NSDate*) last; /** * See +newSecond: */ - (void) newSecond: (id)userInfo; /** * Calls GSTickerTimeNow() and returns the result as an NSDate. */ - (NSDate*) now; /** * Returns the result of GSTickerTimeStart() as an NSDate. */ - (NSDate*) start; /** * Calls GSTickerTimeTick() and returns the result. */ - (unsigned) tick; /** * Calls GSTickerTimeNow(); */ - (void) update; @end #endif Performance-0.6.0/GSTicker.m000066400000000000000000000165071436026266200156270ustar00rootroot00000000000000/* -*-objc-*- */ /** Implementation of GSTicker for GNUStep Copyright (C) 2005 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: November 2005 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. $Date$ $Revision$ */ #import #import #import #import #import #import #import #import "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; @interface GSTickerObservation : NSObject { @public id observer; id userInfo; } - (void) fire: (NSArray*)live; @end @implementation GSTickerObservation - (void) fire: (NSArray*)live { if ([live indexOfObjectIdenticalTo: self] != NSNotFound) { [observer newSecond: userInfo]; } } @end @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 @implementation GSTickerThread - (void) dealloc { [theTimer invalidate]; [theTimer release]; theTimer = nil; [observers release]; observers = nil; [super dealloc]; } - (id) init { if (nil != (self = [super init])) { NSTimeInterval ti = GSTickerTimeNow(); observers = [NSMutableArray new]; theTimer = [[NSTimer scheduledTimerWithTimeInterval: ti - (int)ti target: [GSTicker class] selector: @selector(_tick:) userInfo: self repeats: NO] retain]; } return self; } @end @interface GSTicker (Private) + (void) _tick: (NSTimer*)t; @end inline NSTimeInterval GSTickerTimeLast() { return lastTime; } inline NSTimeInterval GSTickerTimeStart() { if (baseTime == 0) { [GSTicker class]; } return baseTime; } inline unsigned GSTickerTimeTick() { NSTimeInterval start = GSTickerTimeStart(); return (GSTickerTimeLast() - start) + 1; } NSTimeInterval GSTickerTimeNow() { if (baseTime == 0) { [GSTicker class]; } else { NSTimeInterval now; /* * If the clock has been reset so that time has gone backwards, * we adjust the baseTime so that lastTime >= baseTime is true. */ now = (*tiImp)(NSDateClass, tiSel); if (now < lastTime) { baseTime -= (lastTime - now); } lastTime = now; } 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: GSTickerTimeLast()]; } + (void) newSecond: (id)userInfo { return; } + (NSDate*) now { return [NSDateClass dateWithTimeIntervalSinceReferenceDate: GSTickerTimeNow()]; } + (void) registerObserver: (id)anObject userInfo: (id)userInfo { GSTickerThread *tt; GSTickerObservation *to; unsigned count; tt = [[[NSThread currentThread] threadDictionary] objectForKey: @"GSTickerThread"]; if (tt == nil) { tt = [GSTickerThread new]; [[[NSThread currentThread] threadDictionary] setObject: tt forKey: @"GSTickerThread"]; [tt release]; } count = [tt->observers count]; while (count-- > 0) { to = [tt->observers objectAtIndex: count]; if (to->observer == anObject) { return; // Already registered. } } to = [GSTickerObservation new]; to->observer = anObject; to->userInfo = userInfo; [tt->observers addObject: to]; [to release]; } + (NSDate*) start { if (startDate == nil) { startDate = [NSDateClass alloc]; startDate = [startDate initWithTimeIntervalSinceReferenceDate: GSTickerTimeStart()]; } return startDate; } + (unsigned) tick { return GSTickerTimeTick(); } + (void) unregisterObserver: (id)anObject { GSTickerThread *tt; tt = [[[NSThread currentThread] threadDictionary] objectForKey: @"GSTickerThread"]; if (tt != nil) { GSTickerObservation *to; unsigned count = [tt->observers count]; while (count-- > 0) { to = [tt->observers objectAtIndex: count]; if (to->observer == anObject) { [tt->observers removeObjectAtIndex: count]; break; } } } } + (void) update { GSTickerTimeNow(); } - (NSDate*) last { return [NSDateClass dateWithTimeIntervalSinceReferenceDate: GSTickerTimeLast()]; } - (void) newSecond: (id)userInfo { return; } - (NSDate*) now { return [NSDateClass dateWithTimeIntervalSinceReferenceDate: GSTickerTimeNow()]; } - (NSDate*) start { return startDate; } - (unsigned) tick { return GSTickerTimeTick(); } - (void) update { GSTickerTimeNow(); } @end @implementation GSTicker (Private) + (void) _tick: (NSTimer*)t { GSTickerThread *tt = [t userInfo]; if (tt == nil) { tt = [[[NSThread currentThread] threadDictionary] objectForKey: @"GSTickerThread"]; } if (tt != nil && [tt->observers count] > 0) { NSTimeInterval ti; [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]; } } ti = GSTickerTimeNow(); tt->theTimer = [[NSTimer scheduledTimerWithTimeInterval: ti - (int)ti target: self selector: @selector(_tick:) userInfo: tt repeats: NO] retain]; } else { [[[NSThread currentThread] threadDictionary] removeObjectForKey: @"GSTickerThread"]; } } @end Performance-0.6.0/GSUniqued.h000066400000000000000000000063651436026266200160140ustar00rootroot00000000000000#if !defined(INCLUDED_GSUNIQUED) #define INCLUDED_GSUNIQUED 1 /** Copyright (C) 2014 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald 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 /** Class used to unique other objects.
*

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.
* 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.
* 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.
* In short, use with care; while uniquing can have a big performance * advantage for some programs, this is actually quite rare. *

*

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.
* Access to the hash table is protected by locks so that uniqued * objects may be used freely in multiple threads.
* The name of the subclass used is the name of the original class * with 'GSUniqued' added as a prefix. *

*/ @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.
* 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)anObject; @end /** Category for uniquing any copyable object.
* 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 Performance-0.6.0/GSUniqued.m000066400000000000000000000140741436026266200160150ustar00rootroot00000000000000/** Copyright (C) 2014 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald 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 #import #import #import #import #import #import #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)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)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)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)self]; } @end Performance-0.6.0/Info.plist000066400000000000000000000014131436026266200157340ustar00rootroot00000000000000 CFBundleDevelopmentRegion English CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIconFile CFBundleIdentifier org.gnustep.performance CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType FMWK CFBundleSignature ???? CFBundleVersion 1.0 NSPrincipalClass Performance-0.6.0/NSObject+GSExtensions.h000066400000000000000000000041171436026266200201750ustar00rootroot00000000000000/** Declaration of extension methods for base additions Copyright (C) 2003-2010 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald and: Adam Fedor 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 #if !defined(GNUSTEP) #define RETAIN(x) ([x retain]) #endif @class NSHashTable; @interface NSObject(MemoryFootprint) /* This method returns the memory usage of the receiver, excluding any * objects already present in the exclude table.
* 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.
* 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).
* 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 Performance-0.6.0/NSObject+GSExtensions.m000066400000000000000000000024721436026266200202040ustar00rootroot00000000000000/* Implementation of extension methods to base additions Copyright (C) 2010 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald 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 @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 Performance-0.6.0/Performance.h000066400000000000000000000022741436026266200164040ustar00rootroot00000000000000/* JIGS import file for the GNUstep Performance Library. Copyright (C) 2005 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: Novb 2005 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 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., 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" Performance-0.6.0/Performance.jigs000066400000000000000000000011161436026266200171030ustar00rootroot00000000000000{ /* -*-c-*- */ "prerequisite libraries" = ( "gnustep-base" ); types = { NSTimeInterval = double; }; classes = ( { "java name" = "gnu.gnustep.Performance.GSCache"; "objective-c name" = "GSCache"; "class methods" = ( "allInstances" ); "instance methods" = ( "currentObjects", "currentSize", "lifetime", "maxObjects", "maxSize", "name", "objectForKey:", "purge", "setLifetime:", "setMaxObjects:", "setMaxSize:", "setName:", "setObject:forKey:", "setObject:forKey:lifetime:", "shrinkObjects:andSize:" ); }, ); } Performance-0.6.0/Performance.xcodeproj/000077500000000000000000000000001436026266200202225ustar00rootroot00000000000000Performance-0.6.0/Performance.xcodeproj/project.pbxproj000066400000000000000000000475301436026266200233070ustar00rootroot00000000000000// !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 42; 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 */; }; 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 = ""; }; 0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 089C1667FE841158C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 85872EB01284CFC700B4601E /* GSCache.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSCache.h; sourceTree = ""; }; 85872EB11284CFC700B4601E /* GSCache.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSCache.m; sourceTree = ""; }; 85872EB21284CFC700B4601E /* GSIndexedSkipList.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSIndexedSkipList.h; sourceTree = ""; }; 85872EB31284CFC700B4601E /* GSIndexedSkipList.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSIndexedSkipList.m; sourceTree = ""; }; 85872EB41284CFC700B4601E /* GSIOThreadPool.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSIOThreadPool.h; sourceTree = ""; }; 85872EB51284CFC700B4601E /* GSIOThreadPool.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSIOThreadPool.m; sourceTree = ""; }; 85872EB61284CFC700B4601E /* GSLinkedList.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSLinkedList.h; sourceTree = ""; }; 85872EB71284CFC700B4601E /* GSLinkedList.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSLinkedList.m; sourceTree = ""; }; 85872EB81284CFC700B4601E /* GSSkipMutableArray.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSSkipMutableArray.h; sourceTree = ""; }; 85872EB91284CFC700B4601E /* GSSkipMutableArray.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSSkipMutableArray.m; sourceTree = ""; }; 85872EBA1284CFC700B4601E /* GSThreadPool.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSThreadPool.h; sourceTree = ""; }; 85872EBB1284CFC700B4601E /* GSThreadPool.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSThreadPool.m; sourceTree = ""; }; 85872EBC1284CFC700B4601E /* GSThroughput.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSThroughput.h; sourceTree = ""; }; 85872EBD1284CFC700B4601E /* GSThroughput.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSThroughput.m; sourceTree = ""; }; 85872EBE1284CFC700B4601E /* GSTicker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSTicker.h; sourceTree = ""; }; 85872EBF1284CFC700B4601E /* GSTicker.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSTicker.m; sourceTree = ""; }; 85872EC01284CFC700B4601E /* Performance.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Performance.h; sourceTree = ""; }; 8591FBED1A13422800923420 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ChangeLog; sourceTree = ""; }; 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 = ""; }; 85B7DBDB1C034FBA00AF3090 /* NSObject+GSExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+GSExtensions.m"; sourceTree = ""; }; 85B7DC011C036A0900AF3090 /* GSFIFO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GSFIFO.h; sourceTree = ""; }; 85B7DC021C036A0900AF3090 /* GSFIFO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GSFIFO.m; sourceTree = ""; }; 8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; D2F7E79907B2D74100F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; /* 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 = ""; }; 0867D691FE84028FC02AAC07 /* Performance */ = { isa = PBXGroup; children = ( 08FB77AEFE84172EC02AAC07 /* Classes */, 32C88DFF0371C24200C91783 /* Other Sources */, 089C1665FE841158C02AAC07 /* Resources */, 0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */, 034768DFFF38A50411DB9C8B /* Products */, ); name = Performance; sourceTree = ""; }; 0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */ = { isa = PBXGroup; children = ( 1058C7B0FEA5585E11CA2CBB /* Linked Frameworks */, 1058C7B2FEA5585E11CA2CBB /* Other Frameworks */, ); name = "External Frameworks and Libraries"; sourceTree = ""; }; 089C1665FE841158C02AAC07 /* Resources */ = { isa = PBXGroup; children = ( 8591FBED1A13422800923420 /* ChangeLog */, 8DC2EF5A0486A6940098B216 /* Info.plist */, 089C1666FE841158C02AAC07 /* InfoPlist.strings */, ); name = Resources; sourceTree = ""; }; 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 */, ); name = Classes; sourceTree = ""; }; 1058C7B0FEA5585E11CA2CBB /* Linked Frameworks */ = { isa = PBXGroup; children = ( 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */, ); name = "Linked Frameworks"; sourceTree = ""; }; 1058C7B2FEA5585E11CA2CBB /* Other Frameworks */ = { isa = PBXGroup; children = ( 0867D6A5FE840307C02AAC07 /* AppKit.framework */, D2F7E79907B2D74100F64583 /* CoreData.framework */, 0867D69BFE84028FC02AAC07 /* Foundation.framework */, ); name = "Other Frameworks"; sourceTree = ""; }; 32C88DFF0371C24200C91783 /* Other Sources */ = { isa = PBXGroup; children = ( ); name = "Other Sources"; sourceTree = ""; }; /* 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 */, 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 = { ORGANIZATIONNAME = gnustep.org; }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Performance" */; compatibilityVersion = "Xcode 2.4"; 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 = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 1DEB91AE08733DA50010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(NATIVE_ARCH_ACTUAL)"; 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; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; }; name = Debug; }; 1DEB91AF08733DA50010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1)"; ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1 = "x86_64 i386 ppc"; 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; WRAPPER_EXTENSION = framework; }; name = Release; }; 1DEB91B208733DA50010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(NATIVE_ARCH_ACTUAL)"; 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/SDKs/MacOSX10.5.sdk; SYMROOT = "$(PROJECT_DIR)/build"; }; name = Debug; }; 1DEB91B308733DA50010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1)"; ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1 = "x86_64 i386 ppc"; 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/SDKs/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 */; }