xref: /haiku/src/system/kernel/slab/ObjectCache.cpp (revision 62f5ba006a08b0df30631375878effaf67ae5dbc)
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