Compare commits

...

182 commits

Author SHA1 Message Date
rfm
7d78333d26 Avoid old function which doesn't work with new runtime 2025-01-08 15:54:19 +00:00
Riccardo
9a3db6b380
Merge pull request #2 from gnustep/mac_update2
update project to work on mac again (10.9)
2024-07-16 14:54:49 +02:00
Riccardo Mottola
1f79851205 update project to work on mac again (10.9) 2024-07-14 19:37:31 +02:00
rfm
540be025c9 Remove FIFO from hash table in -release rather than -dealloc in order to avoid possible race condition where a deallocating object could be retrieved from the table. 2024-07-09 09:15:06 +01:00
rfm
30bb11b72a Remove redundant code in -dealloc ... instance is already removed from hash table in -release method. 2024-07-09 09:14:15 +01:00
rfm
4145d7e815 Make -drain: consider active threads 2024-03-15 12:14:56 +00:00
Richard Frith-Macdonald
1a8c379be8 Improve chances of threads being removed from pool when the limit is decreased. 2023-02-03 16:50:35 +00:00
Wolfgang Lux
7fa1246f24 Raise exception when attempting to insert object with nil key 2023-01-25 11:04:51 +01:00
Wolfgang Lux
941c6e70aa Remove exception handler block that just would incorrectly unlock a lock 2023-01-24 17:19:32 +01:00
Richard Frith-Macdonald
bc8e65e380 Record last change 2023-01-20 14:02:46 +00:00
Richard Frith-Macdonald
823a8ca6bf Links from a link store should be considered owned by that store. 2023-01-20 14:00:19 +00:00
Richard Frith-Macdonald
5cc01b1ef4 New release 2023-01-13 14:07:46 +00:00
Richard Frith-Macdonald
091389ae0a Add method to get human readable status 2022-08-25 14:24:23 +01:00
Richard Frith-Macdonald
633c3a6da7 Report whether pool is suspended 2022-08-24 11:31:11 +01:00
Richard Frith-Macdonald
09dfc1c403 Add functions to help with link store 2022-08-10 21:36:48 +01:00
Richard Frith-Macdonald
25e60c6301 add missing method declaration 2022-07-11 10:20:16 +01:00
Richard Frith-Macdonald
71f24ca382 Add support for thread naming 2022-07-06 15:05:36 +01:00
Richard Frith-Macdonald
b23777adfa When setting to use defaults for config, update config based on current defs 2021-11-09 11:03:10 +00:00
Richard Frith-Macdonald
c2c1b93bac fix asignment of wrong class 2021-05-27 21:45:22 +01:00
Richard Frith-Macdonald
ab83081490 Allow the class of the links in a link store to be controlled 2021-05-22 12:27:45 +01:00
Richard Frith-Macdonald
3b16cbb283 Add -getObjectRetained and -putObjectConsumed: methods 2021-05-22 11:09:12 +01:00
Richard Frith-Macdonald
5cf55f34c2 Improvde documentation 2021-02-26 16:01:07 +00:00
Richard Frith-Macdonald
e0ba8684dc Consolodate repeated code into a function and optimise a little 2021-02-23 15:53:58 +00:00
Richard Frith-Macdonald
2e707603e5 fix error in comment 2021-02-23 13:57:10 +00:00
Richard Frith-Macdonald
509d365349 Return link information when adding objects to list 2021-02-23 13:53:48 +00:00
Richard Frith-Macdonald
2a1ec7970c fix potential nul pointer dereference 2021-01-14 09:29:42 +00:00
Richard Frith-Macdonald
6eb08935ea Simplify by assuming we will run in a multithreaded environment 2021-01-14 09:29:06 +00:00
Richard Frith-Macdonald
99e01fe21e Cope with objects which can't be copied (ie -copy returns the receiver). 2019-08-08 15:02:04 +01:00
Richard Frith-Macdonald
94eb232b96 allow programmatic setting of config where defaults don't exist 2019-04-16 12:23:28 +01:00
Richard Frith-Macdonald
42e7c87f0b fix locking error 2019-04-16 12:02:26 +01:00
Richard Frith-Macdonald
573793ce5a Allow caches to be configured programmatically 2019-04-16 11:00:05 +01:00
Richard Frith-Macdonald
3bf5d81b15 Improvements for subclassing 2018-03-27 18:45:51 +01:00
Richard Frith-Macdonald
0d411fb45a Fix failure to use argument for link store insertion 2017-10-17 17:13:54 +01:00
Richard Frith-Macdonald
8cd904225c Improve documentation of -terminate: method 2017-07-13 09:41:17 +01:00
Richard Frith-Macdonald
859dc793bf Improve timeout waiting for thread to terminate 2017-07-13 09:32:58 +01:00
Richard Frith-Macdonald
2378e82aaf make -terminate: method usable from other threads 2017-07-13 08:56:55 +01:00
Richard Frith-MacDonald
29f1bab93e add code to allow refreshing of existing items in cache
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@40424 72102866-910b-0410-8b05-ffd578937521
2017-03-28 10:58:51 +00:00
Richard Frith-MacDonald
82f18a1372 Allow very large FIFO for buffering extremely high throughput systems.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@40193 72102866-910b-0410-8b05-ffd578937521
2016-11-07 17:47:35 +00:00
Richard Frith-MacDonald
f73a0a2cb1 fix to nil-out item after release
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@39311 72102866-910b-0410-8b05-ffd578937521
2016-01-26 14:40:30 +00:00
Riccardo Mottola
f37d55d564 add GSFIFO to mac project
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@39191 72102866-910b-0410-8b05-ffd578937521
2015-11-23 16:01:15 +00:00
Riccardo Mottola
78dcc717e2 add GS MemoryReporting extensions for NSObject missing on Mac so that the whole GS base additions are not needed
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@39190 72102866-910b-0410-8b05-ffd578937521
2015-11-23 15:34:29 +00:00
Richard Frith-MacDonald
16b09c5202 add option for writing a whole block in one go
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@39039 72102866-910b-0410-8b05-ffd578937521
2015-10-07 13:54:54 +00:00
Richard Frith-MacDonald
33be15ea6c fix missing declaration
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@39020 72102866-910b-0410-8b05-ffd578937521
2015-10-02 10:34:43 +00:00
Richard Frith-MacDonald
6101a48351 fxup bad declaration
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38857 72102866-910b-0410-8b05-ffd578937521
2015-08-06 19:44:31 +00:00
Richard Frith-MacDonald
92a1962589 Add a few methods
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38846 72102866-910b-0410-8b05-ffd578937521
2015-07-29 14:34:58 +00:00
Richard Frith-MacDonald
08cf714574 New class
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38845 72102866-910b-0410-8b05-ffd578937521
2015-07-29 08:07:44 +00:00
Richard Frith-MacDonald
e2d66ee254 add startup and shutdown methods
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38844 72102866-910b-0410-8b05-ffd578937521
2015-07-28 20:08:10 +00:00
Richard Frith-MacDonald
d2d36aaa35 fix uninitialised variable
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38843 72102866-910b-0410-8b05-ffd578937521
2015-07-28 19:24:40 +00:00
Richard Frith-MacDonald
288e83617f Add support for setting the thread class
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38842 72102866-910b-0410-8b05-ffd578937521
2015-07-28 19:16:21 +00:00
Niels Grewe
6f267319e3 Optionally allow the caller to specify the time it wants to block on an
empty FIFO. This supplements the existing method of having a timeout on
the FIFO, and does not raise an excepion when the wait time is too long.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38814 72102866-910b-0410-8b05-ffd578937521
2015-07-17 20:08:38 +00:00
Niels Grewe
74d1c92669 Implement -sizeInBytesExcluding: on GSFIFO
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38809 72102866-910b-0410-8b05-ffd578937521
2015-07-16 12:57:56 +00:00
Richard Frith-MacDonald
3859653420 fix size of cache reporting
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38806 72102866-910b-0410-8b05-ffd578937521
2015-07-16 09:59:22 +00:00
Richard Frith-MacDonald
3c90224c87 Change method name as suggested by Niels
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38804 72102866-910b-0410-8b05-ffd578937521
2015-07-16 08:56:31 +00:00
Richard Frith-MacDonald
3085456143 experimental changes to memory usagfe accounting
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38800 72102866-910b-0410-8b05-ffd578937521
2015-07-15 14:48:19 +00:00
Richard Frith-MacDonald
e032b577af remove some unnecessary code
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38512 72102866-910b-0410-8b05-ffd578937521
2015-05-19 14:29:54 +00:00
Richard Frith-MacDonald
f2fec29ccc simplify GSIOThread exposure and locking
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38511 72102866-910b-0410-8b05-ffd578937521
2015-05-19 14:03:31 +00:00
Richard Frith-MacDonald
4b5fcddbc8 Expose GSIOThread, add easier customisation of FIFO, make IOThread pool a little
safer with checks for finished/cancelled thread and lock protection of count.



git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38506 72102866-910b-0410-8b05-ffd578937521
2015-05-16 10:31:08 +00:00
Niels Grewe
30a77897f3 Fix potential race condition when getting the top object without
removing it. (previously, we had a window between returning a peeked
pointer and retaining it where another thread might have been able to
pop and release the object).


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38475 72102866-910b-0410-8b05-ffd578937521
2015-05-05 11:32:38 +00:00
Niels Grewe
0f514c0ed4 Add methods to peek at the top/front item in a FIFO without removing it.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38474 72102866-910b-0410-8b05-ffd578937521
2015-05-05 07:57:34 +00:00
Niels Grewe
80276abfa1 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


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38462 72102866-910b-0410-8b05-ffd578937521
2015-04-28 13:28:01 +00:00
Richard Frith-MacDonald
c110b9139e add code to treat a FIFO as a container
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38286 72102866-910b-0410-8b05-ffd578937521
2015-01-09 15:14:53 +00:00
Wolfgang Lux
a5aaf86a79 Fix incorrect comparison introduced in last commit
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38222 72102866-910b-0410-8b05-ffd578937521
2014-12-02 11:24:51 +00:00
Riccardo Mottola
951b791420 fix capitalization
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38206 72102866-910b-0410-8b05-ffd578937521
2014-11-26 23:26:56 +00:00
Riccardo Mottola
21b32d3ed9 enable exceptions
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38205 72102866-910b-0410-8b05-ffd578937521
2014-11-26 23:14:15 +00:00
Riccardo Mottola
e88e1f553e enable exceptions
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38204 72102866-910b-0410-8b05-ffd578937521
2014-11-26 23:02:56 +00:00
Richard Frith-MacDonald
f8bd2919a3 tweaks for osx
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38198 72102866-910b-0410-8b05-ffd578937521
2014-11-23 17:30:01 +00:00
Richard Frith-MacDonald
444f701968 add missing import
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38197 72102866-910b-0410-8b05-ffd578937521
2014-11-23 17:26:37 +00:00
Richard Frith-MacDonald
709b5bdd72 use more informative exceptions
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38170 72102866-910b-0410-8b05-ffd578937521
2014-11-12 15:11:22 +00:00
Riccardo Mottola
af798e503d Import inttypes.h for pointer formatting
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38169 72102866-910b-0410-8b05-ffd578937521
2014-11-12 07:26:04 +00:00
Riccardo Mottola
4f02e79f2e Forward-declare NSRecursiveLock, not NSLock
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38168 72102866-910b-0410-8b05-ffd578937521
2014-11-12 07:23:52 +00:00
Riccardo Mottola
b42130a3c2 add strings
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38167 72102866-910b-0410-8b05-ffd578937521
2014-11-11 19:20:52 +00:00
Richard Frith-MacDonald
60dd3485ec fix missing bracket in comment
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@38156 72102866-910b-0410-8b05-ffd578937521
2014-11-04 11:36:13 +00:00
Richard Frith-MacDonald
b8120c7b7e iYavor's fix for bug #42732
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@37992 72102866-910b-0410-8b05-ffd578937521
2014-07-13 08:34:20 +00:00
Richard Frith-MacDonald
53945281d8 New release version to include GSUniqued
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@37857 72102866-910b-0410-8b05-ffd578937521
2014-05-08 07:30:53 +00:00
Richard Frith-MacDonald
000bf4a523 add GSUniqued
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@37812 72102866-910b-0410-8b05-ffd578937521
2014-04-26 09:26:59 +00:00
Niels Grewe
298f38c629 Fix calculation of the timeout for a cooperating get or put (it was multiplied by 1000 where
it should have been divided by 1000)


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@37362 72102866-910b-0410-8b05-ffd578937521
2013-11-05 10:15:00 +00:00
Richard Frith-MacDonald
30e4443c3b fixup access via isa
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@36999 72102866-910b-0410-8b05-ffd578937521
2013-08-21 10:45:19 +00:00
Richard Frith-MacDonald
62959bec70 fixes for new release
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@36997 72102866-910b-0410-8b05-ffd578937521
2013-08-21 08:47:28 +00:00
Richard Frith-MacDonald
eabad6c509 temporary fixup for nonfragile abi
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@36996 72102866-910b-0410-8b05-ffd578937521
2013-08-21 08:33:55 +00:00
Richard Frith-MacDonald
98874b40a0 64bit format tweaks
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@36995 72102866-910b-0410-8b05-ffd578937521
2013-08-21 08:06:58 +00:00
Richard Frith-MacDonald
6bedce1ca4 fix typo in comment
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@36981 72102866-910b-0410-8b05-ffd578937521
2013-08-16 13:17:35 +00:00
Richard Frith-MacDonald
7dcd2bd08b minor bugfix/improvments
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@36755 72102866-910b-0410-8b05-ffd578937521
2013-06-25 09:39:19 +00:00
Richard Frith-MacDonald
e6f2dceb77 allow default size for shared pool
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@36754 72102866-910b-0410-8b05-ffd578937521
2013-06-25 09:29:54 +00:00
Richard Frith-MacDonald
dfadd70968 Support shrinking number of threads in pool.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@36753 72102866-910b-0410-8b05-ffd578937521
2013-06-25 09:22:49 +00:00
Richard Frith-MacDonald
4782d743ed fix segfault when no threads are configuredb ... should use main thread.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@36752 72102866-910b-0410-8b05-ffd578937521
2013-06-25 09:05:00 +00:00
Niels Grewe
b987200256 Remove accidental commit.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@36732 72102866-910b-0410-8b05-ffd578937521
2013-06-19 05:15:34 +00:00
Niels Grewe
4f56df1445 Improve table views, fix for missing Unicode support on iOS 5.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@36730 72102866-910b-0410-8b05-ffd578937521
2013-06-19 02:37:23 +00:00
Richard Frith-MacDonald
d29eb60b77 tweak formats
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@36260 72102866-910b-0410-8b05-ffd578937521
2013-03-04 12:43:53 +00:00
Sebastian Reitenbach
cd2ae25fd1 * GSThreadPool.h, GSCache.m
shutup clang warnings
OK rfm



git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@36078 72102866-910b-0410-8b05-ffd578937521
2013-02-07 14:11:25 +00:00
Richard Frith-MacDonald
dd0b17ac3c thread exist safety fixup
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@36058 72102866-910b-0410-8b05-ffd578937521
2013-02-03 06:37:42 +00:00
Richard Frith-MacDonald
bd30ad1517 fix missing method in header and stupid deallocation error.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@35997 72102866-910b-0410-8b05-ffd578937521
2013-01-18 18:29:36 +00:00
Niels Grewe
5f028bd548 Change -initWithObjects:count: declaration to match the superclass one in gnustep-base trunk.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@34496 72102866-910b-0410-8b05-ffd578937521
2012-01-11 16:16:12 +00:00
Richard Frith-MacDonald
b003dbbf7f match stats output to description
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@34059 72102866-910b-0410-8b05-ffd578937521
2011-10-25 11:51:10 +00:00
Richard Frith-MacDonald
2ca74e1017 tweak description and fix comment
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@34058 72102866-910b-0410-8b05-ffd578937521
2011-10-25 11:35:27 +00:00
Richard Frith-MacDonald
bdd2b7ce2c improve configurability
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@34057 72102866-910b-0410-8b05-ffd578937521
2011-10-25 11:18:36 +00:00
Richard Frith-MacDonald
8a5867051c remove some unused stuff
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33807 72102866-910b-0410-8b05-ffd578937521
2011-09-02 09:33:25 +00:00
Richard Frith-MacDonald
2a19015dc9 attempt tweak to avoid clang/llvm problem
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33794 72102866-910b-0410-8b05-ffd578937521
2011-08-29 05:30:50 +00:00
Richard Frith-MacDonald
c04f6b42ca implement locking for multiple producer/consumer
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33780 72102866-910b-0410-8b05-ffd578937521
2011-08-24 11:26:01 +00:00
Richard Frith-MacDonald
cd5df0b3ba fix error in locking
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33779 72102866-910b-0410-8b05-ffd578937521
2011-08-23 12:45:04 +00:00
Richard Frith-MacDonald
3c056486d0 fix locking error
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33648 72102866-910b-0410-8b05-ffd578937521
2011-07-27 04:37:50 +00:00
Richard Frith-MacDonald
009cc73545 tiidied
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33322 72102866-910b-0410-8b05-ffd578937521
2011-06-17 09:32:56 +00:00
Richard Frith-MacDonald
f7697069ce more stats updates
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33285 72102866-910b-0410-8b05-ffd578937521
2011-06-12 11:18:41 +00:00
Richard Frith-MacDonald
f05f968d4d Add/remove multiple items in one go
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33284 72102866-910b-0410-8b05-ffd578937521
2011-06-12 08:24:37 +00:00
Richard Frith-MacDonald
7b8ee4e46b fix off by one error
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33283 72102866-910b-0410-8b05-ffd578937521
2011-06-12 07:42:22 +00:00
Richard Frith-MacDonald
3b505b67b5 tweak
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33279 72102866-910b-0410-8b05-ffd578937521
2011-06-11 14:46:27 +00:00
Richard Frith-MacDonald
769d715041 add support for stats
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33278 72102866-910b-0410-8b05-ffd578937521
2011-06-11 14:41:59 +00:00
Richard Frith-MacDonald
00214ef55c fix nil deref
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33275 72102866-910b-0410-8b05-ffd578937521
2011-06-11 07:38:07 +00:00
Richard Frith-MacDonald
71d2b71d96 Support cache refresh via delegate.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33253 72102866-910b-0410-8b05-ffd578937521
2011-06-06 08:25:49 +00:00
Richard Frith-MacDonald
dde5501c2b use condition locks to avoid polling/spinning
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33251 72102866-910b-0410-8b05-ffd578937521
2011-06-05 09:14:05 +00:00
Richard Frith-MacDonald
e1bae0aab8 threading tweaks and doc improvments
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33243 72102866-910b-0410-8b05-ffd578937521
2011-06-04 14:50:40 +00:00
Richard Frith-MacDonald
a840b7fbe7 make it clear we don't message items in FIFO
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33232 72102866-910b-0410-8b05-ffd578937521
2011-06-02 09:24:57 +00:00
Richard Frith-MacDonald
b5bd053574 Add non-blocking FIFO functions/methods
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33064 72102866-910b-0410-8b05-ffd578937521
2011-05-19 06:53:21 +00:00
Richard Frith-MacDonald
6a4b4db806 add a FIFO implementation
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@32914 72102866-910b-0410-8b05-ffd578937521
2011-04-20 10:22:24 +00:00
Richard Frith-MacDonald
d2e6aac0a6 fix interface version error
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@32499 72102866-910b-0410-8b05-ffd578937521
2011-03-08 11:15:38 +00:00
Richard Frith-MacDonald
6ed76b045c update version number for next release
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@32497 72102866-910b-0410-8b05-ffd578937521
2011-03-08 10:59:46 +00:00
Richard Frith-MacDonald
225cf05f9e hacks for old system
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@32446 72102866-910b-0410-8b05-ffd578937521
2011-03-04 08:14:22 +00:00
Riccardo Mottola
2e05fded5f fixed warnings and build on old API
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@32444 72102866-910b-0410-8b05-ffd578937521
2011-03-03 22:39:15 +00:00
Richard Frith-MacDonald
01e4d495a4 tweaks for older osx versions
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@32441 72102866-910b-0410-8b05-ffd578937521
2011-03-03 13:35:52 +00:00
Richard Frith-MacDonald
75fb416fc7 fix includes/imports
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@32439 72102866-910b-0410-8b05-ffd578937521
2011-03-03 10:51:46 +00:00
Richard Frith-MacDonald
790ee4cc55 static analyser tweaks
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@32378 72102866-910b-0410-8b05-ffd578937521
2011-02-26 15:53:44 +00:00
Richard Frith-MacDonald
3531c48adb fix cache size of GSMimeDocument
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@32331 72102866-910b-0410-8b05-ffd578937521
2011-02-23 16:56:21 +00:00
Richard Frith-MacDonald
de5b713420 remove unused headers
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@32003 72102866-910b-0410-8b05-ffd578937521
2011-02-07 15:32:44 +00:00
Richard Frith-MacDonald
49ba03b2a9 comment out obsolete include
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@32000 72102866-910b-0410-8b05-ffd578937521
2011-02-05 11:54:45 +00:00
Riccardo Mottola
a99466d7c4 add tiger project
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@31603 72102866-910b-0410-8b05-ffd578937521
2010-11-11 20:38:16 +00:00
Riccardo Mottola
ed730dcfa1 Complete includes and use typedef instead of #define
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@31602 72102866-910b-0410-8b05-ffd578937521
2010-11-11 20:32:28 +00:00
Riccardo Mottola
6c465a5ec3 use always class_getInstanceSize() on GNUSTEP for all runtimes and define a compatibility macro for old macs
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@31601 72102866-910b-0410-8b05-ffd578937521
2010-11-11 17:40:30 +00:00
Richard Frith-MacDonald
6630d3d098 try to get instance size on all systems
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@31600 72102866-910b-0410-8b05-ffd578937521
2010-11-11 17:29:35 +00:00
Riccardo Mottola
e77f41047b mac 10.4 compatibility definitions
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@31596 72102866-910b-0410-8b05-ffd578937521
2010-11-10 00:15:16 +00:00
Richard Frith-MacDonald
a0c5ad644e Add a simple thread pooling utility.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@31464 72102866-910b-0410-8b05-ffd578937521
2010-10-01 18:51:30 +00:00
Richard Frith-MacDonald
d3ea09ddac add shared pool
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@31457 72102866-910b-0410-8b05-ffd578937521
2010-10-01 10:55:47 +00:00
Richard Frith-MacDonald
e3812b5651 tweak new functions
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@31448 72102866-910b-0410-8b05-ffd578937521
2010-09-30 11:45:36 +00:00
Richard Frith-MacDonald
aae279ec91 fixed incorrect asssertion
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@31447 72102866-910b-0410-8b05-ffd578937521
2010-09-30 11:39:32 +00:00
Richard Frith-MacDonald
b6d8004050 add functions to move to start/end of list
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@31442 72102866-910b-0410-8b05-ffd578937521
2010-09-29 17:32:34 +00:00
Richard Frith-MacDonald
fa6594c545 improve linked list api
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@31440 72102866-910b-0410-8b05-ffd578937521
2010-09-29 14:04:18 +00:00
Richard Frith-MacDonald
927e7df2f7 add some diagnostics
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@31400 72102866-910b-0410-8b05-ffd578937521
2010-09-23 15:39:47 +00:00
Richard Frith-MacDonald
7c651824a0 Tools for more efficient multithrerading than NSOperation allows.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@31396 72102866-910b-0410-8b05-ffd578937521
2010-09-22 12:34:35 +00:00
Richard Frith-MacDonald
5006c5d013 trivial tweak to avoid documentation warning
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@31381 72102866-910b-0410-8b05-ffd578937521
2010-09-20 09:10:54 +00:00
Richard Frith-MacDonald
fd9b7a21d1 warn if gnustep-make not found
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@31310 72102866-910b-0410-8b05-ffd578937521
2010-09-10 13:59:11 +00:00
Richard Frith-MacDonald
f71591cd67 remove accidental commit
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@29046 72102866-910b-0410-8b05-ffd578937521
2009-11-22 10:21:45 +00:00
Richard Frith-MacDonald
235393049a Update to build on snow leopard
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@29028 72102866-910b-0410-8b05-ffd578937521
2009-11-17 20:04:11 +00:00
Richard Frith-MacDonald
d355f0dd03 get makefiles location
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@28683 72102866-910b-0410-8b05-ffd578937521
2009-09-15 09:03:26 +00:00
Richard Frith-MacDonald
af38858926 Only notify observers once per second.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@28670 72102866-910b-0410-8b05-ffd578937521
2009-09-14 16:06:07 +00:00
Richard Frith-MacDonald
6307bc5da8 fix uninitialised variable
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@27283 72102866-910b-0410-8b05-ffd578937521
2008-12-12 10:37:33 +00:00
Richard Frith-MacDonald
9a9c15a904 tweak timestamps in notifications to be on minute boundary.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@26587 72102866-910b-0410-8b05-ffd578937521
2008-05-30 11:05:28 +00:00
Richard Frith-MacDonald
31322e8116 Add support for notifications of stats at one minute intervals
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@26586 72102866-910b-0410-8b05-ffd578937521
2008-05-30 10:58:42 +00:00
Richard Frith-MacDonald
c7326090a7 Experimental changes for thread safety and to make it easier for delegates
to asynchronously update the cache.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@26116 72102866-910b-0410-8b05-ffd578937521
2008-02-21 15:18:55 +00:00
Richard Frith-MacDonald
70251691ab Use new NEEDS_GUI for entire project rather than individual instructions for each component.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@26111 72102866-910b-0410-8b05-ffd578937521
2008-02-20 12:05:24 +00:00
Richard Frith-MacDonald
bd0cf4d192 Don't link with gui if we can avoid it.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@26101 72102866-910b-0410-8b05-ffd578937521
2008-02-19 11:26:00 +00:00
Richard Frith-MacDonald
45016e0e0b Cache keys can be almost any type of object.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@26014 72102866-910b-0410-8b05-ffd578937521
2008-01-31 12:40:34 +00:00
Richard Frith-MacDonald
375ca8298e make new release version
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@25705 72102866-910b-0410-8b05-ffd578937521
2007-12-08 06:36:54 +00:00
Richard Frith-MacDonald
1f6d57315c Output all the stats info we have, not just the current cycle
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@25703 72102866-910b-0410-8b05-ffd578937521
2007-12-08 06:26:21 +00:00
Richard Frith-MacDonald
dd83044cad Update to LGPL3
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@25488 72102866-910b-0410-8b05-ffd578937521
2007-09-14 13:00:42 +00:00
Richard Frith-MacDonald
ca1fee320f Make sure tick is current before calling -newSecond:
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@25207 72102866-910b-0410-8b05-ffd578937521
2007-05-30 09:29:17 +00:00
Richard Frith-MacDonald
c414177897 Replace includes accidentally deleted
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24957 72102866-910b-0410-8b05-ffd578937521
2007-04-01 07:01:55 +00:00
Richard Frith-MacDonald
baf1c438bc Cache size improvements.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24956 72102866-910b-0410-8b05-ffd578937521
2007-04-01 06:59:58 +00:00
Richard Frith-MacDonald
c934b6b188 avoid macos-x compiler warnings
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24955 72102866-910b-0410-8b05-ffd578937521
2007-04-01 05:20:38 +00:00
Richard Frith-MacDonald
e1f6dedffa optimise when setting nil object in cache
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24931 72102866-910b-0410-8b05-ffd578937521
2007-03-26 11:15:51 +00:00
Richard Frith-MacDonald
03d011a637 Improve fix for runtime portability
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24709 72102866-910b-0410-8b05-ffd578937521
2007-02-27 15:19:35 +00:00
Richard Frith-MacDonald
3d16a6089d Fix missing include for macos
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24689 72102866-910b-0410-8b05-ffd578937521
2007-02-24 11:11:29 +00:00
Richard Frith-MacDonald
32a7aef7e9 Avoid runtime specific code
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24688 72102866-910b-0410-8b05-ffd578937521
2007-02-24 11:06:42 +00:00
Richard Frith-MacDonald
4cb3f77efb use import rather than include for macos
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24687 72102866-910b-0410-8b05-ffd578937521
2007-02-24 11:04:53 +00:00
Richard Frith-MacDonald
5998934e88 bump version to 0.3.0 for next unstable/trunk release
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24294 72102866-910b-0410-8b05-ffd578937521
2006-12-30 06:47:01 +00:00
Richard Frith-MacDonald
9699a7c832 New subminor for last bugfix
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24282 72102866-910b-0410-8b05-ffd578937521
2006-12-28 11:05:55 +00:00
Matt Rice
4de059c229 * GSMutableSkipArray.m: Fix bug when deallocating an empty list.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24076 72102866-910b-0410-8b05-ffd578937521
2006-11-12 07:30:09 +00:00
Richard Frith-MacDonald
6eabd32ede Add method to match add:duration:
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24074 72102866-910b-0410-8b05-ffd578937521
2006-11-11 07:24:18 +00:00
Richard Frith-MacDonald
f7a943baea Add new duration recording method
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24073 72102866-910b-0410-8b05-ffd578937521
2006-11-11 07:14:30 +00:00
Richard Frith-MacDonald
fc2ce5e06c Improve documentation
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24066 72102866-910b-0410-8b05-ffd578937521
2006-11-10 12:59:27 +00:00
Richard Frith-MacDonald
7710087e5a Fix stupid error ... need sleep.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24064 72102866-910b-0410-8b05-ffd578937521
2006-11-09 13:24:49 +00:00
Richard Frith-MacDonald
ce4f386047 Fix dumb bug in average duration description
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24062 72102866-910b-0410-8b05-ffd578937521
2006-11-09 13:22:16 +00:00
Richard Frith-MacDonald
7af886523d When describing multiple instances, order descriptions alphabetically by name.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24061 72102866-910b-0410-8b05-ffd578937521
2006-11-09 13:18:12 +00:00
Richard Frith-MacDonald
3d89c08e6d Tweak svn settings
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24059 72102866-910b-0410-8b05-ffd578937521
2006-11-09 09:47:46 +00:00
Richard Frith-MacDonald
976b5a7b48 Set SVN base URL
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24057 72102866-910b-0410-8b05-ffd578937521
2006-11-09 09:39:23 +00:00
Richard Frith-MacDonald
1771a1181f Bump subminor version ... doesn't break backward compatibility
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24056 72102866-910b-0410-8b05-ffd578937521
2006-11-09 09:33:38 +00:00
Richard Frith-MacDonald
019efd8558 Add ability to keep running total rather than stats broken up over time
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@24055 72102866-910b-0410-8b05-ffd578937521
2006-11-09 09:22:11 +00:00
Richard Frith-MacDonald
afceff5b21 Hide skip list better.
Bump version number for next release


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@23759 72102866-910b-0410-8b05-ffd578937521
2006-10-04 15:39:58 +00:00
Richard Frith-MacDonald
3204b60c7d Add new header for skip lists
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@23635 72102866-910b-0410-8b05-ffd578937521
2006-09-27 10:48:30 +00:00
Richard Frith-MacDonald
bfe4fb8df2 Some tidyups
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@23634 72102866-910b-0410-8b05-ffd578937521
2006-09-27 10:46:47 +00:00
Matt Rice
05cebfc22b * GSSkipMutableArray.[hm]: New NSMutableArray subclass.
* GSIndexedSkipList.[hm]: Underlying C implementation.



git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@23633 72102866-910b-0410-8b05-ffd578937521
2006-09-27 09:23:25 +00:00
Richard Frith-MacDonald
dc6f3dda03 small thread safety fix
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@23450 72102866-910b-0410-8b05-ffd578937521
2006-09-10 13:54:58 +00:00
Richard Frith-MacDonald
4bb7b9e189 Add some headers
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@23185 72102866-910b-0410-8b05-ffd578937521
2006-07-25 12:15:10 +00:00
Richard Frith-MacDonald
7ff737d852 New caching convenience method
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@23039 72102866-910b-0410-8b05-ffd578937521
2006-06-07 14:41:18 +00:00
Richard Frith-MacDonald
3178a829a5 remove svn:eol-style property
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@22546 72102866-910b-0410-8b05-ffd578937521
2006-02-22 11:46:49 +00:00
30 changed files with 7841 additions and 540 deletions

166
COPYING.LIB Normal file
View file

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

383
ChangeLog
View file

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

Binary file not shown.

View file

@ -1,55 +1,80 @@
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.1.0
CVS_MODULE_NAME = gnustep/dev-libs/Performance
CVS_TAG_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_INTERFACE_VERSION=0.1
Performance_OBJC_FILES += \
GSCache.m \
GSFIFO.m \
GSIOThreadPool.m \
GSLinkedList.m \
GSThreadPool.m \
GSThroughput.m \
GSTicker.m \
GSIndexedSkipList.m \
GSSkipMutableArray.m \
GSUniqued.m \
Performance_HEADER_FILES += \
GSCache.h \
GSFIFO.h \
GSIOThreadPool.h \
GSLinkedList.h \
GSThreadPool.h \
GSThroughput.h \
GSTicker.h \
GSSkipMutableArray.h \
GSUniqued.h \
Performance_AGSDOC_FILES += \
GSCache.h \
GSFIFO.h \
GSIOThreadPool.h \
GSLinkedList.h \
GSThreadPool.h \
GSThroughput.h \
GSTicker.h
GSTicker.h \
GSSkipMutableArray.h \
GSUniqued.h \
# Optional Java wrappers for the library
JAVA_WRAPPER_NAME = Performance
#
# Assume that the use of the gnu runtime means we have the gnustep
# base library and can use its extensions to build Performance stuff.
#
ifeq ($(OBJC_RUNTIME_LIB),gnu)
APPLE=0
else
APPLE=1
endif
ifeq ($(APPLE),1)
ADDITIONAL_OBJC_LIBS += -lgnustep-baseadd
Performance_LIBRARIES_DEPEND_UPON = -lgnustep-baseadd
endif
Performance_HEADER_FILES_INSTALL_DIR = Performance
-include GNUmakefile.preamble

184
GNUstep.h Normal file
View file

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

165
GSCache.h
View file

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

672
GSCache.m
View file

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

421
GSFIFO.h Normal file
View file

@ -0,0 +1,421 @@
#if !defined(INCLUDED_GSFIFO)
#define INCLUDED_GSFIFO 1
/**
Copyright (C) 2011 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: April 2011
This file is part of the Performance Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#import <Foundation/NSObject.h>
#import <Foundation/NSDate.h>
#if !defined (GNUSTEP)
#import "GNUstep.h"
#endif
@class NSArray;
@class NSCondition;
@class NSNumber;
@class NSString;
@class NSThread;
/** GSFIFO manages a first-in-first-out queue of items.<br />
* Items in the queue are <em>NOT</em> retained objects ... memory management
* is not the job of this class and it is not intended to be treated
* as a 'collection', rather its role is intended to be that of an
* inter-thread coordination mechanism.<br />
* Instances of the GSFIFO class are intended to support the producer-consumer
* model of processing. The ideal example is that of a production line,
* where you have a stream of items to be processed and while that processing
* can be broken down into separate stages, they must be done in a particular
* order. The FIFO is used as the link betwen those stages, ensuring that
* the required ordering is maintained even when separate threads handle
* each stage.<br />
* Where there is a single producer and a single consumer thread, a fast
* lock-free algorthm is used to get/pu items from the FIFO.<br />
* To minimise the overheads of using the FIFO, we provide inline functions
* to support the addition of items in the single producer thread case and to
* support the removal of items in the single consumer thread case. When
* operating that way, the overhead of using the FIFO is only a few CPU cycles
* and it becomes reasonable to split sequentional processing into a long
* series of small operations each handled by a separate thread (making
* effective use of a multi-cpu machine).<br />
* The FIFO may also be useful where you don't have a strictly sequential
* process to manage, but some parts need to be sequential ... in these
* cases it may make sense to have multiple consumers and/or producers.
* In these cases, some locking is required and the use of the inline
* functions is not allowed (you must call the -get and -put: methods.<br />
* It is recommended that you create FIFOs using the -initWithName: method
* so that you can easily use the NSUserDefaults system to adjust their
* configurations to tests/tweak performance.<br />
* While a FIFO fundamentally works on abstract items without memory
* management, the API provides methods for handling NSObject values
* threating the FIFO as a container which retains the objects until
* they are removed from the FIFO.
*/
@interface GSFIFO : NSObject
{
@public
/* While the following instance variables are nominally public, they are in
* fact only intended to be used by the provided inline functions ... you
* should not access them directly in your own code.
*/
volatile uint64_t _head;
volatile uint64_t _tail;
uint64_t _getTryFailure;
uint64_t _getTrySuccess;
uint64_t _putTryFailure;
uint64_t _putTrySuccess;
void **_items;
uint32_t _capacity;
@private
uint32_t boundsCount;
uint16_t granularity;
uint16_t timeout;
uint64_t fullCount; // Total waits for full FIFO
uint64_t emptyCount; // Total waits for empty FIFO
NSCondition *condition;
NSString *name;
NSTimeInterval getWaitTotal; // Total time waiting for gets
NSTimeInterval putWaitTotal; // Total time waiting for puts
NSTimeInterval *waitBoundaries; // Stats boundaries
uint64_t *getWaitCounts; // Waits for gets by time
uint64_t *putWaitCounts; // Waits for puts by time
NSThread *getThread; // Single consumer thread
NSThread *putThread; // Single producer thread
}
/** Return statistics for all current GSFIFO instances.<br />
* Statistics for FIFOs which are configued to be lock-free are empty
* (listing the name only) except where we can safely obtain get or put
* stats because the FIFOs consumer/producer thread is the same as the
* current thread.
*/
+ (NSString*) stats;
/** Returns the approximate number of items in the FIFO.
*/
- (NSUInteger) count;
/** Reads up to count items from the FIFO into buf.
* If block is YES, this blocks if necessary until at least one item
* is available, and raises an exception if the FIFO is configured
* with a timeout and it is exceeded.<br />
* Returns the number of items actually read.
*/
- (unsigned) get: (void**)buf count: (unsigned)count shouldBlock: (BOOL)block;
/**
* Reads up to count items from the FIFO into the buf. If blocking is requested
* and a before date is specified, the operation blocks until the specified time
* and returns 0 if it could not read any items. The timeout configured for the
* FIFO still takes precedence.
*/
- (unsigned) get: (void**)buf
count: (unsigned)count
shouldBlock: (BOOL)block
before: (NSDate*)date;
/** Reads up to count objects from the FIFO (which must contain objects
* or nil items) into buf and autoreleases them.<br />
* If block is YES, this blocks if necessary until at least one object
* is available, and raises an exception if the FIFO is configured
* with a timeout and it is exceeded.<br />
* Returns the number of object actually read.
*/
- (unsigned) getObjects: (NSObject**)buf
count: (unsigned)count
shouldBlock: (BOOL)block;
/**
* Reads up to count autoreleased objects from the FIFO into the buf.
* If blocking
* is requested and a before date is specified, the operation blocks until the
* specified time and returns 0 if it could not read any items. The timeout
* configured for the FIFO still takes precedence.
*/
- (unsigned) getObjects: (NSObject**)buf
count: (unsigned)count
shouldBlock: (BOOL)block
before: (NSDate*)date;
/** Gets the next item from the FIFO, blocking if necessary until an
* item is available. Raises an exception if the FIFO is configured
* with a timeout and it is exceeded.<br />
* Implemented using -get:count:shouldBlock:
*/
- (void*) get;
/** Gets the next object from the FIFO (which must contain objects or
* nil items), blocking if necessary until an object is available.
* Autoreleases the object before returning it.<br />
* Raises an exception if the FIFO is configured
* with a timeout and it is exceeded.<br />
* Implemented using -get:count:shouldBlock:
*/
- (NSObject*) getObject;
/** Gets the next item from the FIFO, blocking if necessary until an
* item is available. Raises an exception if the FIFO is configured
* with a timeout and it is exceeded.<br />
* The returned item/object, is not autoreleased.<br />
* Implemented using -get:count:shouldBlock:
*/
- (NSObject*) getObjectRetained NS_RETURNS_RETAINED;
/** <init/>
* Initialises the receiver with the specified capacity (buffer size).<br />
* The capacity must lie in the range from one to a hundred million, otherwise
* the receiver is deallocated and this method returns nil.<br />
* If the granularity value is non-zero, it is treated as the maximum time
* in milliseconds for which a -get or -put: operation will pause between
* successive attempts.<br />
* If the timeout value is non-zero, it is treated as the total time in
* milliseconds for which a -get or -put: operation may block, and a
* longer delay will cause those methods to raise an exception.<br />
* If the multiProducer or multiConsumer flag is YES, the FIFO is
* configured to support multiple producer/consumer threads using locking.<br />
* The boundaries array is an ordered list of NSNumber objects containing
* time intervals found boundaries of bands into which to categorise wait
* time stats. Any wait whose duration is less than the interval specified
* in the Nth element is counted in the stat's for the Nth band.
* If this is nil, a default set of boundaries is used. If it is an empty
* array then no time based stats are recorded.<br />
* The name string is a unique identifier for the receiver and is used when
* printing diagnostics and statistics. If an instance with the same name
* already exists, the receiveris deallocated and an exception is raised.
*/
- (id) initWithCapacity: (uint32_t)c
granularity: (uint16_t)g
timeout: (uint16_t)t
multiProducer: (BOOL)mp
multiConsumer: (BOOL)mc
boundaries: (NSArray*)a
name: (NSString*)n;
/** Initialises the receiver as a multi-producer, multi-consumer FIFO with
* no timeout and with default stats gathering enabled.<br />
* However, these values (including the supplied capacity) may be overridden
* as specified in -initWithName:
*/
- (id) initWithCapacity: (uint32_t)c
name: (NSString*)n;
/** Initialises the receiver using the specified name and obtaining other
* details from the NSUserDefaults system using defaults keys where 'NNN'
* is the supplied name.<br />
* The GSFIFOCapacityNNN default specifies the capacity for the FIFO, and
* if missing a capacity of 1000 is assumed.<br />
* The GSFIFOGranularityNNN integer is zero by default.<br />
* The GSFIFOTimeoutNNN integer is zero by default.<br />
* The GSFIFOSingleConsumerNNN boolean is NO by default.<br />
* The GSFIFOSingleProducerNNN boolean is NO by default.<br />
* The GSFIFOBoundariesNNN array is missing by default.<br />
* If no default is found for the specific named FIFO, the default set
* for a FIFO with an empty name is used.
*/
- (id) initWithName: (NSString*)n;
/** Writes exactly count items from buf into the FIFO, blocking if
* necessary until there is space for the entire write.<br />
* Raises an exception if the FIFO is configured with a timeout and it is
* exceeded, or if the count is greater than the FIFO size, or if the
* FIFO was not configured for multi producer or multi consumer use.<br />
* If rtn is YES, the method treats the buffer as containing objects
* which are retained as they are added.
*/
- (void) putAll: (void**)buf count: (unsigned)count shouldRetain: (BOOL)rtn;
/** Writes up to count items from buf into the FIFO.
* If block is YES, this blocks if necessary until at least one item
* can be written, and raises an exception if the FIFO is configured
* with a timeout and it is exceeded.<br />
* Returns the number of items actually written.
*/
- (unsigned) put: (void**)buf count: (unsigned)count shouldBlock: (BOOL)block;
/** Writes up to count objects from buf into the FIFO, retaining each.<br />
* If block is YES, this blocks if necessary until at least one object
* can be written, and raises an exception if the FIFO is configured
* with a timeout and it is exceeded.<br />
* Returns the number of objects actually written.
*/
- (unsigned) putObjects: (NSObject**)buf
count: (unsigned)count
shouldBlock: (BOOL)block;
/** Adds an item to the FIFO, blocking if necessary until there is
* space in the buffer. Raises an exception if the FIFO is configured
* with a timeout and it is exceeded.br />
* The item may be an object (which is not retained when added) or a
* pointer to memory. The ownership of the item memory should be
* transferred to the FIFO.<br />
* Implemented using -put:count:shouldBlock:
*/
- (void) put: (void*)item;
/** Adds an object to the FIFO (retaining the object), blocking if
* necessary until there is space in the buffer.<br />
* Raises an exception if the FIFO is configured
* with a timeout and it is exceeded.br />
* Implemented using -put:count:shouldBlock:
*/
- (void) putObject: (NSObject*)item;
/** Adds an object to the FIFO, blocking if necessary until there is
* space in the buffer. Raises an exception if the FIFO is configured
* with a timeout and it is exceeded.br />
* The object is not retained when added, so ownership of the memory
* should be transferred to the FIFO.<br />
* Implemented using -put:count:shouldBlock:
*/
- (void) putObjectConsumed: (NSObject*) NS_CONSUMED item;
/** Return any available statistics for the receiver.<br />
*/
- (NSString*) stats;
/** Return statistics on get operations for the receiver.<br />
* NB. If the recever is not configured for multiple consumers,
* this method may only be called from the single consumer thread.
*/
- (NSString*) statsGet;
/** Return statistics on put operations for the receiver.<br />
* NB. If the recever is not configured for multiple producers,
* this method may only be called from the single producer thread.
*/
- (NSString*) statsPut;
/** Checks the FIFO and returns the first available item or NULL if the
* FIFO is empty.<br />
* Implemented using -get:count:shouldBlock:
*/
- (void*) tryGet;
/** Checks the FIFO and returns the first available object (autoreleased)
* or nil if the FIFO is empty (or contains a nil object).<br />
* Implemented using -get:count:shouldBlock:
*/
- (NSObject*) tryGetObject;
/**
* Checks the FIFO and returns a reference to the first available item
* or NULL if the FIFO is empty. <br />Calling this method does
* <em>not</em> remove the item from the queue.
*/
- (void*) peek;
/**
* Checks the FIFO and returns the an autoreleased reference to the first
* available object, or nil if the FIFO is empty (or contains a nil object).
* <br /> Calling this method does <em>not</em> remove the object from the
* queue.
*/
- (NSObject*) peekObject;
/** Attempts to put an object (or nil) into the FIFO, returning YES
* on success or NO if the FIFO is full.<br />
* Implemented using -put:count:shouldBlock:
*/
- (BOOL) tryPut: (void*)item;
/** Attempts to retain an object while putting it into the FIFO,
* returning YES on success or NO if the FIFO is full.<br />
* Implemented using -put:count:shouldBlock:
*/
- (BOOL) tryPutObject: (NSObject*)item;
@end
/** Function to efficiently get an item from a fast FIFO.<br />
* Returns NULL if the FIFO is empty.<br />
* Warning ... only for use if the FIFO is NOT configured for multiple
* consumers.
*/
static inline void*
GSGetFastNonBlockingFIFO(GSFIFO *receiver)
{
if (receiver->_head > receiver->_tail)
{
void *item;
item = receiver->_items[receiver->_tail % receiver->_capacity];
receiver->_tail++;
receiver->_getTrySuccess++;
return item;
}
receiver->_getTryFailure++;
return NULL;
}
/** Function to efficiently get an item from a fast FIFO, blocking if
* necessary until an item is available or the timeout occurs.<br />
* Warning ... only for use if the FIFO is NOT configured for multiple
* consumers.
*/
static inline void*
GSGetFastFIFO(GSFIFO *receiver)
{
void *item = GSGetFastNonBlockingFIFO(receiver);
if (0 == item)
{
item = [receiver get];
}
return item;
}
/** Function to efficiently put an item to a fast FIFO.<br />
* Returns YES on success, NO on failure (FIFO is full).<br />
* Warning ... only for use if the FIFO is NOT configured for multiple
* producers.
*/
static inline BOOL
GSPutFastNonBlockingFIFO(GSFIFO *receiver, void *item)
{
if (receiver->_head - receiver->_tail < receiver->_capacity)
{
receiver->_items[receiver->_head % receiver->_capacity] = item;
receiver->_head++;
receiver->_putTrySuccess++;
return YES;
}
receiver->_putTryFailure++;
return NO;
}
/** Function to efficiently put an item to a fast FIFO, blocking if
* necessary until there is space in the FIFO or until the timeout
* occurs.<br />
* Warning ... only for use if the FIFO is NOT configured for multiple
* producers.
*/
static inline void
GSPutFastFIFO(GSFIFO *receiver, void *item)
{
if (NO == GSPutFastNonBlockingFIFO(receiver, item))
{
[receiver put: item];
}
}
#endif

1089
GSFIFO.m Normal file

File diff suppressed because it is too large Load diff

152
GSIOThreadPool.h Normal file
View file

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

426
GSIOThreadPool.m Normal file
View file

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

82
GSIndexedSkipList.h Normal file
View file

@ -0,0 +1,82 @@
/**
Copyright (C) 2006 Free Software Foundation, Inc.
Written by: Matt Rice <ratmice@yahoo.com>
Date: 2006
This file is part of the Performance Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#import <Foundation/NSZone.h>
#define GSISLMaxNumberOfLevels 16
#define GSISLMaxLevel 15
/*
* attempt at caching the previously looked up index
* to reduce the search time required when wantedIndex > previousIndex
* this didn't seem to provide any benefit, actually negatively impacting
* performance though it was never thoroughly tested it
*/
#define GSISL_CACHE_OPT 0
typedef id GSISLValueType;
typedef struct GSISLNode_t *GSISLNode;
extern GSISLNode GSISLNil;
struct GSISLForward_t
{
unsigned delta;
GSISLNode next;
};
struct GSISLNode_t
{
GSISLValueType value;
struct GSISLForward_t forward[1];
};
typedef struct GSIndexedSkipList
{
int level; /* Maximum level of the list
(1 more than the number of levels in the list) */
GSISLNode header; /* pointer to header */
unsigned count;
NSZone *zone;
#if GSISL_CACHE_OPT
unsigned indexCache[GSISLMaxNumberOfLevels];
GSISLNode nodeCache[GSISLMaxNumberOfLevels];
#endif
} * GSISList;
void GSISLInitialize();
void GSISLFreeList(GSISList l);
GSISList GSISLInitList(NSZone *zone);
void GSISLInsertItemAtIndex(GSISList l,
GSISLValueType value,
unsigned index);
GSISLValueType GSISLItemAtIndex(GSISList l, unsigned index);
GSISLValueType GSISLRemoveItemAtIndex(GSISList l,
unsigned index);
GSISLValueType GSISLReplaceItemAtIndex(GSISList l,
GSISLValueType newVal,
unsigned index);

371
GSIndexedSkipList.m Normal file
View file

@ -0,0 +1,371 @@
/**
Copyright (C) 2006 Free Software Foundation, Inc.
Written by: Matt Rice <ratmice@yahoo.com>
Date: 2006
This file is part of the Performance Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#import "GSIndexedSkipList.h"
/* This no longer seems to be needed/correct
#if defined(__MINGW32__)
#include <cstdlib.h> // declares rand()
#endif
*/
#define PrettyErr(x) do { fprintf(stderr, "%s:%i: %s\n",__FILE__, __LINE__, x); exit(EXIT_FAILURE); } while (0)
GSISLNode GSISLNil;
GSISLNode GSISLNewNodeOfLevel(int l, NSZone *zone)
{
GSISLNode ret = (GSISLNode) NSZoneMalloc(zone, sizeof(struct GSISLNode_t)
+ ((l) * sizeof(struct GSISLForward_t)));
if (ret == NULL)
{
PrettyErr(strerror(errno));
}
do
{
ret->forward[l].delta = 0;
} while (--l >= 0);
return ret;
}
void GSISLInitialize()
{
if (GSISLNil != NULL) return;
GSISLNil = GSISLNewNodeOfLevel(0, NSDefaultMallocZone());
GSISLNil->forward[0].delta = UINT_MAX;
GSISLNil->value = nil;
GSISLNil->forward[0].next = NULL;
}
GSISList GSISLInitList(NSZone *zone)
{
GSISList l;
int i;
l = (GSISList)NSZoneMalloc(zone, sizeof(struct GSIndexedSkipList));
if (l == NULL)
{
PrettyErr(strerror(errno));
}
l->zone = zone;
l->level = 0;
l->count = 0;
l->header = GSISLNewNodeOfLevel(GSISLMaxNumberOfLevels, l->zone);
l->header->value = nil;
for (i=0; i < GSISLMaxNumberOfLevels; i++)
{
l->header->forward[0].delta = 0;
l->header->forward[0].next = GSISLNil;
#if GSISL_CACHE_OPT
l->indexCache[i] = 0;
l->nodeCache[i] = l->header;
#endif
}
return(l);
}
void GSISLFreeList(GSISList l)
{
GSISLNode p,q;
p = l->header;
do
{
q = p->forward[0].next;
NSZoneFree(l->zone,p);
p = q;
} while (p != GSISLNil);
NSZoneFree(l->zone, l);
};
int GSISLRandomLevel()
{
int level = 0;
static int p = RAND_MAX / 4;
while (rand() < p && level < GSISLMaxLevel)
{
level++;
}
return level;
}
void GSISLInsertItemAtIndex(GSISList l, GSISLValueType value,
unsigned index)
{
int k, i;
GSISLNode update[GSISLMaxNumberOfLevels];
unsigned updateIndexes[GSISLMaxNumberOfLevels];
GSISLNode p,q;
unsigned depth;
depth = 0;
k = l->level;
#if GSISL_CACHE_OPT
if (l->indexCache[k] < index)
{
p = l->nodeCache[k];
depth = l->indexCache[k];
}
else
{
p = l->header;
}
#else
p = l->header;
#endif
do
{
while (q = p->forward[k].next,
q != GSISLNil && depth + p->forward[k].delta < index + 1)
{
depth += p->forward[k].delta;
p = q;
}
updateIndexes[k] = depth;
update[k] = p;
} while(--k >= 0);
k = GSISLRandomLevel();
q = GSISLNewNodeOfLevel(k, l->zone);
if (k > l->level)
{
/* we are creating a new level that looks like
* header ---> new node ---> tail
*/
k = l->level;
l->level++;
#if GSISL_CACHE_OPT
l->nodeCache[l->level] = l->header;
l->indexCache[l->level] = 0;
#endif
l->header->forward[l->level].delta = index + 1;
l->header->forward[l->level].next = q;
q->forward[l->level].delta = 0;
q->forward[l->level].next = GSISLNil;
}
else
{
/* if there are higher nodes than this nodes level.
increment the deltas in the update, as we are inserting
a node inbetween their starting point and their ending
*/
for (i = k + 1; i <= l->level; i++)
{
if (update[i]->forward[i].delta != 0)
update[i]->forward[i].delta++;
#if GSISL_CACHE_OPT
l->nodeCache[i] = update[i];
l->indexCache[i] = updateIndexes[i];
#endif
}
}
q->value = value;
do
{
/* update from the nodes highest level down to level 0
* on all the levels already existing in the list
*/
p = update[k];
if (p->forward[k].delta)
q->forward[k].delta = updateIndexes[k] + p->forward[k].delta - depth;
p->forward[k].delta = depth + 1 - updateIndexes[k];
q->forward[k].next = p->forward[k].next;
p->forward[k].next = q;
#if GSISL_CACHE_OPT
l->indexCache[k] = updateIndexes[k];
l->nodeCache[k] = update[k];
#endif
} while(--k >= 0);
l->count++;
}
GSISLValueType GSISLRemoveItemAtIndex(GSISList l, unsigned index)
{
int k,m;
GSISLNode update[GSISLMaxNumberOfLevels];
unsigned updateIndexes[GSISLMaxNumberOfLevels];
GSISLNode p,q;
unsigned depth = 0;
GSISLValueType ret;
k = m = l->level;
#if GSISL_CACHE_OPT
if (l->indexCache[k] < index)
{
p = l->nodeCache[k];
depth = l->indexCache[k];
}
else
{
p = l->header;
}
#else
p = l->header;
#endif
do
{
while (q = p->forward[k].next,
q != GSISLNil && depth + p->forward[k].delta < index + 1)
{
depth += p->forward[k].delta;
p = q;
}
update[k] = p;
updateIndexes[k] = depth;
} while(--k >= 0);
for (k = 0; k <= m; k++)
{
p = update[k];
#if GSISL_CACHE_OPT
l->indexCache[k] = updateIndexes[k];
l->nodeCache[k] = update[k];
#endif
if (p->forward[k].next == q)
{
p->forward[k].delta
= (q->forward[k].next == GSISLNil) ? 0
: p->forward[k].delta + q->forward[k].delta - 1;
p->forward[k].next = q->forward[k].next;
}
else if (p->forward[k].next != GSISLNil)
{
p->forward[k].delta--;
}
else
{
p->forward[k].delta = 0;
}
}
ret = q->value;
NSZoneFree(l->zone,q);
/* if header points to nil, decrement the list level */
while (l->header->forward[m].next == GSISLNil && m > 0 )
{
l->header->forward[m].delta = 0;
m--;
}
l->level = m;
l->count--;
return ret;
}
GSISLValueType GSISLItemAtIndex(GSISList l, unsigned index)
{
int k;
unsigned depth = 0;
GSISLNode p,q;
k = l->level;
#if GSISL_CACHE_OPT
if (l->indexCache[k] < index)
{
p = l->nodeCache[k];
depth = l->indexCache[k];
}
else
{
p = l->header;
}
#else
p = l->header;
#endif
do
{
while (q = p->forward[k].next,
q != GSISLNil && depth + p->forward[k].delta < index + 1)
{
depth += p->forward[k].delta;
p = q;
}
#if GSISL_CACHE_OPT
l->nodeCache[k] = p;
l->indexCache[k] = depth;
#endif
} while(--k >= 0);
return(q->value);
}
GSISLValueType
GSISLReplaceItemAtIndex(GSISList l, GSISLValueType newVal, unsigned index)
{
int k;
unsigned depth = 0;
GSISLNode p,q;
GSISLValueType ret;
k = l->level;
#if GSISL_CACHE_OPT
if (l->indexCache[k] < index)
{
p = l->nodeCache[k];
depth = l->indexCache[k];
}
else
{
p = l->header;
}
#else
p = l->header;
#endif
do
{
while (q = p->forward[k].next,
q != GSISLNil && depth + p->forward[k].delta < index + 1)
{
depth += p->forward[k].delta;
p = q;
}
#if GSISL_CACHE_OPT
l->indexCache[k] = depth;
l->nodeCache[k] = p;
#endif
} while(--k >= 0);
ret = q->value;
q->value = newVal;
return ret;
}

440
GSLinkedList.h Normal file
View file

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

655
GSLinkedList.m Normal file
View file

@ -0,0 +1,655 @@
/**
Copyright (C) 2010 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: September 2010
This file is part of the Performance Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#import <Foundation/NSString.h>
#import <Foundation/NSException.h>
#import "GSLinkedList.h"
#if !defined (GNUSTEP)
#import "GNUstep.h"
#endif
@implementation GSListLink
- (void) dealloc
{
NSAssert(nil == owner, NSInternalInconsistencyException);
[item release];
DEALLOC;
}
- (id) initCircular
{
if ((self = [super init]) != nil)
{
next = previous = self;
}
return self;
}
- (NSObject*) item
{
return item;
}
- (GSListLink*) next
{
if (next == self)
{
return nil;
}
return next;
}
- (GSLinkedList*) owner
{
return owner;
}
- (GSListLink*) previous
{
if (previous == self)
{
return nil;
}
return previous;
}
- (void) setItem: (NSObject*)anItem
{
id o = item;
item = [anItem retain];
RELEASE(o);
}
@end
@implementation GSLinkedList
- (void) append: (GSListLink*)link
{
if (nil == link)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] nil argument",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
if (self == link->owner)
{
if (link != tail)
{
GSLinkedListRemove(link, self);
GSLinkedListInsertAfter(link, self, tail);
}
}
else
{
if (nil != link->owner || nil != link->next || nil != link->previous)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] other link is still in a list",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
GSLinkedListInsertAfter(link, self, tail);
RETAIN(link);
}
}
- (NSUInteger) count
{
return count;
}
- (void) dealloc
{
[self empty];
DEALLOC;
}
- (void) empty
{
GSListLink *link;
while (nil != (link = head))
{
head = link->next;
link->owner = nil;
link->next = link->previous = nil;
RELEASE(link);
}
tail = nil;
count = 0;
}
- (GSListLink*) findEqual: (NSObject*)object
from: (GSListLink*)start
back: (BOOL)aFlag
{
if (nil != start && start->owner != self)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] start link is not in this list",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
return GSLinkedListFindEqual(object, self, start, aFlag);
}
- (GSListLink*) findIdentical: (NSObject*)object
from: (GSListLink*)start
back: (BOOL)aFlag
{
if (nil != start && start->owner != self)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] start link is not in this list",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
return GSLinkedListFindIdentical(object, self, start, aFlag);
}
- (GSListLink*) head
{
return head;
}
- (void) insert: (GSListLink*)link after: (GSListLink*)at
{
if (nil == link)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] nil link argument",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
if (nil == at)
{
at = tail;
}
if (at->owner != self)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] 'at' link is not in this list",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
if (at == link)
{
return;
}
if (link->owner == self)
{
GSLinkedListRemove(link, self);
GSLinkedListInsertAfter(link, self, at);
}
else
{
if (nil != link->owner || nil != link->next || nil != link->previous)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] other link is still in a list",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
GSLinkedListInsertAfter(link, self, at);
RETAIN(link);
}
}
- (void) insert: (GSListLink*)link before: (GSListLink*)at
{
if (nil == link)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] nil link argument",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
if (nil == at)
{
at = head;
}
if (at->owner != self)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] 'at' link is not in this list",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
if (at == link)
{
return;
}
if (link->owner == self)
{
GSLinkedListRemove(link, self);
GSLinkedListInsertBefore(link, self, at);
}
else
{
if (nil != link->owner || nil != link->next || nil != link->previous)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] other link is still in a list",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
GSLinkedListInsertBefore(link, self, at);
RETAIN(link);
}
}
- (void) removeLink: (GSListLink*)link
{
if (nil == link)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] nil link argument",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
if (link->owner != self)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] link is not in this list",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
GSLinkedListRemove(link, self);
RETAIN(link);
}
- (GSListLink*) tail
{
return tail;
}
@end
GSListLink*
GSLinkedListFindEqual(NSObject *object, GSLinkedList *list,
GSListLink *from, BOOL back)
{
if (nil == object)
{
return GSLinkedListFindIdentical(object, list, from, back);
}
else
{
BOOL (*imp)(id, SEL, id);
imp = (BOOL(*)(id,SEL,id))[object methodForSelector: @selector(isEqual:)];
if (YES == back)
{
if (nil == from)
{
from = list->tail;
}
while (nil != from)
{
if (YES == (*imp)(object, @selector(isEqual:), from->item))
{
return from;
}
from = from->previous;
}
}
else
{
if (nil == from)
{
from = list->head;
}
while (nil != from)
{
if (YES == (*imp)(object, @selector(isEqual:), from->item))
{
return from;
}
from = from->next;
}
}
}
return nil;
}
GSListLink*
GSLinkedListFindIdentical(NSObject *object, GSLinkedList *list,
GSListLink *from, BOOL back)
{
if (YES == back)
{
if (nil == from)
{
from = list->tail;
}
while (nil != from)
{
if (object == from->item)
{
return from;
}
from = from->previous;
}
}
else
{
if (nil == from)
{
from = list->head;
}
while (nil != from)
{
if (object == from->item)
{
return from;
}
from = from->next;
}
}
return nil;
}
void
GSLinkedListInsertAfter(GSListLink *link, GSLinkedList *list, GSListLink *at)
{
if (nil == list->head)
{
list->head = list->tail = link;
}
else
{
if (nil == at)
{
at = list->tail;
}
link->next = at ? at->next : nil;
if (nil == link->next)
{
list->tail = link;
}
else
{
link->next->previous = link;
}
if (at)
{
at->next = link;
}
link->previous = at;
}
link->owner = list;
list->count++;
}
void
GSLinkedListInsertBefore(GSListLink *link, GSLinkedList *list, GSListLink *at)
{
if (nil == list->head)
{
list->head = list->tail = link;
}
else
{
if (nil == at)
{
at = list->head;
}
link->previous = at->previous;
if (nil == link->previous)
{
list->head = link;
}
else
{
link->previous->next = link;
}
at->previous = link;
link->next = at;
}
link->owner = list;
list->count++;
}
void
GSLinkedListRemove(GSListLink *link, GSLinkedList *list)
{
if (list->head == link)
{
list->head = link->next;
if (nil != list->head)
{
list->head->previous = nil;
}
}
else
{
link->previous->next = link->next;
}
if (list->tail == link)
{
list->tail = link->previous;
if (nil != list->tail)
{
list->tail->next = nil;
}
}
else if (nil != link->next)
{
link->next->previous = link->previous;
}
link->next = link->previous = nil;
link->owner = nil;
list->count--;
}
extern void
GSLinkedListMoveToHead(GSListLink *link, GSLinkedList *list)
{
if (link != list->head)
{
if (link == list->tail)
{
list->tail = link->previous;
list->tail->next = nil;
}
else
{
link->next->previous = link->previous;
link->previous->next = link->next;
}
link->next = list->head;
link->previous = nil;
list->head->previous = link;
list->head = link;
}
}
extern void
GSLinkedListMoveToTail(GSListLink *link, GSLinkedList *list)
{
if (link != list->tail)
{
if (link == list->head)
{
list->head = link->next;
list->head->previous = nil;
}
else
{
link->next->previous = link->previous;
link->previous->next = link->next;
}
link->next = nil;
link->previous = list->tail;
list->tail->next = link;
list->tail = link;
}
}
@implementation GSLinkStore
+ (GSLinkStore*) storeFor: (Class)theLinkClass
{
Class c = [GSListLink class];
GSLinkStore *s;
if (Nil == theLinkClass)
{
theLinkClass = c;
}
NSAssert([theLinkClass isSubclassOfClass: c], NSInvalidArgumentException);
s = [self new];
s->linkClass = theLinkClass;
return AUTORELEASE(s);
}
- (GSListLink*) addObject: (id)anObject
{
return GSLinkStoreInsertObjectAfter(anObject, self, tail);
}
- (void) dealloc
{
[self empty];
[self purge];
DEALLOC
}
- (void) empty
{
while (nil != head)
{
GSLinkStoreRemoveObjectAt(self, head);
}
}
- (id) firstObject
{
return GSLinkedListFirstObject(self);
}
- (id) init
{
if (nil != (self = [super init]))
{
linkClass = [GSListLink class];
}
return self;
}
- (GSListLink*) insertObject: (id)anObject after: (GSListLink*)at
{
return GSLinkStoreInsertObjectAfter(anObject, self, at);
}
- (GSListLink*) insertObject: (id)anObject before: (GSListLink*)at
{
return GSLinkStoreInsertObjectBefore(anObject, self, at);
}
- (id) lastObject
{
return GSLinkedListLastObject(self);
}
- (void) purge
{
while (nil != free)
{
GSListLink *link = free;
free = link->next;
link->next = nil;
RELEASE(link);
}
}
- (void) removeFirstObject
{
if (nil != head)
{
GSLinkStoreRemoveObjectAt(self, head);
}
}
- (void) removeLastObject
{
if (nil != tail)
{
GSLinkStoreRemoveObjectAt(self, tail);
}
}
- (void) removeObjectAt: (GSListLink*)at
{
GSLinkStoreRemoveObjectAt(self, at);
}
- (void) removeObjectAtIndex: (NSUInteger)index
{
GSListLink *link = head;
if (index >= count)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] index too large",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
while (index-- > 0)
{
link = link->next;
}
GSLinkStoreRemoveObjectAt(self, link);
}
@end
GSListLink*
GSLinkStoreInsertObjectAfter(
NSObject *anObject, GSLinkStore *list, GSListLink *at)
{
GSListLink *link = GSLinkStoreProvideLink(list);
link->item = RETAIN(anObject);
GSLinkedListInsertAfter(link, list, (nil == at) ? list->tail : at);
return link;
}
GSListLink*
GSLinkStoreInsertObjectBefore(
NSObject *anObject, GSLinkStore *list, GSListLink *at)
{
GSListLink *link = GSLinkStoreProvideLink(list);
link->item = RETAIN(anObject);
GSLinkedListInsertBefore(link, list, (nil == at) ? list->head : at);
return link;
}
void
GSLinkStoreRemoveObjectAt(GSLinkStore *list, GSListLink *at)
{
if (nil != at && at->owner == list)
{
GSLinkedListRemove(at, list);
RELEASE(at->item);
at->item = nil;
GSLinkStoreConsumeLink(list, at);
}
}

79
GSSkipMutableArray.h Normal file
View file

@ -0,0 +1,79 @@
/**
Copyright (C) 2006 Free Software Foundation, Inc.
Written by: Matt Rice <ratmice@yahoo.com>
Date: 2006
This file is part of the Performance Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#import <Foundation/NSArray.h>
/**
<p>An NSMutableArray category to provide an NSMutableArray instance
which uses a skip list variant for it's underlying data structure.
</p>
<p>While a skip list is typically sorted and represents a dictionary.
the indexed skip list is sorted by index and maintains deltas to represent
the distance between linked nodes.
</p>
<p>The underlying data structure looks much like the figure below:
</p>
<example>
index -> HEAD 1 2 3 4 5 6 TAIL
5| ---------------------> # ------> #
3| -----------> 2 ------> # ------> #
1| -> 1 -> 1 -> 1 -> 1 -> 1 -> # -> #
</example>
<p>Where the numbers represent how many indexes it is to the next node
of the appropriate level. The bottom level always points to the next node.</p>
<p>Finding a specific index starts at the top level, until the current
depth + the next nodes delta is larger than wanted index, then it goes down
1 level, and repeats until it finds the wanted index.
</p>
<p>Addition and removal of indexes requires an update of the deltas of nodes
which begin before, and end after the wanted index,
these are the places where it goes down a level.
</p>
<p>The rationale behind it was where a linked list based mutable array will
quickly add and remove elements, it may perform poorly at accessing any
random index (because it must traverse the entire list to get to the index).
</p>
<p>While a C array based mutable array will perform good at random index
access it may perform poorly at adding and removing indexes
(because it must move all items after the altered index).
</p>
<p>While a GSSkipMutableArray may not outperform a linked list or a
C array based mutable array at their specific strengths, it attempts
to not suffer from either of their weaknesses, at the cost of additional
memory overhead.
</p>
*/
@interface NSMutableArray (GSSkipMutableArray)
/**
* Creates and returns an autoreleased NSMutableArray implemented as a
* skip list for rapid insertion/deletion within very large arrays.
*/
+ (NSMutableArray*) skipArray;
@end

329
GSSkipMutableArray.m Normal file
View file

@ -0,0 +1,329 @@
/**
Copyright (C) 2006 Free Software Foundation, Inc.
Written by: Matt Rice <ratmice@yahoo.com>
Date: 2006
This file is part of the Performance Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#include <inttypes.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSException.h>
#import <Foundation/NSValue.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSString.h>
#import "GSSkipMutableArray.h"
#import "GSIndexedSkipList.h"
#if !defined (GNUSTEP) && (MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4)
typedef unsigned int NSUInteger;
#endif
static Class abstractClass = 0;
static Class concreteClass = 0;
@interface GSSkipMutableArray : NSMutableArray
@end
@interface GSConcreteSkipArray : GSSkipMutableArray
{
GSISList l;
}
- (GSISList) _list;
@end
@implementation NSMutableArray (GSSkipMutableArray)
+ (NSMutableArray*) skipArray
{
return [GSSkipMutableArray array];
}
@end
@implementation GSSkipMutableArray
+ (id) allocWithZone: (NSZone*)z
{
if (self == abstractClass)
{
return [concreteClass allocWithZone: z];
}
return [super allocWithZone: z];
}
+ (void) initialize
{
if (abstractClass == 0)
{
abstractClass = [GSSkipMutableArray class];
concreteClass = [GSConcreteSkipArray class];
}
}
@end
@interface GSConcreteSkipArrayEnumerator : NSEnumerator
{
GSISLNode node;
}
@end
@implementation GSConcreteSkipArrayEnumerator
- (id) initWithArray: (NSArray *)arr
{
if (![arr isKindOfClass: [GSConcreteSkipArray class]])
{
[[NSException exceptionWithName: NSInternalInconsistencyException
reason: @"not a GSConcreteSkipArray"
userInfo: nil] raise];
}
self = [super init];
node = [(GSConcreteSkipArray *)arr _list]->header->forward[0].next;
return self;
}
- (id) nextObject
{
id foo = node->value;
if (node == GSISLNil)
return nil;
node = node->forward[0].next;
return foo;
}
@end
@implementation GSConcreteSkipArray
- (GSISList) _list
{
return l;
}
- (void) _raiseRangeExceptionWithIndex: (NSUInteger)index from: (SEL)sel
{
NSDictionary *info;
NSException *exception;
NSString *reason;
info = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithUnsignedInt: index], @"Index",
[NSNumber numberWithUnsignedInt: l->count], @"Count",
self, @"Array", nil, nil];
reason = [NSString stringWithFormat: @"Index %"PRIuPTR
@" is out of range %d (in '%@')",
index, l->count, NSStringFromSelector(sel)];
exception = [NSException exceptionWithName: NSRangeException
reason: reason
userInfo: info];
[exception raise];
}
+ (void) initialize
{
GSISLInitialize();
}
- (id) initWithObjects: (const id[])objects count: (NSUInteger) count
{
int i;
self = [super init];
if (!self) return nil;
l = GSISLInitList([self zone]);
for (i = 0; i < count; i++)
{
GSISLInsertItemAtIndex(l, [objects[i] retain], i);
}
return self;
}
- (id) init
{
self = [super init];
if (!self) return nil;
l = GSISLInitList([self zone]);
return self;
}
- (void) dealloc
{
GSISLNode p,q;
p = l->header->forward[0].next;
while (p != GSISLNil)
{
q = p->forward[0].next;
[p->value release];
NSZoneFree(l->zone,p);
p = q;
}
NSZoneFree(l->zone, l->header);
NSZoneFree(l->zone, l);
[super dealloc];
}
- (void) insertObject: (id)object atIndex: (NSUInteger)index
{
if (index > l->count)
{
[self _raiseRangeExceptionWithIndex: index from: _cmd];
}
GSISLInsertItemAtIndex(l, [object retain], index);
}
- (id) objectAtIndex: (NSUInteger)index
{
if (index >= l->count)
{
[self _raiseRangeExceptionWithIndex: index from: _cmd];
}
return GSISLItemAtIndex(l, index);
}
- (void) removeObjectAtIndex: (NSUInteger)index
{
if (index >= l->count)
{
[self _raiseRangeExceptionWithIndex: index from: _cmd];
}
[GSISLRemoveItemAtIndex(l, index) release];
}
- (void) addObject: (id)obj
{
GSISLInsertItemAtIndex(l, [obj retain], l->count);
}
- (NSUInteger) count
{
return l->count;
}
- (void) replaceObjectAtIndex: (NSUInteger)index withObject: (id)obj
{
[GSISLReplaceItemAtIndex(l, [obj retain], index) release];
}
- (NSEnumerator*) objectEnumerator
{
id e;
e = [GSConcreteSkipArrayEnumerator
allocWithZone: NSDefaultMallocZone()];
e = [e initWithArray: self];
return [e autorelease];
}
/* returns an in an NSString suitable for running through graphviz,
* with the graph named 'graphName'
*/
- (NSString *) _makeGraphOfInternalLayoutNamed: (NSString *)graphName
{
GSISLNode p;
NSUInteger k, i;
NSMutableDictionary *values;
NSMutableArray *edges;
NSMutableString *graph;
NSArray *tmp;
graph = [[NSMutableString alloc] initWithCapacity: 1024];
[graph appendString:
[NSString stringWithFormat: @"digraph %@ {\n", graphName]];
[graph appendString: @"graph [rankdir = LR];\n"];
[graph appendString: @"node [shape = record];\n"];
values = [[NSMutableDictionary alloc] init];
edges = [[NSMutableArray alloc] init];
[values setObject:
[NSMutableString stringWithFormat:
@"\"%p\" [label = \"%p (NIL) |{ <delta0> 0 | <forward0> }",
GSISLNil, GSISLNil]
forKey: [NSString stringWithFormat: @"%p", GSISLNil]];
for (k = 0; k < l->level + 1; k++)
{
for (p = l->header; p != GSISLNil; p = p->forward[k].next)
{
NSString *value;
NSMutableString *foo;
value = [NSString stringWithFormat: @"%p", p];
foo = [values objectForKey: value];
if (foo == nil)
{
foo = [[NSMutableString alloc] init];
[foo appendString:
[NSString stringWithFormat:
@"\"%p\" [label = \"%p%@ |{ <delta%"PRIuPTR
@"> %i | <forward%"PRIuPTR"> }",
p, p, p == l->header ? @"(HEADER)" : @"", k,
p->forward[k].delta, k]];
if (p != GSISLNil)
[edges addObject:
[NSString stringWithFormat:
@"\"%p\": forward%"PRIuPTR" -> \"%p\": delta%"PRIuPTR";\n",
p, k, p->forward[k].next,
p->forward[k].next == GSISLNil ? 0 : k]];
[values setObject: foo forKey: value];
[foo release];
}
else
{
[foo appendString:
[NSString stringWithFormat:
@"|{ <delta%"PRIuPTR"> %i | <forward%"PRIuPTR"> }",
k, p->forward[k].delta, k]];
if (p != GSISLNil)
[edges addObject:
[NSString stringWithFormat:
@"\"%p\": forward%"PRIuPTR" -> \"%p\": delta%"PRIuPTR";\n",
p, k, p->forward[k].next,
p->forward[k].next == GSISLNil ? 0 : k]];
[values setObject: foo forKey: value];
}
}
}
tmp = [values allKeys];
for (i = 0; i < [tmp count]; i++)
{
[graph appendString: [values objectForKey: [tmp objectAtIndex: i]]];
[graph appendString: @"\"];\n"];
}
for (i = 0; i < [edges count]; i++)
{
[graph appendString: [edges objectAtIndex: i]];
}
[graph appendString: @"}\n"];
[values release];
[edges release];
return [graph autorelease];
}
@end

147
GSThreadPool.h Normal file
View file

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

595
GSThreadPool.m Normal file
View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

@ -7,16 +7,16 @@
This file is part of the Performance Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
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.
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
Library General Public License for more details.
Lesser General Public License for more details.
You should have received a copy of the GNU Library General Public
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.
@ -26,7 +26,7 @@
#ifndef INCLUDED_GSTicker_H
#define INCLUDED_GSTicker_H
#include <Foundation/NSDate.h>
@class NSDate;
/**
* Returns the timestamp of the most recent call to GSTickerTimeNow().
@ -41,7 +41,7 @@ extern NSTimeInterval GSTickerTimeLast();
extern NSTimeInterval GSTickerTimeNow();
/**
* This returns the timestamp from which GSTickerTimeNow() was first called.
* This returns the timestamp from which GSTicker was first used.
*/
extern NSTimeInterval GSTickerTimeStart();

View file

@ -9,36 +9,39 @@
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 Library General Public
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.
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
Library General Public License for more details.
Lesser General Public License for more details.
You should have received a copy of the GNU Library General Public
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 <Foundation/NSArray.h>
#include <Foundation/NSDate.h>
#include <Foundation/NSThread.h>
#include <Foundation/NSTimer.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSException.h>
#import <Foundation/NSThread.h>
#import <Foundation/NSString.h>
#import <Foundation/NSTimer.h>
#include "GSTicker.h"
#import "GSTicker.h"
static Class NSDateClass = 0;
static NSDate *startDate = nil;
static SEL tiSel = 0;
static NSTimeInterval (*tiImp)(Class,SEL) = 0;
static NSTimeInterval baseTime = 0;
static NSTimeInterval lastTime = 0;
static NSDate *startDate = nil;
static volatile NSTimeInterval baseTime = 0;
static volatile NSTimeInterval lastTime = 0;
@interface GSTickerObservation : NSObject
{
@ -63,8 +66,15 @@ static NSDate *startDate = nil;
@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
@ -72,20 +82,25 @@ static NSDate *startDate = nil;
- (void) dealloc
{
[theTimer invalidate];
[theTimer release];
theTimer = nil;
DESTROY(observers);
[observers release];
observers = nil;
[super dealloc];
}
- (id) init
{
NSTimeInterval ti = GSTickerTimeNow();
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];
observers = [NSMutableArray new];
theTimer = [[NSTimer scheduledTimerWithTimeInterval: ti - (int)ti
target: [GSTicker class]
selector: @selector(_tick:)
userInfo: self
repeats: NO] retain];
}
return self;
}
@end
@ -103,7 +118,7 @@ inline NSTimeInterval GSTickerTimeStart()
{
if (baseTime == 0)
{
return GSTickerTimeNow();
[GSTicker class];
}
return baseTime;
}
@ -120,12 +135,7 @@ NSTimeInterval GSTickerTimeNow()
{
if (baseTime == 0)
{
NSDateClass = [NSDate class];
tiSel = @selector(timeIntervalSinceReferenceDate);
tiImp
= (NSTimeInterval (*)(Class,SEL))[NSDateClass methodForSelector: tiSel];
baseTime = lastTime = (*tiImp)(NSDateClass, tiSel);
return baseTime;
[GSTicker class];
}
else
{
@ -141,12 +151,26 @@ NSTimeInterval GSTickerTimeNow()
baseTime -= (lastTime - now);
}
lastTime = now;
return lastTime;
}
return lastTime;
}
@implementation GSTicker
+ (void) initialize
{
if (0 == baseTime)
{
NSDateClass = [NSDate class];
tiSel = @selector(timeIntervalSinceReferenceDate);
tiImp
= (NSTimeInterval (*)(Class,SEL))[NSDateClass methodForSelector: tiSel];
baseTime = lastTime = (*tiImp)(NSDateClass, tiSel);
startDate = [[NSDateClass alloc]
initWithTimeIntervalSinceReferenceDate: baseTime];
}
}
+ (NSDate*) last
{
return [NSDateClass dateWithTimeIntervalSinceReferenceDate:
@ -155,6 +179,7 @@ NSTimeInterval GSTickerTimeNow()
+ (void) newSecond: (id)userInfo
{
return;
}
+ (NSDate*) now
@ -177,7 +202,7 @@ NSTimeInterval GSTickerTimeNow()
tt = [GSTickerThread new];
[[[NSThread currentThread] threadDictionary]
setObject: tt forKey: @"GSTickerThread"];
RELEASE(tt);
[tt release];
}
count = [tt->observers count];
while (count-- > 0)
@ -193,7 +218,7 @@ NSTimeInterval GSTickerTimeNow()
to->observer = anObject;
to->userInfo = userInfo;
[tt->observers addObject: to];
RELEASE(to);
[to release];
}
+ (NSDate*) start
@ -249,6 +274,7 @@ NSTimeInterval GSTickerTimeNow()
- (void) newSecond: (id)userInfo
{
return;
}
- (NSDate*) now
@ -259,12 +285,6 @@ NSTimeInterval GSTickerTimeNow()
- (NSDate*) start
{
if (startDate == nil)
{
startDate = [NSDateClass alloc];
startDate = [startDate initWithTimeIntervalSinceReferenceDate:
GSTickerTimeStart()];
}
return startDate;
}
@ -293,23 +313,42 @@ NSTimeInterval GSTickerTimeNow()
if (tt != nil && [tt->observers count] > 0)
{
NSTimeInterval ti;
NSArray *a = [tt->observers copy];
if (tt->theTimer != t)
{
[tt->theTimer invalidate];
tt->theTimer = nil;
}
[a makeObjectsPerformSelector: @selector(fire:)
withObject: tt->observers];
RELEASE(a);
[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];
tt->theTimer = [[NSTimer scheduledTimerWithTimeInterval: ti - (int)ti
target: self
selector: @selector(_tick:)
userInfo: tt
repeats: NO] retain];
}
else
{

80
GSUniqued.h Normal file
View file

@ -0,0 +1,80 @@
#if !defined(INCLUDED_GSUNIQUED)
#define INCLUDED_GSUNIQUED 1
/**
Copyright (C) 2014 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: April 2014
This file is part of the Performance Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#import <Foundation/NSObject.h>
/** Class used to unique other objects.<br />
* <p>The point of this class is to lower the memory footprint and speed
* up comparisons (pointer equality) in cases where an application
* stores multiple copies of the same object in various maps.<br />
* Since uniquing is performed by storing an immutable copy of the
* original object in a map until there are no further references
* to that object, it's pointless to use this uniquing unless the
* application would be storing at least two copies of the object.<br />
* Also, since this is thread-safe there is a lock management
* overhead wherever a uniqued object is released, so performance
* gains are to be expected only if the uniqued object has a
* relatively long lifetime and is tested for equality with other
* instances frequently.<br />
* In short, use with care; while uniquing can have a big performance
* advantage for some programs, this is actually quite rare.
* </p>
* <p>The internal implementation of the uniquing works by taking
* immutable copies of the objects to be uniqued, storing those copies
* in a hash table, and swizzling their class pointers to a sub-class
* which will automatically remove the instance from the hash table
* before it is deallocated.<br />
* Access to the hash table is protected by locks so that uniqued
* objects may be used freely in multiple threads.<br />
* The name of the subclass used is the name of the original class
* with 'GSUniqued' added as a prefix.
* </p>
*/
@interface GSUniqued : NSObject
/** This method returns a copy of its argument, uniqued so that other
* such copies of equal objects will be the same instance.<br />
* The argument must respond to -copyWithZone: by returning an instance
* of class of immutable objects (ie where the -hash and -isEqual:
* methods are stable for that instance).
*/
+ (id) copyUniqued: (id<NSObject,NSCopying>)anObject;
@end
/** Category for uniquing any copyable object.<br />
* NB. This must only be used by classes for which -copyWithZone:
* produces an instance of an immutable class.
*/
@interface NSObject (GSUniqued)
/** This method returns a copy of the receiver uniqued so that other
* such copies of equal objects content will be the same instance.
*/
- (id) copyUniqued;
@end
#endif

213
GSUniqued.m Normal file
View file

@ -0,0 +1,213 @@
/**
Copyright (C) 2014 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: April 2014
This file is part of the Performance Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#import <Foundation/NSDictionary.h>
#import <Foundation/NSException.h>
#import <Foundation/NSHashTable.h>
#import <Foundation/NSLock.h>
#import <Foundation/NSObjCRuntime.h>
#import <Foundation/NSSet.h>
#import <GNUstepBase/GSObjCRuntime.h>
#import "GSUniqued.h"
static Class GSUniquedClass = Nil;
static NSLock *uniquedObjectsLock;
static IMP iLock;
static IMP iUnlock;
static NSHashTable *uniquedObjects;
static NSLock *classLock;
static NSMutableDictionary *classMap;
/* Deallocate a uniqued object ... we must remove it from the uniqued
* objects table and then call the real -dealloc method.
*/
static void
uDealloc(id self, SEL _cmd)
{
Class c;
IMP i;
NSHashRemove(uniquedObjects, self);
c = object_getClass(self);
c = class_getSuperclass(c);
i = class_getMethodImplementation(c, _cmd);
(*i)(self, _cmd);
}
/* Release a uniqued object ... we must obtain a lock in case the uniqued
* objects table has to be modified by removal of this instance on
* deallocation.
*/
static void
uRelease(id self, SEL _cmd)
{
Class c;
IMP i;
c = object_getClass(self);
c = class_getSuperclass(c);
i = class_getMethodImplementation(c, _cmd);
(*iLock)(uniquedObjectsLock, @selector(lock));
(*i)(self, _cmd);
(*iUnlock)(uniquedObjectsLock, @selector(unlock));
}
@implementation GSUniqued
static Class NSObjectClass;
+ (void) initialize
{
if (Nil == GSUniquedClass)
{
classLock = [NSLock new];
classMap = [NSMutableDictionary new];
uniquedObjectsLock = [NSLock new];
iLock = [uniquedObjectsLock methodForSelector: @selector(lock)];
iUnlock = [uniquedObjectsLock methodForSelector: @selector(unlock)];
uniquedObjects = NSCreateHashTable(
NSNonRetainedObjectHashCallBacks, 10000);
NSObjectClass = [NSObject class];
GSUniquedClass = [GSUniqued class];
}
}
+ (id) allocWithZone: (NSZone*)z
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to allocate instance of GSUniqued"];
return nil;
}
+ (id) copyUniqued: (id<NSObject,NSCopying>)anObject
{
NSObject *found;
NSAssert(nil != anObject, NSInvalidArgumentException);
(*iLock)(uniquedObjectsLock, @selector(lock));
found = [(NSObject*)NSHashGet(uniquedObjects, anObject) retain];
(*iUnlock)(uniquedObjectsLock, @selector(unlock));
if (nil == found)
{
NSObject *aCopy;
Class c;
Class u;
aCopy = [anObject copyWithZone: NSDefaultMallocZone()];
if (aCopy == anObject)
{
/* Unable to make a copy ... that probably means the object
* is already unique and we can just return it.
*/
if (NO == [aCopy isKindOfClass: [NSString class]])
{
return aCopy;
}
else
{
NSMutableString *m;
/* We have different subclasses of NSString and we can't swizzle
* a constant string, so in that case we need to work with another
* type of string.
*/
m = [aCopy mutableCopy];
[aCopy release];
aCopy = [m copy];
[m release];
}
}
c = object_getClass(aCopy);
[classLock lock];
u = [classMap objectForKey: (id<NSCopying>)c];
if (Nil == u)
{
const char *cn = class_getName(c);
char name[strlen(cn) + 20];
Method method;
sprintf(name, "GSUniqued%s", cn);
u = objc_allocateClassPair(c, name, 0);
method = class_getInstanceMethod(NSObjectClass,
@selector(dealloc));
class_addMethod(u, @selector(dealloc),
(IMP)uDealloc, method_getTypeEncoding(method));
method = class_getInstanceMethod(NSObjectClass,
@selector(release));
class_addMethod(u, @selector(release),
(IMP)uRelease, method_getTypeEncoding(method));
method = class_getInstanceMethod(GSUniquedClass,
@selector(copyUniqued));
class_addMethod(u, @selector(copyUniqued),
class_getMethodImplementation(GSUniquedClass,
@selector(copyUniqued)),
method_getTypeEncoding(method));
objc_registerClassPair(u);
[classMap setObject: u forKey: (id<NSCopying>)c];
}
[classLock unlock];
(*iLock)(uniquedObjectsLock, @selector(lock));
found = [(NSObject*)NSHashGet(uniquedObjects, anObject) retain];
if (nil == found)
{
found = aCopy;
#if defined(GNUSTEP)
GSClassSwizzle(found, u);
#else
object_setClass(found, u);
#endif
NSHashInsert(uniquedObjects, found);
}
else
{
[aCopy release]; // Already uniqued by another thread
}
(*iUnlock)(uniquedObjectsLock, @selector(unlock));
}
return found;
}
- (id) copyUniqued
{
return [self retain];
}
@end
@implementation NSObject (GSUniqued)
- (id) copyUniqued
{
if (Nil == GSUniquedClass) [GSUniqued class];
return [GSUniquedClass copyUniqued: (id<NSObject,NSCopying>)self];
}
@end

26
Info.plist Normal file
View file

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

52
NSObject+GSExtensions.h Normal file
View file

@ -0,0 +1,52 @@
/** Declaration of extension methods for base additions
Copyright (C) 2003-2010 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
and: Adam Fedor <fedor@gnu.org>
This file is part of the GNUstep Base Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
*/
#import <Foundation/NSObject.h>
#if !defined (GNUSTEP)
#import "GNUstep.h"
#endif
@class NSHashTable;
@interface NSObject(MemoryFootprint)
/* This method returns the memory usage of the receiver, excluding any
* objects already present in the exclude table.<br />
* The argument is a hash table configured to hold non-retained pointer
* objects and is used to inform the receiver that its size should not
* be counted again if it's already in the table.<br />
* The NSObject implementation returns zero if the receiver is in the
* table, but otherwise adds itself to the table and returns its memory
* footprint (the sum of all of its instance variables, but not any
* memory pointed to by those variables).<br />
* Subclasses should override this method by calling the superclass
* implementation, and either return the result (if it was zero) or
* return that value plus the sizes of any memory owned by the receiver
* (eg found by calling the same method on objects pointed to by the
* receiver's instance variables).
*/
- (NSUInteger) sizeInBytesExcluding: (NSHashTable*)exclude;
@end

42
NSObject+GSExtensions.m Normal file
View file

@ -0,0 +1,42 @@
/* Implementation of extension methods to base additions
Copyright (C) 2010 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
This file is part of the GNUstep Base Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
*/
#import <Foundation/NSHashTable.h>
@implementation NSObject (MemoryFootprint)
+ (NSUInteger) sizeInBytesExcluding: (NSHashTable*)exclude
{
return 0;
}
- (NSUInteger) sizeInBytesExcluding: (NSHashTable*)exclude
{
if (0 == NSHashGet(exclude, self))
{
NSHashInsert(exclude, self);
return class_getInstanceSize(object_getClass(self));
}
return 0;
}
@end

View file

@ -1,5 +1,5 @@
/*
JIGS include file for the GNUstep Performance Library.
JIGS import file for the GNUstep Performance Library.
Copyright (C) 2005 Free Software Foundation, Inc.
@ -9,21 +9,28 @@
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 Library General Public
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.
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
Library General Public License for more details.
Lesser General Public License for more details.
You should have received a copy of the GNU Library General Public
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.
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
*/
#include "GSCache.h"
#include "GSThroughput.h"
#include "GSTicker.h"
#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"

View file

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