Browse code

Improve documentation. Rename some functions to be more correct.

Nat! authored on 17-10-2016 15:18:44
Showing 14 changed files
... ...
@@ -1,2 +1,3 @@
1
-https://www.mulle-kybernetik.com/repositories/mulle-configuration
2
-https://www.mulle-kybernetik.com/repositories/mulle-tests
1
+https://www.mulle-kybernetik.com/repositories/mulle-tests;tests/mulle-tests;${MULLE_TESTS_BRANCH:-release}
2
+https://www.mulle-kybernetik.com/repositories/mulle-homebrew;bin/mulle-homebrew;${MULLE_HOMEBREW_BRANCH:-release}
3
+https://www.mulle-kybernetik.com/repositories/mulle-configuration;;${MULLE_CONFIGURATION_BRANCH:-release}
... ...
@@ -16,5 +16,14 @@ tmp/
16 16
 *.dSYM/
17 17
 *.exe
18 18
 mulle-tests/
19
-/include
20
-/lib
21 19
\ No newline at end of file
20
+/include/
21
+/lib/
22
+/addictions/
23
+/bin/mulle-homebrew/
24
+/addictions/
25
+/tests/include/
26
+/tests/lib/
27
+/tests/pointerarray/pointerarray
28
+/tests/pointerarray/simple
29
+/tests/hashmap/hashmap
30
+/tests/hashmap/othersimple
... ...
@@ -1,141 +1,64 @@
1
-## `mulle_concurrent_pointerlist`
2
-
3
-> Most of the ideas are taken from [Preshing on Programming](http://preshing.com/20160222/a-resizable-concurrent-map/).
4
-> The definition of concurrent and wait-free are from [concurrencyfreaks.blogspot.de](http://concurrencyfreaks.blogspot.de/2013/05/lock-free-and-wait-free-definition-and.html)
5
-
6
-A growing array of pointers, that is **wait-free**.
7
-
8
-Here is a simple usage example:
9
-```
10
-#include <mulle_standalone_concurrent/mulle_standalone_concurrent.h>
1
+# mulle-concurrent
11 2
 
3
+**mulle-concurrent** is a library for wait-free data structures. Wait-freeness
4
+is a desirable property for "hotly" contested data structures in multi-threaded
5
+environments.
12 6
 
13
-static void   test( void)
14
-{
15
-   struct mulle_concurrent_pointerarray             map;
16
-   struct mulle_concurrent_pointerarrayenumerator   rover;
17
-   unsigned int                                     i;
18
-   void                                             *value;
19 7
 
20
-   _mulle_concurrent_pointerarray_init( &map, 0, NULL);
8
+## Data structures
21 9
 
22
-   value = (void *) 0x1848;
10
+Name                            | Description
11
+--------------------------------|---------------------------------------------
12
+`mulle_concurrent_hashmap`      | A growing, mutable map of pointers, indexed by a hash
13
+`mulle_concurrent_pointerarray` | A growing array of pointers
23 14
 
24
-   _mulle_concurrent_pointerarray_add( &map, value);
25
-   value = _mulle_concurrent_pointerarray_get( &map, 0);
26
-   printf( "%p\n", value);
27 15
 
28
-   rover = mulle_concurrent_pointerarray_enumerate( &map);
29
-   while( _mulle_concurrent_pointerarrayenumerator_next( &rover, &value) == 1)
30
-   {
31
-      printf( "%p\n", value);
32
-   }
33
-   _mulle_concurrent_pointerarrayenumerator_done( &rover);
34
-
35
-   _mulle_concurrent_pointerarray_done( &map);
36
-}
37 16
 
17
+> Most of the ideas are taken from [Preshing on Programming](http://preshing.com/20160222/a-resizable-concurrent-map/).
18
+> The definition of concurrent and wait-free are from [concurrencyfreaks.blogspot.de](http://concurrencyfreaks.blogspot.de/2013/05/lock-free-and-wait-free-definition-and.html)
38 19
 
39
-int   main( void)
40
-{
41
-   mulle_aba_init( &mulle_default_allocator);
42
-   mulle_aba_register();
43 20
 
44
-   test();
21
+## Install
45 22
 
46
-   mulle_aba_unregister();
47
-   mulle_aba_done();
23
+On OS X and Linux you can use
24
+[homebrew](//brew.sh), respectively
25
+[linuxbrew](//linuxbrew.sh)
26
+to install the library:
48 27
 
49
-   return( 0);
50
-}
51 28
 ```
52
-
53
-## `mulle_concurrent_hashmap`
54
-
55
-A mutable map of pointers, indexed by a hash, that is **wait-free**.
56
-
57
-Here is a also a simple usage example:
58
-
29
+brew tap mulle-kybernetik/software
30
+brew install mulle-concurrent
59 31
 ```
60
-#include <mulle_standalone_concurrent/mulle_standalone_concurrent.h>
61
-
62
-static void  test( void)
63
-{
64
-   intptr_t                                    hash;
65
-   struct mulle_concurrent_hashmap             map;
66
-   struct mulle_concurrent_hashmapenumerator   rover;
67
-   unsigned int                                i;
68
-   void                                        *value;
69 32
 
70
-   _mulle_concurrent_hashmap_init( &map, 0, NULL);
71
-   {
72
-      _mulle_concurrent_hashmap_insert( &map, 100000, (void *) 0x1848);
73
-      value =  _mulle_concurrent_hashmap_lookup( &map, 100000);
74
-      printf( "%p\n", value);
33
+On other platforms you can use **mulle-install** from
34
+[mulle-build](//www.mulle-kybernetik.com/software/git/mulle-build)
35
+to install the library:
75 36
 
76
-      value =  _mulle_concurrent_hashmap_lookup( &map, 123456);
77
-      printf( "%s\n", value == (void *) 0x1848 ? "unexpected" : "expected");
78
-
79
-
80
-      rover = mulle_concurrent_hashmap_enumerate( &map);
81
-      while( _mulle_concurrent_hashmapenumerator_next( &rover, &hash, &value) == 1)
82
-      {
83
-         printf( "%ld %p\n", hash, value);
84
-      }
85
-      _mulle_concurrent_hashmapenumerator_done( &rover);
86
-
87
-      _mulle_concurrent_hashmap_remove( &map, 100000, (void *) 0x1848);
88
-
89
-      value =  _mulle_concurrent_hashmap_lookup( &map, 100000);
90
-      printf( "%s\n", value == (void *) 0x1848 ? "unexpected" : "expected");
91
-   }
92
-   _mulle_concurrent_hashmap_done( &map);
93
-}
94
-
95
-
96
-int   main( void)
97
-{
98
-   mulle_aba_init( NULL);
99
-   mulle_aba_register();
100
-
101
-   test();
102
-
103
-   mulle_aba_unregister();
104
-   mulle_aba_done();
105
-
106
-   return( 0);
107
-}
37
+```
38
+mulle-install --prefix /usr/local --branch release https://www.mulle-kybernetik.com/repositories/mulle-concurrent
108 39
 ```
109 40
 
110
-## ABA
111 41
 
112
-This library assumes that the allocator you give it, has a vector installed
113
-for 'abafree', that is smart enough to solve the ABA problem when freeing memory.
42
+Otherwise read:
114 43
 
115
-> Hint : Use [mulle-aba](https://www.mulle-kybernetik.com/weblog/2015/mulle_aba_release.html) for that.
44
+* [How to Build](dox/BUILD.md)
116 45
 
117 46
 
118
-## Dependencies
47
+## API
119 48
 
120
-* [mulle-thread](//www.mulle-kybernetik.com/repositories/mulle-thread)
121
-* [mulle-allocator](//www.mulle-kybernetik.com/repositories/mulle-allocator)
122
-* [mulle-aba](//www.mulle-kybernetik.com/repositories/mulle-aba) (for testing)
123
-* [mulle-bootstrap](//www.mulle-kybernetik.com/repositories/mulle-bootstrap) (optional)
124
-* xcodebuild for OS X
125
-* cmake 3.0 for other Unixes
49
+* [mulle_concurrent_pointerarray](dox/API_POINTERARRAY.md)
50
+* [mulle_concurrent_hashmap](dox/API_HASHMAP.md)
126 51
 
127 52
 
128
-## How to build on OS X
53
+### Platforms and Compilers
129 54
 
130
-Get the newest version of `mulle-bootstrap` (at least 0.19)
55
+All platforms and compilers supported by
56
+[mulle-c11](//www.mulle-kybernetik.com/software/git/mulle-c11/) and
57
+[mulle-thread](//www.mulle-kybernetik.com/software/git/mulle-thread/).
131 58
 
132
-```
133
-brew tap mulle-kybernetik/software
134
-brew install mulle-bootstrap
135
-```
136 59
 
137
-Download and build this repository, with dependent libraries:
60
+## Author
138 61
 
139
-```
140
-mulle-bootstrap clone https://www.mulle-kybernetik.com/repositories/mulle-concurrent
141
-```
62
+[Nat!](//www.mulle-kybernetik.com/weblog) for
63
+[Mulle kybernetiK](//www.mulle-kybernetik.com) and
64
+[Codeon GmbH](//www.codeon.de)
142 65
\ No newline at end of file
... ...
@@ -1,3 +1,10 @@
1
+# v1.0
2
+
3
+* renamed `_mulle_concurrent_hashmap_lookup_any` to `mulle_concurrent_hashmap_lookup_any` since its safe to pass NULL.
4
+* renamed `_mulle_concurrent_hashmap_get_count` to `mulle_concurrent_hashmap_count`,
5
+since it's safe to pass NULL and it's not a get operation.'
6
+* improved the documentation
7
+
1 8
 # v0.5
2 9
 
3 10
 * changed internal representation of mask from unsigned int to uintptr_t,
4 11
new file mode 100755
... ...
@@ -0,0 +1,67 @@
1
+#! /bin/sh
2
+
3
+PROJECT="MulleConcurrent"    # requires camel-case
4
+DESC="Wait-free datastuctures (Array/Hashtable) in C"
5
+DEPENDENCIES="mulle-c11
6
+mulle-allocator
7
+mulle-aba
8
+mulle-thread"            # names not camel case
9
+ORIGIN=public            # git repo to push
10
+LANGUAGE=c               # c,cpp, objc
11
+
12
+# source mulle-homebrew.sh (clumsily)
13
+
14
+. ./bin/mulle-homebrew/mulle-homebrew.sh
15
+
16
+# parse options
17
+homebrew_parse_options "$@"
18
+
19
+# dial past options
20
+while [ $# -ne 0 ]
21
+do
22
+   case "$1" in
23
+      -*)
24
+         shift
25
+      ;;
26
+      *)
27
+         break;
28
+      ;;
29
+   esac
30
+done
31
+
32
+#
33
+# these can usually be deduced, if you follow the conventions
34
+#
35
+NAME="`get_name_from_project "${PROJECT}" "${LANGUAGE}"`"
36
+HEADER="`get_header_from_name "${NAME}"`"
37
+VERSIONNAME="`get_versionname_from_project "${PROJECT}"`"
38
+VERSION="`get_header_version "${HEADER}" "${VERSIONNAME}"`"
39
+
40
+
41
+# --- HOMEBREW FORMULA ---
42
+# Information needed to construct a proper brew formula
43
+#
44
+HOMEPAGE="https://www.mulle-kybernetik.com/software/git/${NAME}"
45
+ARCHIVEURL='https://www.mulle-kybernetik.com/software/git/${NAME}/tarball/${VERSION}'  # ARCHIVEURL will be evaled later! keep it in single quotes
46
+
47
+
48
+# --- HOMEBREW TAP ---
49
+# Specify to where and under what bame to publish via your brew tap
50
+#
51
+RBFILE="${NAME}.rb"                    # ruby file for brew
52
+HOMEBREWTAP="../homebrew-software"     # your tap repository path
53
+
54
+
55
+# --- GIT ---
56
+# tag to tag your release
57
+# and the origin where
58
+TAG="${1:-${TAGPREFIX}${VERSION}}"
59
+
60
+
61
+main()
62
+{
63
+   git_main "${ORIGIN}" "${TAG}" || exit 1
64
+   homebrew_main
65
+}
66
+
67
+main "$@"
0 68
new file mode 100644
... ...
@@ -0,0 +1,212 @@
1
+# `mulle_concurrent_hashmap`
2
+
3
+`mulle_concurrent_hashmap` is a mutable map of pointers, indexed by a hash.
4
+Such a hashmap is extremely volatile when shared by multiple threads, that are
5
+inserting and removing entries. As an example: when you get the number of elements
6
+of a such a map it is just a fleeting glimpse of it at a now fairly distant point
7
+in time.
8
+
9
+The following operations should be executed in single-threaded fashion:
10
+
11
+* `_mulle_concurrent_hashmap_init`
12
+* `_mulle_concurrent_hashmap_done`
13
+* `_mulle_concurrent_hashmap_get_size`
14
+
15
+The following operations are fine in multi-threaded environments:
16
+
17
+* `_mulle_concurrent_hashmap_insert`
18
+* `_mulle_concurrent_hashmap_remove`
19
+* `_mulle_concurrent_hashmap_lookup`
20
+
21
+
22
+The following operations work in multi-threaded environments, but should be approached with caution:
23
+
24
+* `mulle_concurrent_hashmap_enumerate`
25
+* `_mulle_concurrent_hashmapenumerator_next`
26
+* `_mulle_concurrent_hashmapenumerator_done`
27
+* `mulle_concurrent_hashmap_lookup_any`
28
+* `mulle_concurrent_hashmap_count`
29
+
30
+## single-threaded
31
+
32
+
33
+### `_mulle_concurrent_hashmap_init`
34
+
35
+```
36
+void   _mulle_concurrent_hashmap_init( struct mulle_concurrent_hashmap *map,
37
+                                       unsigned int size,
38
+                                       struct mulle_allocator *allocator)
39
+```
40
+
41
+Initialize `map`, with a starting `size` of elements. `allocator` will be
42
+used to allocate and free memory during the lifetime of `map`.  You can pass in
43
+for `allocator` to use the default. Call this in single-threaded fashion.
44
+
45
+
46
+### `void  _mulle_concurrent_hashmap_done( struct mulle_concurrent_hashmap *map)`
47
+
48
+```
49
+void  _mulle_concurrent_hashmap_done( struct mulle_concurrent_hashmap *map)
50
+```
51
+
52
+This will free all allocated resources `map`. It will not **free** `map` itself
53
+though. `map` must be a valid pointer. Call this in single-threaded fashion.
54
+
55
+
56
+### `_mulle_concurrent_hashmap_get_size`
57
+
58
+```
59
+unsigned int   mulle_concurrent_hashmap_get_count( struct mulle_concurrent_hashmap *map);
60
+```
61
+
62
+This gives you the current number of hash/value entries of `map`. The returned
63
+number is close to meaningless, when the map is accessed in multi-threaded
64
+fashion. Call this in single-threaded fashion.
65
+
66
+
67
+## multi-threaded
68
+
69
+
70
+### `_mulle_concurrent_hashmap_insert`
71
+
72
+```
73
+int  _mulle_concurrent_hashmap_insert( struct mulle_concurrent_hashmap *map,
74
+                                       intptr_t hash,
75
+                                       void *value)
76
+```
77
+
78
+Insert a `hash`, `value` pair.
79
+`hash` must not be zero. It should be a unique integer key, suitably treated to
80
+be a good hash value. Here is an example of an avalance function for simple
81
+integer keys (1-...)
82
+
83
+```
84
+static inline uint64_t   mulle_hash_avalanche64(uint64_t h)
85
+{
86
+   h ^= h >> 33;
87
+   h *= 0xff51afd7ed558ccd;
88
+   h ^= h >> 33;
89
+   h *= 0xc4ceb9fe1a85ec53;
90
+   h ^= h >> 33;
91
+   return h;
92
+}
93
+```
94
+
95
+`value` can be any `void *` except `NULL` or `(void *) INTPTR_MAX`.  It will
96
+not get dereferenced by the hashmap.
97
+
98
+
99
+Return Values:
100
+   0      : OK
101
+   EEXIST : duplicate
102
+   ENOMEM : out of memory
103
+
104
+
105
+### `_mulle_concurrent_hashmap_remove` - remove a hash/value pair
106
+
107
+```
108
+int  _mulle_concurrent_hashmap_remove( struct mulle_concurrent_hashmap *map,
109
+                                       intptr_t hash,
110
+                                       void *value)
111
+```
112
+
113
+Remove a `hash`, `value` pair. Read the description of
114
+`_mulle_concurrent_hashmap_insert` for information about restrictions
115
+pertaining to both.
116
+
117
+Return Values:
118
+   0      : OK
119
+   ENOENT : not found
120
+   ENOMEM : out of memory
121
+
122
+
123
+### `_mulle_concurrent_hashmap_lookup` - search for a value by hash
124
+
125
+```
126
+void   *_mulle_concurrent_hashmap_lookup( struct mulle_concurrent_hashmap *map,
127
+                                          intptr_t hash)
128
+```
129
+
130
+Looks up a value by its hash.
131
+
132
+Return Values:
133
+   NULL  : not found
134
+   otherwise the value for this hash
135
+
136
+---
137
+
138
+# `mulle_concurrent_hashmapenumerator`
139
+
140
+```
141
+struct mulle_concurrent_hashmapenumerator  mulle_concurrent_hashmap_enumerate( struct mulle_concurrent_hashmap *map)
142
+```
143
+
144
+Enumerate a hashtable. This works reliably if `map` is accessed in
145
+single-threaded fashion, which it probably will NOT be. In multi-threaded
146
+environments, the enumeration may be interrupted by mutations of the hashtable
147
+by other threads. The enumerator itself should not be shared with other threads.
148
+
149
+Here is a simple usage example:
150
+
151
+
152
+```
153
+   struct mulle_concurrent_hashmap             *map;
154
+   struct mulle_concurrent_hashmapenumerator   rover;
155
+   intptr_t                                    hash;
156
+   void                                        *value;
157
+
158
+   rover = mulle_concurrent_hashmap_enumerate( map);
159
+   while( _mulle_concurrent_hashmapenumerator_next( &rover, &hash, &value) == 1)
160
+   {
161
+      printf( "%ld %p\n", hash, value);
162
+   }
163
+   _mulle_concurrent_hashmapenumerator_done( &rover);
164
+```
165
+
166
+### `_mulle_concurrent_hashmapenumerator_next`
167
+
168
+```
169
+int  _mulle_concurrent_hashmapenumerator_next( struct mulle_concurrent_hashmapenumerator *rover,
170
+                                               intptr_t *hash,
171
+                                               void **value)
172
+```
173
+
174
+Get the next `hash`, `value` pair from the enumerator.
175
+
176
+Return Values:
177
+   1           : OK
178
+   0           : nothing left
179
+   -ECANCELLED : hashtable was mutated (Note: **negative errno value**!)
180
+   -ENOMEM     : out of memory         (Note: **negative errno value**!)
181
+
182
+
183
+### `_mulle_concurrent_hashmapenumerator_done`
184
+
185
+```
186
+void  _mulle_concurrent_hashmapenumerator_done( struct mulle_concurrent_hashmapenumerator *rover)
187
+```
188
+
189
+It's a mere conventional function. It may be left out.
190
+
191
+
192
+### `mulle_concurrent_hashmap_count`
193
+
194
+```
195
+unsigned int   mulle_concurrent_hashmap_count( struct mulle_concurrent_hashmap *map);
196
+```
197
+
198
+This gives you the current number of hash/value entries of `map`. It is implemented as an iterator loop, that counts the number of values.
199
+The returned number is close to meaningless, when the map is accessed in multi-threaded fashion.
200
+
201
+
202
+### `mulle_concurrent_hashmap_lookup_any` - get a value from the hashmap
203
+
204
+```
205
+void  *mulle_concurrent_hashmap_lookup_any( struct mulle_concurrent_hashmap *map);
206
+```
207
+
208
+This will return a value from the map. It is implemented as an iterator loop,
209
+that returns the first value. It returns NULL if `map` contains no entries or
210
+is NULL.
211
+
212
+
0 213
new file mode 100644
... ...
@@ -0,0 +1,200 @@
1
+# `mulle_concurrent_pointerlist`
2
+
3
+`mulle_concurrent_pointerlist` is a mutable array of pointers that can only
4
+grow. Such an array can be shared with multiple threads, that can access the
5
+array without locking. It's limitations are it's strength, as it makes its
6
+handling very simple.
7
+
8
+
9
+The following operations should be executed in single-threaded fashion:
10
+
11
+* `_mulle_concurrent_pointerarray_init`
12
+* `_mulle_concurrent_pointerarray_done`
13
+* `_mulle_concurrent_pointerarray_get_size`
14
+
15
+The following operations are fine in multi-threaded environments:
16
+
17
+* `_mulle_concurrent_pointerarray_add`
18
+* `_mulle_concurrent_pointerarray_get`
19
+
20
+The following operations work in multi-threaded environments,
21
+but should be approached with caution:
22
+
23
+* `mulle_concurrent_pointerarray_enumerate`
24
+* `_mulle_concurrent_pointerarrayenumerator_next`
25
+* `_mulle_concurrent_pointerarrayenumerator_done`
26
+* `_mulle_concurrent_pointerarray_reverseenumerate`
27
+* `_mulle_concurrent_pointerarrayreverseenumerator_next`
28
+* `_mulle_concurrent_pointerarrayreverseenumerator_done`
29
+* `mulle_concurrent_pointerarray_map`
30
+* `_mulle_concurrent_pointerarray_find`
31
+* `mulle_concurrent_pointerarray_get_count`
32
+
33
+
34
+### `_mulle_concurrent_pointerarray_init` - initialize pointerarray
35
+
36
+```
37
+void   _mulle_concurrent_pointerarray_init( struct mulle_concurrent_pointerarray *array,
38
+                                       unsigned int size,
39
+                                       struct mulle_allocator *allocator)
40
+```
41
+
42
+Initialize `array`, with a starting `size` of elements. `allocator` will be
43
+used to allocate and free memory during the lifetime of `array`.  You can pass in
44
+for `allocator` to use the default. Call this in single-threaded fashion.
45
+
46
+
47
+### `_mulle_concurrent_pointerarray_done` - free pointerarray resources
48
+
49
+```
50
+void  _mulle_concurrent_pointerarray_done( struct mulle_concurrent_pointerarray *array)
51
+```
52
+
53
+This will free all allocated resources `array`. It will not **free** `array`
54
+itself though. `array` must be a valid pointer. Call this in single-threaded
55
+fashion.
56
+
57
+
58
+### `_mulle_concurrent_pointerarray_insert` - insert a hash/value pair
59
+
60
+```
61
+int  _mulle_concurrent_pointerarray_insert( struct mulle_concurrent_pointerarray *array,
62
+                                       intptr_t hash,
63
+                                       void *value)
64
+```
65
+
66
+Insert a `hash`, `value` pair.
67
+`hash` must not be zero. It should be a unique integer key, suitably treated to
68
+be a good hash value. Here is an example of an avalance function for simple
69
+integer keys (1-...)
70
+
71
+```
72
+static inline uint64_t   mulle_hash_avalanche64(uint64_t h)
73
+{
74
+   h ^= h >> 33;
75
+   h *= 0xff51afd7ed558ccd;
76
+   h ^= h >> 33;
77
+   h *= 0xc4ceb9fe1a85ec53;
78
+   h ^= h >> 33;
79
+   return h;
80
+}
81
+```
82
+
83
+`value` can be any `void *` except `NULL` or `(void *) INTPTR_MAX`.  It will
84
+not get dereferenced by the pointerarray.
85
+
86
+
87
+Return Values:
88
+   0      : OK
89
+   EEXIST : duplicate
90
+   ENOMEM : out of memory
91
+
92
+
93
+### `_mulle_concurrent_pointerarray_remove` - remove a hash/value pair
94
+
95
+```
96
+int  _mulle_concurrent_pointerarray_remove( struct mulle_concurrent_pointerarray *array,
97
+                                       intptr_t hash,
98
+                                       void *value)
99
+```
100
+
101
+Remove a `hash`, `value` pair. Read the description of
102
+`_mulle_concurrent_pointerarray_insert` for information about restrictions
103
+pertaining to both.
104
+
105
+Return Values:
106
+   0      : OK
107
+   ENOENT : not found
108
+   ENOMEM : out of memory
109
+
110
+
111
+### `_mulle_concurrent_pointerarray_lookup` - search for a value by hash
112
+
113
+```
114
+void   *_mulle_concurrent_pointerarray_lookup( struct mulle_concurrent_pointerarray *array,
115
+                                          intptr_t hash)
116
+```
117
+
118
+Looks up a value by its hash.
119
+
120
+Return Values:
121
+   NULL  : not found
122
+   otherwise the value for this hash
123
+
124
+
125
+### `_mulle_concurrent_pointerarray_get_size` - get size of pointerarray
126
+
127
+```
128
+unsigned int  _mulle_concurrent_pointerarray_get_size( struct mulle_concurrent_pointerarray *array)
129
+```
130
+
131
+This gives you the capacity of `array`. This value is close to
132
+meaningless, when the array is accessed in multi-threaded fashion.
133
+
134
+
135
+### `_mulle_concurrent_pointerarray_get_size` - get number of entries of pointerarray
136
+
137
+```
138
+unsigned int   mulle_concurrent_pointerarray_get_count( struct mulle_concurrent_pointerarray *array);
139
+```
140
+
141
+This gives you the current number of hash/value entries of `array`. The returned
142
+number is close to meaningless, when the array is accessed in multi-threaded
143
+fashion.
144
+
145
+
146
+## `mulle_concurrent_pointerarrayenumerator` - enumerator interface
147
+
148
+```
149
+struct mulle_concurrent_pointerarrayenumerator  mulle_concurrent_pointerarray_enumerate( struct mulle_concurrent_pointerarray *array)
150
+```
151
+
152
+Enumerate a hashtable. This works reliably if `array` is accessed in
153
+single-threaded fashion, which it probably will NOT be. In multi-threaded
154
+environments, the enumeration may be interrupted by mutations of the hashtable
155
+by other threads. The enumerator itself should not be shared accessed by other threads.
156
+
157
+Here is a simple usage example:
158
+
159
+
160
+```
161
+   struct mulle_concurrent_pointerarray             *array;
162
+   struct mulle_concurrent_pointerarrayenumerator   rover;
163
+   intptr_t                                    hash;
164
+   void                                        *value;
165
+
166
+   rover = mulle_concurrent_pointerarray_enumerate( array);
167
+   while( _mulle_concurrent_pointerarrayenumerator_next( &rover, &hash, &value) == 1)
168
+   {
169
+      printf( "%ld %p\n", hash, value);
170
+   }
171
+   _mulle_concurrent_pointerarrayenumerator_done( &rover);
172
+```
173
+
174
+### `_mulle_concurrent_pointerarrayenumerator_next` - get next hash/value pair
175
+
176
+```
177
+int  _mulle_concurrent_pointerarrayenumerator_next( struct mulle_concurrent_pointerarrayenumerator *rover,
178
+                                               intptr_t *hash,
179
+                                               void **value)
180
+```
181
+
182
+Get the next `hash`, `value` pair from the enumerator.
183
+
184
+Return Values:
185
+   1           : OK
186
+   0           : nothing left
187
+   -ECANCELLED : hashtable was mutated (Note: **negative errno value**!)
188
+   -ENOMEM     : out of memory         (Note: **negative errno value**!)
189
+
190
+
191
+### `_mulle_concurrent_pointerarrayenumerator_done` - mark the end of the enumerator
192
+
193
+```
194
+void  _mulle_concurrent_pointerarrayenumerator_done( struct mulle_concurrent_pointerarrayenumerator *rover)
195
+```
196
+
197
+It's a mere conventional function. It may be left out.
198
+
199
+
200
+
... ...
@@ -396,6 +396,15 @@ void  _mulle_concurrent_hashmap_done( struct mulle_concurrent_hashmap *map)
396 396
 }
397 397
 
398 398
 
399
+unsigned int  _mulle_concurrent_hashmap_get_size( struct mulle_concurrent_hashmap *map)
400
+{
401
+   struct _mulle_concurrent_hashmapstorage   *p;
402
+   
403
+   p = _mulle_atomic_pointer_read( &map->storage.pointer);
404
+   return( (unsigned int) p->mask + 1);
405
+}
406
+
407
+
399 408
 static int  _mulle_concurrent_hashmap_migrate_storage( struct mulle_concurrent_hashmap *map,
400 409
                                                        struct _mulle_concurrent_hashmapstorage *p)
401 410
 {
... ...
@@ -461,9 +470,6 @@ retry:
461 470
    return( value);
462 471
 }
463 472
 
464
-
465
-
466
-
467 473
 static int   _mulle_concurrent_hashmap_search_next( struct mulle_concurrent_hashmap *map,
468 474
                                                     unsigned int  *expect_mask,
469 475
                                                     unsigned int  *index,
... ...
@@ -471,8 +477,8 @@ static int   _mulle_concurrent_hashmap_search_next( struct mulle_concurrent_hash
471 477
                                                     void **p_value)
472 478
 {
473 479
    struct _mulle_concurrent_hashmapstorage   *p;
474
-   struct _mulle_concurrent_hashvaluepair   *entry;
475
-   void                                     *value;
480
+   struct _mulle_concurrent_hashvaluepair    *entry;
481
+   void                                      *value;
476 482
    
477 483
 retry:
478 484
    p = _mulle_atomic_pointer_read( &map->storage.pointer);
... ...
@@ -572,19 +578,39 @@ retry:
572 578
 }
573 579
 
574 580
 
575
-unsigned int  _mulle_concurrent_hashmap_get_size( struct mulle_concurrent_hashmap *map)
581
+
582
+#pragma mark -
583
+#pragma mark not so concurrent enumerator
584
+
585
+int  _mulle_concurrent_hashmapenumerator_next( struct mulle_concurrent_hashmapenumerator *rover,
586
+                                               intptr_t *p_hash,
587
+                                               void **p_value)
576 588
 {
577
-   struct _mulle_concurrent_hashmapstorage   *p;
589
+   int        rval;
590
+   void       *value;
591
+   intptr_t   hash;
578 592
    
579
-   p = _mulle_atomic_pointer_read( &map->storage.pointer);
580
-   return( (unsigned int) p->mask + 1);
593
+   rval = _mulle_concurrent_hashmap_search_next( rover->map, &rover->mask, &rover->index, &hash, &value);
594
+   
595
+   if( rval <= 0)
596
+      return( rval);
597
+   
598
+   if( p_hash)
599
+      *p_hash = hash;
600
+   if( p_value)
601
+      *p_value = value;
602
+
603
+   return( 1);
581 604
 }
582 605
 
583 606
 
607
+#pragma mark -
608
+#pragma mark enumerator based code
609
+
584 610
 //
585 611
 // obviously just a snapshot at some recent point in time
586 612
 //
587
-unsigned int  mulle_concurrent_hashmap_get_count( struct mulle_concurrent_hashmap *map)
613
+unsigned int  mulle_concurrent_hashmap_count( struct mulle_concurrent_hashmap *map)
588 614
 {
589 615
    unsigned int                                count;
590 616
    int                                         rval;
... ...
@@ -592,7 +618,7 @@ unsigned int  mulle_concurrent_hashmap_get_count( struct mulle_concurrent_hashma
592 618
    
593 619
 retry:
594 620
    count = 0;
595
-
621
+   
596 622
    rover = mulle_concurrent_hashmap_enumerate( map);
597 623
    for(;;)
598 624
    {
... ...
@@ -606,38 +632,13 @@ retry:
606 632
       }
607 633
       ++count;
608 634
    }
609
-
635
+   
610 636
    _mulle_concurrent_hashmapenumerator_done( &rover);
611 637
    return( count);
612 638
 }
613 639
 
614 640
 
615
-#pragma mark -
616
-#pragma mark not so concurrent enumerator
617
-
618
-int  _mulle_concurrent_hashmapenumerator_next( struct mulle_concurrent_hashmapenumerator *rover,
619
-                                               intptr_t *p_hash,
620
-                                               void **p_value)
621
-{
622
-   int        rval;
623
-   void       *value;
624
-   intptr_t   hash;
625
-   
626
-   rval = _mulle_concurrent_hashmap_search_next( rover->map, &rover->mask, &rover->index, &hash, &value);
627
-   
628
-   if( rval <= 0)
629
-      return( rval);
630
-   
631
-   if( p_hash)
632
-      *p_hash = hash;
633
-   if( p_value)
634
-      *p_value = value;
635
-
636
-   return( 1);
637
-}
638
-
639
-
640
-void  *_mulle_concurrent_hashmap_lookup_any( struct mulle_concurrent_hashmap *map)
641
+void  *mulle_concurrent_hashmap_lookup_any( struct mulle_concurrent_hashmap *map)
641 642
 {
642 643
    struct mulle_concurrent_hashmapenumerator  rover;
643 644
    void  *any;
... ...
@@ -58,11 +58,18 @@ struct mulle_concurrent_hashmap
58 58
    struct mulle_allocator                          *allocator;
59 59
 };
60 60
 
61
+#pragma mark -
62
+#pragma mark single-threaded
63
+
61 64
 int  _mulle_concurrent_hashmap_init( struct mulle_concurrent_hashmap *map,
62 65
                                      unsigned int size,
63 66
                                      struct mulle_allocator *allocator);
64 67
 void  _mulle_concurrent_hashmap_done( struct mulle_concurrent_hashmap *map);
65 68
 
69
+unsigned int  _mulle_concurrent_hashmap_get_size( struct mulle_concurrent_hashmap *map);
70
+
71
+#pragma mark -
72
+#pragma mark multi-threaded
66 73
 
67 74
 // if rval == 0, inserted
68 75
 // rval == EEXIST, detected duplicate
... ...
@@ -82,12 +89,8 @@ int  _mulle_concurrent_hashmap_remove( struct mulle_concurrent_hashmap *map,
82 89
 void  *_mulle_concurrent_hashmap_lookup( struct mulle_concurrent_hashmap *map,
83 90
                                          intptr_t hash);
84 91
 
85
-unsigned int  _mulle_concurrent_hashmap_get_size( struct mulle_concurrent_hashmap *map);
86
-unsigned int  mulle_concurrent_hashmap_get_count( struct mulle_concurrent_hashmap *map);
87
-
88
-
89 92
 #pragma mark -
90
-#pragma mark not so concurrent enumerator
93
+#pragma mark limited multi-threaded
91 94
 
92 95
 struct mulle_concurrent_hashmapenumerator
93 96
 {
... ...
@@ -126,8 +129,9 @@ static inline void  _mulle_concurrent_hashmapenumerator_done( struct mulle_concu
126 129
 {
127 130
 }
128 131
 
129
-// convenience using the enumerator
132
+// conveniences using the enumerator
130 133
 
131
-void  *_mulle_concurrent_hashmap_lookup_any( struct mulle_concurrent_hashmap *map);
134
+void   *mulle_concurrent_hashmap_lookup_any( struct mulle_concurrent_hashmap *map);
135
+unsigned int  mulle_concurrent_hashmap_count( struct mulle_concurrent_hashmap *map);
132 136
 
133 137
 #endif /* mulle_concurrent_hashmap_h */
... ...
@@ -315,7 +315,7 @@ unsigned int  _mulle_concurrent_pointerarray_get_size( struct mulle_concurrent_p
315 315
 //
316 316
 // obviously just a snapshot at some recent point in time
317 317
 //
318
-unsigned int  mulle_concurrent_pointerarray_get_count( struct mulle_concurrent_pointerarray *array)
318
+unsigned int   mulle_concurrent_pointerarray_get_count( struct mulle_concurrent_pointerarray *array)
319 319
 {
320 320
    struct _mulle_concurrent_pointerarraystorage   *p;
321 321
    
... ...
@@ -371,7 +371,6 @@ int  _mulle_concurrent_pointerarrayreverseenumerator_next( struct mulle_concurre
371 371
 }
372 372
 
373 373
 
374
-
375 374
 int   _mulle_concurrent_pointerarray_find( struct mulle_concurrent_pointerarray *array,
376 375
                                        void *search)
377 376
 {
... ...
@@ -415,4 +414,3 @@ int   mulle_concurrent_pointerarray_map( struct mulle_concurrent_pointerarray *l
415 414
    _mulle_concurrent_pointerarrayenumerator_done( &rover);
416 415
    return( 0);
417 416
 }
418
-
... ...
@@ -55,6 +55,7 @@ struct mulle_concurrent_pointerarray
55 55
    struct mulle_allocator                               *allocator;
56 56
 };
57 57
 
58
+
58 59
 int  _mulle_concurrent_pointerarray_init( struct mulle_concurrent_pointerarray *array,
59 60
                                           unsigned int size,
60 61
                                           struct mulle_allocator *allocator);
... ...
@@ -65,10 +66,10 @@ int  _mulle_concurrent_pointerarray_add( struct mulle_concurrent_pointerarray *a
65 66
                                          void *value);
66 67
 
67 68
 void  *_mulle_concurrent_pointerarray_get( struct mulle_concurrent_pointerarray *array,
68
-                                           unsigned int n);
69
+                                           unsigned int i);
69 70
 
70 71
 int  _mulle_concurrent_pointerarray_find( struct mulle_concurrent_pointerarray *array,
71
-                                            void *value);
72
+                                          void *value);
72 73
 
73 74
 
74 75
 unsigned int  _mulle_concurrent_pointerarray_get_size( struct mulle_concurrent_pointerarray *array);
... ...
@@ -92,8 +93,8 @@ struct mulle_concurrent_pointerarrayreverseenumerator
92 93
 
93 94
 //
94 95
 // the specific retuned enumerator is only useable for the calling thread
95
-// if you remove stuff from the array, the enumerator will be unhappy and
96
-// stop (but will tell you). If the array grows, the rover is equally unhappy.
96
+// if you add stuff to the array, the enumerator will be unhappy and
97
+// stop (but will tell you).
97 98
 //
98 99
 static inline struct mulle_concurrent_pointerarrayenumerator
99 100
    mulle_concurrent_pointerarray_enumerate( struct mulle_concurrent_pointerarray *array)
... ...
@@ -1,7 +1,11 @@
1 1
 #! /bin/sh
2 2
 
3
-LIBRARY_SHORTNAME="mulle-concurrent"
3
+PROJECTDIR="`dirname "$PWD"`"
4
+PROJECTNAME="`basename "${PROJECTDIR}"`"
5
+LIBRARY_SHORTNAME="mulle_concurrent"
4 6
 
5
-. "../mulle-tests/test-c-common.sh"
6
-. "../mulle-tests/test-tools-common.sh"
7
-. "../mulle-tests/build-test-common.sh"
7
+
8
+. "mulle-tests/test-c-common.sh"
9
+. "mulle-tests/test-tools-common.sh"
10
+. "mulle-tests/test-sharedlib-common.sh"
11
+. "mulle-tests/build-test-common.sh"
8 12
deleted file mode 120000
... ...
@@ -1 +0,0 @@
1
-run-test.sh
2 0
\ No newline at end of file
... ...
@@ -8,8 +8,15 @@
8 8
 #  (was run-mulle-scion-test)
9 9
 
10 10
 
11
+PROJECTDIR="`dirname "$PWD"`"
12
+PROJECTNAME="`basename "${PROJECTDIR}"`"
11 13
 LIBRARY_SHORTNAME="mulle_concurrent"
12 14
 
13
-. "../mulle-tests/test-c-common.sh"
14
-. "../mulle-tests/test-tools-common.sh"
15
-. "../mulle-tests/run-test-common.sh"
15
+
16
+. "mulle-tests/test-c-common.sh"
17
+RELEASE_CL_CFLAGS="${RELEASE_CL_CFLAGS} -DMULLE_ALLOCATOR_EXTERN_GLOBAL=extern"
18
+DEBUG_CL_CFLAGS="${DEBUG_CL_CFLAGS} -DMULLE_ALLOCATOR_EXTERN_GLOBAL=extern"
19
+
20
+. "mulle-tests/test-tools-common.sh"
21
+. "mulle-tests/test-sharedlib-common.sh"
22
+. "mulle-tests/run-test-common.sh"