1 /* 2 * Copyright 2008-2010, Axel Dörfler. All Rights Reserved. 3 * Copyright 2007, Hugo Santos. All Rights Reserved. 4 * 5 * Distributed under the terms of the MIT License. 6 */ 7 8 9 #include "ObjectCache.h" 10 11 #include <string.h> 12 13 #include <util/AutoLock.h> 14 #include <vm/vm.h> 15 #include <vm/VMAddressSpace.h> 16 17 #include "slab_private.h" 18 19 20 static const size_t kCacheColorPeriod = 8; 21 22 23 static void 24 object_cache_return_object_wrapper(object_depot* depot, void* cookie, 25 void* object, uint32 flags) 26 { 27 ObjectCache* cache = (ObjectCache*)cookie; 28 29 MutexLocker _(cache->lock); 30 cache->ReturnObjectToSlab(cache->ObjectSlab(object), object, flags); 31 } 32 33 34 // #pragma mark - 35 36 37 ObjectCache::~ObjectCache() 38 { 39 } 40 41 42 status_t 43 ObjectCache::Init(const char* name, size_t objectSize, size_t alignment, 44 size_t maximum, size_t magazineCapacity, size_t maxMagazineCount, 45 uint32 flags, void* cookie, object_cache_constructor constructor, 46 object_cache_destructor destructor, object_cache_reclaimer reclaimer) 47 { 48 strlcpy(this->name, name, sizeof(this->name)); 49 50 mutex_init(&lock, this->name); 51 52 if (objectSize < sizeof(object_link)) 53 objectSize = sizeof(object_link); 54 55 if (alignment > 0 && (objectSize & (alignment - 1))) 56 object_size = objectSize + alignment - (objectSize & (alignment - 1)); 57 else 58 object_size = objectSize; 59 60 TRACE_CACHE(this, "init %lu, %lu -> %lu", objectSize, alignment, 61 object_size); 62 63 cache_color_cycle = 0; 64 total_objects = 0; 65 used_count = 0; 66 empty_count = 0; 67 pressure = 0; 68 min_object_reserve = 0; 69 70 maintenance_pending = false; 71 maintenance_in_progress = false; 72 maintenance_resize = false; 73 maintenance_delete = false; 74 75 usage = 0; 76 this->maximum = maximum; 77 78 this->flags = flags; 79 80 resize_request = NULL; 81 resize_entry_can_wait = NULL; 82 resize_entry_dont_wait = NULL; 83 84 // no gain in using the depot in single cpu setups 85 if (smp_get_num_cpus() == 1) 86 this->flags |= CACHE_NO_DEPOT; 87 88 if (!(this->flags & CACHE_NO_DEPOT)) { 89 // Determine usable magazine configuration values if none had been given 90 if (magazineCapacity == 0) { 91 magazineCapacity = objectSize < 256 92 ? 32 : (objectSize < 512 ? 16 : 8); 93 } 94 if (maxMagazineCount == 0) 95 maxMagazineCount = magazineCapacity / 2; 96 97 status_t status = object_depot_init(&depot, magazineCapacity, 98 maxMagazineCount, flags, this, object_cache_return_object_wrapper); 99 if (status != B_OK) { 100 mutex_destroy(&lock); 101 return status; 102 } 103 } 104 105 this->cookie = cookie; 106 this->constructor = constructor; 107 this->destructor = destructor; 108 this->reclaimer = reclaimer; 109 110 return B_OK; 111 } 112 113 114 slab* 115 ObjectCache::InitSlab(slab* slab, void* pages, size_t byteCount, uint32 flags) 116 { 117 TRACE_CACHE(this, "construct (%p, %p .. %p, %lu)", slab, pages, 118 ((uint8*)pages) + byteCount, byteCount); 119 120 slab->pages = pages; 121 slab->count = slab->size = byteCount / object_size; 122 slab->free = NULL; 123 124 size_t spareBytes = byteCount - (slab->size * object_size); 125 126 if ((this->flags & CACHE_ALIGN_ON_SIZE) != 0) { 127 slab->offset = cache_color_cycle; 128 129 if (slab->offset > spareBytes) 130 cache_color_cycle = slab->offset = 0; 131 else 132 cache_color_cycle += kCacheColorPeriod; 133 } else 134 slab->offset = 0; 135 136 TRACE_CACHE(this, " %lu objects, %lu spare bytes, offset %lu", 137 slab->size, spareBytes, slab->offset); 138 139 uint8* data = ((uint8*)pages) + slab->offset; 140 141 CREATE_PARANOIA_CHECK_SET(slab, "slab"); 142 143 for (size_t i = 0; i < slab->size; i++) { 144 status_t status = B_OK; 145 if (constructor) 146 status = constructor(cookie, data); 147 148 if (status != B_OK) { 149 data = ((uint8*)pages) + slab->offset; 150 for (size_t j = 0; j < i; j++) { 151 if (destructor) 152 destructor(cookie, data); 153 data += object_size; 154 } 155 156 DELETE_PARANOIA_CHECK_SET(slab); 157 158 return NULL; 159 } 160 161 _push(slab->free, object_to_link(data, object_size)); 162 163 ADD_PARANOIA_CHECK(PARANOIA_SUSPICIOUS, slab, 164 &object_to_link(data, object_size)->next, sizeof(void*)); 165 166 data += object_size; 167 } 168 169 return slab; 170 } 171 172 173 void 174 ObjectCache::UninitSlab(slab* slab) 175 { 176 TRACE_CACHE(this, "destruct %p", slab); 177 178 if (slab->count != slab->size) 179 panic("cache: destroying a slab which isn't empty."); 180 181 usage -= slab_size; 182 total_objects -= slab->size; 183 184 DELETE_PARANOIA_CHECK_SET(slab); 185 186 uint8* data = ((uint8*)slab->pages) + slab->offset; 187 188 for (size_t i = 0; i < slab->size; i++) { 189 if (destructor) 190 destructor(cookie, data); 191 data += object_size; 192 } 193 } 194 195 196 void 197 ObjectCache::ReturnObjectToSlab(slab* source, void* object, uint32 flags) 198 { 199 if (source == NULL) { 200 panic("object_cache: free'd object has no slab"); 201 return; 202 } 203 204 ParanoiaChecker _(source); 205 206 object_link* link = object_to_link(object, object_size); 207 208 TRACE_CACHE(this, "returning %p (%p) to %p, %lu used (%lu empty slabs).", 209 object, link, source, source->size - source->count, 210 empty_count); 211 212 _push(source->free, link); 213 source->count++; 214 used_count--; 215 216 ADD_PARANOIA_CHECK(PARANOIA_SUSPICIOUS, source, &link->next, sizeof(void*)); 217 218 if (source->count == source->size) { 219 partial.Remove(source); 220 221 if (empty_count < pressure 222 && total_objects - used_count - source->size 223 >= min_object_reserve) { 224 empty_count++; 225 empty.Add(source); 226 } else { 227 ReturnSlab(source, flags); 228 } 229 } else if (source->count == 1) { 230 full.Remove(source); 231 partial.Add(source); 232 } 233 } 234