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