headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / kernel / slab / ObjectDepot.cpp
blob702b624461dcc8a6c4e02ad453f0129fb8f4f9aa
1 /*
2 * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2008-2010, Axel Dörfler. All Rights Reserved.
4 * Copyright 2007, Hugo Santos. All Rights Reserved.
6 * Distributed under the terms of the MIT License.
7 */
10 #include <slab/ObjectDepot.h>
12 #include <algorithm>
14 #include <int.h>
15 #include <slab/Slab.h>
16 #include <smp.h>
17 #include <util/AutoLock.h>
19 #include "slab_debug.h"
20 #include "slab_private.h"
23 struct DepotMagazine {
24 DepotMagazine* next;
25 uint16 current_round;
26 uint16 round_count;
27 void* rounds[0];
29 public:
30 inline bool IsEmpty() const;
31 inline bool IsFull() const;
33 inline void* Pop();
34 inline bool Push(void* object);
36 #if PARANOID_KERNEL_FREE
37 bool ContainsObject(void* object) const;
38 #endif
42 struct depot_cpu_store {
43 DepotMagazine* loaded;
44 DepotMagazine* previous;
48 RANGE_MARKER_FUNCTION_BEGIN(SlabObjectDepot)
51 bool
52 DepotMagazine::IsEmpty() const
54 return current_round == 0;
58 bool
59 DepotMagazine::IsFull() const
61 return current_round == round_count;
65 void*
66 DepotMagazine::Pop()
68 return rounds[--current_round];
72 bool
73 DepotMagazine::Push(void* object)
75 if (IsFull())
76 return false;
78 rounds[current_round++] = object;
79 return true;
83 #if PARANOID_KERNEL_FREE
85 bool
86 DepotMagazine::ContainsObject(void* object) const
88 for (uint16 i = 0; i < current_round; i++) {
89 if (rounds[i] == object)
90 return true;
93 return false;
96 #endif // PARANOID_KERNEL_FREE
99 // #pragma mark -
102 static DepotMagazine*
103 alloc_magazine(object_depot* depot, uint32 flags)
105 DepotMagazine* magazine = (DepotMagazine*)slab_internal_alloc(
106 sizeof(DepotMagazine) + depot->magazine_capacity * sizeof(void*),
107 flags);
108 if (magazine) {
109 magazine->next = NULL;
110 magazine->current_round = 0;
111 magazine->round_count = depot->magazine_capacity;
114 return magazine;
118 static void
119 free_magazine(DepotMagazine* magazine, uint32 flags)
121 slab_internal_free(magazine, flags);
125 static void
126 empty_magazine(object_depot* depot, DepotMagazine* magazine, uint32 flags)
128 for (uint16 i = 0; i < magazine->current_round; i++)
129 depot->return_object(depot, depot->cookie, magazine->rounds[i], flags);
130 free_magazine(magazine, flags);
134 static bool
135 exchange_with_full(object_depot* depot, DepotMagazine*& magazine)
137 ASSERT(magazine->IsEmpty());
139 SpinLocker _(depot->inner_lock);
141 if (depot->full == NULL)
142 return false;
144 depot->full_count--;
145 depot->empty_count++;
147 _push(depot->empty, magazine);
148 magazine = _pop(depot->full);
149 return true;
153 static bool
154 exchange_with_empty(object_depot* depot, DepotMagazine*& magazine,
155 DepotMagazine*& freeMagazine)
157 ASSERT(magazine == NULL || magazine->IsFull());
159 SpinLocker _(depot->inner_lock);
161 if (depot->empty == NULL)
162 return false;
164 depot->empty_count--;
166 if (magazine != NULL) {
167 if (depot->full_count < depot->max_count) {
168 _push(depot->full, magazine);
169 depot->full_count++;
170 freeMagazine = NULL;
171 } else
172 freeMagazine = magazine;
175 magazine = _pop(depot->empty);
176 return true;
180 static void
181 push_empty_magazine(object_depot* depot, DepotMagazine* magazine)
183 SpinLocker _(depot->inner_lock);
185 _push(depot->empty, magazine);
186 depot->empty_count++;
190 static inline depot_cpu_store*
191 object_depot_cpu(object_depot* depot)
193 return &depot->stores[smp_get_current_cpu()];
197 // #pragma mark - public API
200 status_t
201 object_depot_init(object_depot* depot, size_t capacity, size_t maxCount,
202 uint32 flags, void* cookie, void (*return_object)(object_depot* depot,
203 void* cookie, void* object, uint32 flags))
205 depot->full = NULL;
206 depot->empty = NULL;
207 depot->full_count = depot->empty_count = 0;
208 depot->max_count = maxCount;
209 depot->magazine_capacity = capacity;
211 rw_lock_init(&depot->outer_lock, "object depot");
212 B_INITIALIZE_SPINLOCK(&depot->inner_lock);
214 int cpuCount = smp_get_num_cpus();
215 depot->stores = (depot_cpu_store*)slab_internal_alloc(
216 sizeof(depot_cpu_store) * cpuCount, flags);
217 if (depot->stores == NULL) {
218 rw_lock_destroy(&depot->outer_lock);
219 return B_NO_MEMORY;
222 for (int i = 0; i < cpuCount; i++) {
223 depot->stores[i].loaded = NULL;
224 depot->stores[i].previous = NULL;
227 depot->cookie = cookie;
228 depot->return_object = return_object;
230 return B_OK;
234 void
235 object_depot_destroy(object_depot* depot, uint32 flags)
237 object_depot_make_empty(depot, flags);
239 slab_internal_free(depot->stores, flags);
241 rw_lock_destroy(&depot->outer_lock);
245 void*
246 object_depot_obtain(object_depot* depot)
248 ReadLocker readLocker(depot->outer_lock);
249 InterruptsLocker interruptsLocker;
251 depot_cpu_store* store = object_depot_cpu(depot);
253 // To better understand both the Alloc() and Free() logic refer to
254 // Bonwick's ``Magazines and Vmem'' [in 2001 USENIX proceedings]
256 // In a nutshell, we try to get an object from the loaded magazine
257 // if it's not empty, or from the previous magazine if it's full
258 // and finally from the Slab if the magazine depot has no full magazines.
260 if (store->loaded == NULL)
261 return NULL;
263 while (true) {
264 if (!store->loaded->IsEmpty())
265 return store->loaded->Pop();
267 if (store->previous
268 && (store->previous->IsFull()
269 || exchange_with_full(depot, store->previous))) {
270 std::swap(store->previous, store->loaded);
271 } else
272 return NULL;
277 void
278 object_depot_store(object_depot* depot, void* object, uint32 flags)
280 ReadLocker readLocker(depot->outer_lock);
281 InterruptsLocker interruptsLocker;
283 depot_cpu_store* store = object_depot_cpu(depot);
285 // We try to add the object to the loaded magazine if we have one
286 // and it's not full, or to the previous one if it is empty. If
287 // the magazine depot doesn't provide us with a new empty magazine
288 // we return the object directly to the slab.
290 while (true) {
291 if (store->loaded != NULL && store->loaded->Push(object))
292 return;
294 DepotMagazine* freeMagazine = NULL;
295 if ((store->previous != NULL && store->previous->IsEmpty())
296 || exchange_with_empty(depot, store->previous, freeMagazine)) {
297 std::swap(store->loaded, store->previous);
299 if (freeMagazine != NULL) {
300 // Free the magazine that didn't have space in the list
301 interruptsLocker.Unlock();
302 readLocker.Unlock();
304 empty_magazine(depot, freeMagazine, flags);
306 readLocker.Lock();
307 interruptsLocker.Lock();
309 store = object_depot_cpu(depot);
311 } else {
312 // allocate a new empty magazine
313 interruptsLocker.Unlock();
314 readLocker.Unlock();
316 DepotMagazine* magazine = alloc_magazine(depot, flags);
317 if (magazine == NULL) {
318 depot->return_object(depot, depot->cookie, object, flags);
319 return;
322 readLocker.Lock();
323 interruptsLocker.Lock();
325 push_empty_magazine(depot, magazine);
326 store = object_depot_cpu(depot);
332 void
333 object_depot_make_empty(object_depot* depot, uint32 flags)
335 WriteLocker writeLocker(depot->outer_lock);
337 // collect the store magazines
339 DepotMagazine* storeMagazines = NULL;
341 int cpuCount = smp_get_num_cpus();
342 for (int i = 0; i < cpuCount; i++) {
343 depot_cpu_store& store = depot->stores[i];
345 if (store.loaded) {
346 _push(storeMagazines, store.loaded);
347 store.loaded = NULL;
350 if (store.previous) {
351 _push(storeMagazines, store.previous);
352 store.previous = NULL;
356 // detach the depot's full and empty magazines
358 DepotMagazine* fullMagazines = depot->full;
359 depot->full = NULL;
361 DepotMagazine* emptyMagazines = depot->empty;
362 depot->empty = NULL;
364 writeLocker.Unlock();
366 // free all magazines
368 while (storeMagazines != NULL)
369 empty_magazine(depot, _pop(storeMagazines), flags);
371 while (fullMagazines != NULL)
372 empty_magazine(depot, _pop(fullMagazines), flags);
374 while (emptyMagazines)
375 free_magazine(_pop(emptyMagazines), flags);
379 #if PARANOID_KERNEL_FREE
381 bool
382 object_depot_contains_object(object_depot* depot, void* object)
384 WriteLocker writeLocker(depot->outer_lock);
386 int cpuCount = smp_get_num_cpus();
387 for (int i = 0; i < cpuCount; i++) {
388 depot_cpu_store& store = depot->stores[i];
390 if (store.loaded != NULL && !store.loaded->IsEmpty()) {
391 if (store.loaded->ContainsObject(object))
392 return true;
395 if (store.previous != NULL && !store.previous->IsEmpty()) {
396 if (store.previous->ContainsObject(object))
397 return true;
401 for (DepotMagazine* magazine = depot->full; magazine != NULL;
402 magazine = magazine->next) {
403 if (magazine->ContainsObject(object))
404 return true;
407 return false;
410 #endif // PARANOID_KERNEL_FREE
413 // #pragma mark - private kernel API
416 void
417 dump_object_depot(object_depot* depot)
419 kprintf(" full: %p, count %lu\n", depot->full, depot->full_count);
420 kprintf(" empty: %p, count %lu\n", depot->empty, depot->empty_count);
421 kprintf(" max full: %lu\n", depot->max_count);
422 kprintf(" capacity: %lu\n", depot->magazine_capacity);
423 kprintf(" stores:\n");
425 int cpuCount = smp_get_num_cpus();
427 for (int i = 0; i < cpuCount; i++) {
428 kprintf(" [%d] loaded: %p\n", i, depot->stores[i].loaded);
429 kprintf(" previous: %p\n", depot->stores[i].previous);
435 dump_object_depot(int argCount, char** args)
437 if (argCount != 2)
438 kprintf("usage: %s [address]\n", args[0]);
439 else
440 dump_object_depot((object_depot*)parse_expression(args[1]));
442 return 0;
447 dump_depot_magazine(int argCount, char** args)
449 if (argCount != 2) {
450 kprintf("usage: %s [address]\n", args[0]);
451 return 0;
454 DepotMagazine* magazine = (DepotMagazine*)parse_expression(args[1]);
456 kprintf("next: %p\n", magazine->next);
457 kprintf("current_round: %u\n", magazine->current_round);
458 kprintf("round_count: %u\n", magazine->round_count);
460 for (uint16 i = 0; i < magazine->current_round; i++)
461 kprintf(" [%i] %p\n", i, magazine->rounds[i]);
463 return 0;
467 RANGE_MARKER_FUNCTION_END(SlabObjectDepot)