xref: /haiku/src/kits/support/BlockCache.cpp (revision f2b4344867e97c3f4e742a1b4a15e6879644601a)
1 /*
2  * Copyright (c) 2003 Marcus Overhagen
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include <BlockCache.h>
24 #include <Debug.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <new>
28 
29 #define MAGIC1		0x9183f4d9
30 #define MAGIC2		0xa6b3c87d
31 
32 struct BBlockCache::_FreeBlock {
33 	DEBUG_ONLY(	uint32		magic1;	)
34 				_FreeBlock *next;
35 	DEBUG_ONLY(	uint32		magic2;	)
36 };
37 
38 
39 // The requirements set by the BeBook's description of the destructor,
40 // as well as Get() function, allowing the caller to dispose of the
41 // memory, do not allow to allocate one large block to be used as pool.
42 // Thus we need to create multiple small ones.
43 // We maintain a list of free blocks.
44 
45 BBlockCache::BBlockCache(uint32 blockCount,
46 						 size_t blockSize,
47 						 uint32 allocationType)
48  :	fFreeList(0),
49  	fBlockSize(blockSize),
50  	fFreeBlocks(0),
51  	fBlockCount(blockCount),
52 	fLocker("some BBlockCache lock"),
53 	fAlloc(0),
54 	fFree(0)
55 {
56 	switch (allocationType) {
57 		case B_OBJECT_CACHE:
58 			fAlloc = &operator new[];
59 			fFree = &operator delete[];
60 			break;
61 		case B_MALLOC_CACHE:
62 		default:
63 			fAlloc = &malloc;
64 			fFree = &free;
65 			break;
66 	}
67 
68 	// To properly maintain a list of free buffers, a buffer must be
69 	// large enough to contain the _FreeBlock struct that is used.
70 	if (blockSize < sizeof(_FreeBlock))
71 		blockSize = sizeof(_FreeBlock);
72 
73 	// should have at least one block
74 	if (blockCount == 0)
75 		blockCount = 1;
76 
77 	// create blocks and put them into the free list
78 	while (blockCount--) {
79 		_FreeBlock *block = reinterpret_cast<_FreeBlock *>(fAlloc(blockSize));
80 		if (!block)
81 			break;
82 		fFreeBlocks++;
83 		block->next = fFreeList;
84 		fFreeList = block;
85 		DEBUG_ONLY(block->magic1 = MAGIC1);
86 		DEBUG_ONLY(block->magic2 = MAGIC2 + (uint32)(addr_t)block->next);
87 	}
88 }
89 
90 BBlockCache::~BBlockCache()
91 {
92 	// walk the free list and deallocate all blocks
93 	fLocker.Lock();
94 	while (fFreeList) {
95 		ASSERT(fFreeList->magic1 == MAGIC1);
96 		ASSERT(fFreeList->magic2 == MAGIC2 + (uint32)(addr_t)fFreeList->next);
97 		void *pointer = fFreeList;
98 		fFreeList = fFreeList->next;
99 		DEBUG_ONLY(memset(pointer, 0xCC, sizeof(_FreeBlock)));
100 		fFree(pointer);
101 	}
102 	fLocker.Unlock();
103 }
104 
105 void *
106 BBlockCache::Get(size_t blockSize)
107 {
108 	if (!fLocker.Lock())
109 		return 0;
110 	void *pointer;
111 	if (blockSize == fBlockSize && fFreeList != 0) {
112 		// we can take a block from the list
113 		ASSERT(fFreeList->magic1 == MAGIC1);
114 		ASSERT(fFreeList->magic2 == MAGIC2 + (uint32)(addr_t)fFreeList->next);
115 		pointer = fFreeList;
116 		fFreeList = fFreeList->next;
117 		fFreeBlocks--;
118 		DEBUG_ONLY(memset(pointer, 0xCC, sizeof(_FreeBlock)));
119 	} else {
120 		if (blockSize < sizeof(_FreeBlock))
121 			blockSize = sizeof(_FreeBlock);
122 		pointer = fAlloc(blockSize);
123 		DEBUG_ONLY(if (pointer) memset(pointer, 0xCC, sizeof(_FreeBlock)));
124 	}
125 	fLocker.Unlock();
126 	return pointer;
127 }
128 
129 void
130 BBlockCache::Save(void *pointer, size_t blockSize)
131 {
132 	if (!fLocker.Lock())
133 		return;
134 	if (blockSize == fBlockSize && fFreeBlocks < fBlockCount) {
135 		// the block needs to be returned to the cache
136 		_FreeBlock *block = reinterpret_cast<_FreeBlock *>(pointer);
137 		block->next = fFreeList;
138 		fFreeList = block;
139 		fFreeBlocks++;
140 		DEBUG_ONLY(block->magic1 = MAGIC1);
141 		DEBUG_ONLY(block->magic2 = MAGIC2 + (uint32)(addr_t)block->next);
142 	} else {
143 		DEBUG_ONLY(memset(pointer, 0xCC, sizeof(_FreeBlock)));
144 		fFree(pointer);
145 	}
146 	fLocker.Unlock();
147 }
148 
149 void BBlockCache::_ReservedBlockCache1() {}
150 void BBlockCache::_ReservedBlockCache2() {}
151