2 * Copyright 2008-2010, Axel Dörfler. All Rights Reserved.
3 * Copyright 2007, Hugo Santos. All Rights Reserved.
5 * Distributed under the terms of the MIT License.
9 #include "ObjectCache.h"
13 #include <util/AutoLock.h>
15 #include <vm/VMAddressSpace.h>
17 #include "MemoryManager.h"
18 #include "slab_private.h"
21 RANGE_MARKER_FUNCTION_BEGIN(SlabObjectCache
)
25 object_cache_return_object_wrapper(object_depot
* depot
, void* cookie
,
26 void* object
, uint32 flags
)
28 ObjectCache
* cache
= (ObjectCache
*)cookie
;
30 MutexLocker
_(cache
->lock
);
31 cache
->ReturnObjectToSlab(cache
->ObjectSlab(object
), object
, flags
);
38 ObjectCache::~ObjectCache()
44 ObjectCache::Init(const char* name
, size_t objectSize
, size_t alignment
,
45 size_t maximum
, size_t magazineCapacity
, size_t maxMagazineCount
,
46 uint32 flags
, void* cookie
, object_cache_constructor constructor
,
47 object_cache_destructor destructor
, object_cache_reclaimer reclaimer
)
49 strlcpy(this->name
, name
, sizeof(this->name
));
51 mutex_init(&lock
, this->name
);
53 if (objectSize
< sizeof(object_link
))
54 objectSize
= sizeof(object_link
);
56 if (alignment
< kMinObjectAlignment
)
57 alignment
= kMinObjectAlignment
;
59 if (alignment
> 0 && (objectSize
& (alignment
- 1)))
60 object_size
= objectSize
+ alignment
- (objectSize
& (alignment
- 1));
62 object_size
= objectSize
;
64 TRACE_CACHE(this, "init %lu, %lu -> %lu", objectSize
, alignment
,
67 this->alignment
= alignment
;
68 cache_color_cycle
= 0;
73 min_object_reserve
= 0;
75 maintenance_pending
= false;
76 maintenance_in_progress
= false;
77 maintenance_resize
= false;
78 maintenance_delete
= false;
81 this->maximum
= maximum
;
85 resize_request
= NULL
;
86 resize_entry_can_wait
= NULL
;
87 resize_entry_dont_wait
= NULL
;
89 // no gain in using the depot in single cpu setups
90 if (smp_get_num_cpus() == 1)
91 this->flags
|= CACHE_NO_DEPOT
;
93 if (!(this->flags
& CACHE_NO_DEPOT
)) {
94 // Determine usable magazine configuration values if none had been given
95 if (magazineCapacity
== 0) {
96 magazineCapacity
= objectSize
< 256
97 ? 32 : (objectSize
< 512 ? 16 : 8);
99 if (maxMagazineCount
== 0)
100 maxMagazineCount
= magazineCapacity
/ 2;
102 status_t status
= object_depot_init(&depot
, magazineCapacity
,
103 maxMagazineCount
, flags
, this, object_cache_return_object_wrapper
);
104 if (status
!= B_OK
) {
105 mutex_destroy(&lock
);
110 this->cookie
= cookie
;
111 this->constructor
= constructor
;
112 this->destructor
= destructor
;
113 this->reclaimer
= reclaimer
;
120 ObjectCache::InitSlab(slab
* slab
, void* pages
, size_t byteCount
, uint32 flags
)
122 TRACE_CACHE(this, "construct (%p, %p .. %p, %lu)", slab
, pages
,
123 ((uint8
*)pages
) + byteCount
, byteCount
);
126 slab
->count
= slab
->size
= byteCount
/ object_size
;
129 size_t spareBytes
= byteCount
- (slab
->size
* object_size
);
131 slab
->offset
= cache_color_cycle
;
133 cache_color_cycle
+= alignment
;
134 if (cache_color_cycle
> spareBytes
)
135 cache_color_cycle
= 0;
137 TRACE_CACHE(this, " %lu objects, %lu spare bytes, offset %lu",
138 slab
->size
, spareBytes
, slab
->offset
);
140 uint8
* data
= ((uint8
*)pages
) + slab
->offset
;
142 CREATE_PARANOIA_CHECK_SET(slab
, "slab");
145 for (size_t i
= 0; i
< slab
->size
; i
++) {
146 status_t status
= B_OK
;
148 status
= constructor(cookie
, data
);
150 if (status
!= B_OK
) {
151 data
= ((uint8
*)pages
) + slab
->offset
;
152 for (size_t j
= 0; j
< i
; j
++) {
154 destructor(cookie
, data
);
158 DELETE_PARANOIA_CHECK_SET(slab
);
163 _push(slab
->free
, object_to_link(data
, object_size
));
165 ADD_PARANOIA_CHECK(PARANOIA_SUSPICIOUS
, slab
,
166 &object_to_link(data
, object_size
)->next
, sizeof(void*));
176 ObjectCache::UninitSlab(slab
* slab
)
178 TRACE_CACHE(this, "destruct %p", slab
);
180 if (slab
->count
!= slab
->size
)
181 panic("cache: destroying a slab which isn't empty.");
184 total_objects
-= slab
->size
;
186 DELETE_PARANOIA_CHECK_SET(slab
);
188 uint8
* data
= ((uint8
*)slab
->pages
) + slab
->offset
;
190 for (size_t i
= 0; i
< slab
->size
; i
++) {
192 destructor(cookie
, data
);
199 ObjectCache::ReturnObjectToSlab(slab
* source
, void* object
, uint32 flags
)
201 if (source
== NULL
) {
202 panic("object_cache: free'd object %p has no slab", object
);
206 ParanoiaChecker
_(source
);
209 uint8
* objectsStart
= (uint8
*)source
->pages
+ source
->offset
;
210 if (object
< objectsStart
211 || object
>= objectsStart
+ source
->size
* object_size
212 || ((uint8
*)object
- objectsStart
) % object_size
!= 0) {
213 panic("object_cache: tried to free invalid object pointer %p", object
);
218 object_link
* link
= object_to_link(object
, object_size
);
220 TRACE_CACHE(this, "returning %p (%p) to %p, %lu used (%lu empty slabs).",
221 object
, link
, source
, source
->size
- source
->count
,
224 _push(source
->free
, link
);
228 ADD_PARANOIA_CHECK(PARANOIA_SUSPICIOUS
, source
, &link
->next
, sizeof(void*));
230 if (source
->count
== source
->size
) {
231 partial
.Remove(source
);
233 if (empty_count
< pressure
234 && total_objects
- used_count
- source
->size
235 >= min_object_reserve
) {
239 ReturnSlab(source
, flags
);
241 } else if (source
->count
== 1) {
249 ObjectCache::ObjectAtIndex(slab
* source
, int32 index
) const
251 return (uint8
*)source
->pages
+ source
->offset
+ index
* object_size
;
255 #if PARANOID_KERNEL_FREE
258 ObjectCache::AssertObjectNotFreed(void* object
)
260 MutexLocker
locker(lock
);
262 slab
* source
= ObjectSlab(object
);
263 if (!partial
.Contains(source
) && !full
.Contains(source
)) {
264 panic("object_cache: to be freed object %p: slab not part of cache!",
269 object_link
* link
= object_to_link(object
, object_size
);
270 for (object_link
* freeLink
= source
->free
; freeLink
!= NULL
;
271 freeLink
= freeLink
->next
) {
272 if (freeLink
== link
) {
273 panic("object_cache: double free of %p (slab %p, cache %p)",
274 object
, source
, this);
282 #endif // PARANOID_KERNEL_FREE
285 #if SLAB_OBJECT_CACHE_ALLOCATION_TRACKING
288 ObjectCache::AllocateTrackingInfos(slab
* slab
, size_t byteCount
, uint32 flags
)
291 size_t objectCount
= byteCount
/ object_size
;
292 status_t result
= MemoryManager::AllocateRaw(
293 objectCount
* sizeof(AllocationTrackingInfo
), flags
, pages
);
294 if (result
== B_OK
) {
295 slab
->tracking
= (AllocationTrackingInfo
*)pages
;
296 for (size_t i
= 0; i
< objectCount
; i
++)
297 slab
->tracking
[i
].Clear();
305 ObjectCache::FreeTrackingInfos(slab
* slab
, uint32 flags
)
307 MemoryManager::FreeRawOrReturnCache(slab
->tracking
, flags
);
311 AllocationTrackingInfo
*
312 ObjectCache::TrackingInfoFor(void* object
) const
314 slab
* objectSlab
= ObjectSlab(object
);
315 return &objectSlab
->tracking
[((addr_t
)object
- objectSlab
->offset
316 - (addr_t
)objectSlab
->pages
) / object_size
];
319 #endif // SLAB_OBJECT_CACHE_ALLOCATION_TRACKING
322 RANGE_MARKER_FUNCTION_END(SlabObjectCache
)