xref: /haiku/src/system/kernel/slab/HashedObjectCache.cpp (revision 1294543de9ac0eff000eaea1b18368c36435d08e)
1 /*
2  * Copyright 2008, 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 "HashedObjectCache.h"
10 
11 #include "MemoryManager.h"
12 #include "slab_private.h"
13 
14 
15 static inline int
16 __fls0(size_t value)
17 {
18 	if (value == 0)
19 		return -1;
20 
21 	int bit;
22 	for (bit = 0; value != 1; bit++)
23 		value >>= 1;
24 	return bit;
25 }
26 
27 
28 static HashedSlab*
29 allocate_slab(uint32 flags)
30 {
31 	return (HashedSlab*)slab_internal_alloc(sizeof(HashedSlab), flags);
32 }
33 
34 
35 static void
36 free_slab(HashedSlab* slab, uint32 flags)
37 {
38 	slab_internal_free(slab, flags);
39 }
40 
41 
42 // #pragma mark -
43 
44 
45 HashedObjectCache::HashedObjectCache()
46 	:
47 	hash_table(this)
48 {
49 }
50 
51 
52 /*static*/ HashedObjectCache*
53 HashedObjectCache::Create(const char* name, size_t object_size,
54 	size_t alignment, size_t maximum, size_t magazineCapacity,
55 	size_t maxMagazineCount, uint32 flags, void* cookie,
56 	object_cache_constructor constructor, object_cache_destructor destructor,
57 	object_cache_reclaimer reclaimer)
58 {
59 	void* buffer = slab_internal_alloc(sizeof(HashedObjectCache), flags);
60 	if (buffer == NULL)
61 		return NULL;
62 
63 	HashedObjectCache* cache = new(buffer) HashedObjectCache();
64 
65 	// init the hash table
66 	size_t hashSize = cache->hash_table.ResizeNeeded();
67 	buffer = slab_internal_alloc(hashSize, flags);
68 	if (buffer == NULL) {
69 		cache->Delete();
70 		return NULL;
71 	}
72 
73 	cache->hash_table.Resize(buffer, hashSize, true);
74 
75 	if (cache->Init(name, object_size, alignment, maximum, magazineCapacity,
76 			maxMagazineCount, flags, cookie, constructor, destructor,
77 			reclaimer) != B_OK) {
78 		cache->Delete();
79 		return NULL;
80 	}
81 
82 	if ((flags & CACHE_LARGE_SLAB) != 0)
83 		cache->slab_size = 128 * object_size;
84 	else
85 		cache->slab_size = 8 * object_size;
86 
87 	cache->slab_size = MemoryManager::AcceptableChunkSize(cache->slab_size);
88 	cache->lower_boundary = __fls0(cache->slab_size);
89 
90 	return cache;
91 }
92 
93 
94 void
95 HashedObjectCache::Delete()
96 {
97 	this->~HashedObjectCache();
98 	slab_internal_free(this, 0);
99 }
100 
101 
102 slab*
103 HashedObjectCache::CreateSlab(uint32 flags)
104 {
105 	if (!check_cache_quota(this))
106 		return NULL;
107 
108 	Unlock();
109 
110 	HashedSlab* slab = allocate_slab(flags);
111 	if (slab != NULL) {
112 		void* pages;
113 		if (MemoryManager::Allocate(this, flags, pages) == B_OK) {
114 			Lock();
115 			if (InitSlab(slab, pages, slab_size, flags)) {
116 				hash_table.InsertUnchecked(slab);
117 				_ResizeHashTableIfNeeded(flags);
118 				return slab;
119 			}
120 			Unlock();
121 			MemoryManager::Free(pages, flags);
122 		}
123 
124 		free_slab(slab, flags);
125 	}
126 
127 	Lock();
128 	return NULL;
129 }
130 
131 
132 void
133 HashedObjectCache::ReturnSlab(slab* _slab, uint32 flags)
134 {
135 	HashedSlab* slab = static_cast<HashedSlab*>(_slab);
136 
137 	hash_table.RemoveUnchecked(slab);
138 	_ResizeHashTableIfNeeded(flags);
139 
140 	UninitSlab(slab);
141 
142 	Unlock();
143 	MemoryManager::Free(slab->pages, flags);
144 	free_slab(slab, flags);
145 	Lock();
146 }
147 
148 
149 slab*
150 HashedObjectCache::ObjectSlab(void* object) const
151 {
152 	HashedSlab* slab = hash_table.Lookup(::lower_boundary(object, slab_size));
153 	if (slab == NULL) {
154 		panic("hash object cache %p: unknown object %p", this, object);
155 		return NULL;
156 	}
157 	return slab;
158 }
159 
160 
161 void
162 HashedObjectCache::_ResizeHashTableIfNeeded(uint32 flags)
163 {
164 	size_t hashSize = hash_table.ResizeNeeded();
165 	if (hashSize != 0) {
166 		Unlock();
167 		void* buffer = slab_internal_alloc(hashSize, flags);
168 		Lock();
169 
170 		if (buffer != NULL) {
171 			if (hash_table.ResizeNeeded() == hashSize) {
172 				void* oldHash;
173 				hash_table.Resize(buffer, hashSize, true, &oldHash);
174 				if (oldHash != NULL) {
175 					Unlock();
176 					slab_internal_free(oldHash, flags);
177 					Lock();
178 				}
179 			}
180 		}
181 	}
182 }
183