Pooled Object Allocator

As part of a little test I have written a pooled object allocator. This is the first time I have written something like this and I quite enjoyed it. I dare say that I have probably fallen foul of some of the little gotchas in developing one of these but hopefully on my next pass or if I use it a little they may become apparent. Overall though for a little exercise I learned a little and wasn't a bad use of a spare hour or so.

THE CODE

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#include <utility> //std::forward
#include <stack>   //std::stack

template<class inputType>
class SimplePool
{
public:

	SimplePool(unsigned int _capacity, int _overrideTypeSize = -1)
		:
		m_allocatedCount(0u),
		m_rawMemory(NULL),
		m_endOfMemory(NULL),
		m_typeSize(0),
		m_maxCount(0)
	{
		// This override type size might sound odd, but can be useful if you need to add padding but don't want to add it to the class.
		m_typeSize = _overrideTypeSize != -1 ? _overrideTypeSize : sizeof(inputType);
		
		// Assign the base memory pool we will allocate from.
		m_rawMemory = static_cast<inputType*>( malloc(_capacity * m_typeSize) );

		//Checking assignment was possible.
		if (m_rawMemory)
		{
			m_endOfMemory = m_rawMemory + _capacity;
			m_maxCount = _capacity;

			//We could also use a vector and reserve all the memory ahead of time and keep track of where we are in it
			//but using a stack for simplicities sake.
			for (unsigned int i = 0; i < m_maxCount; i++)
			{
				m_locations.push(m_rawMemory + i);
			}
		}
	}

	~SimplePool()
	{
		// Freeing all the assigned memory.
		free(m_rawMemory);

		// Cleaning up. You never know this pool might be in a pool.
		m_rawMemory		= NULL;
		m_endOfMemory		= NULL;
		m_allocatedCount	= 0u;
		m_typeSize		= 0u;
		m_maxCount		= 0u;
	}

	bool IsInitialised()
	{
		//Check to see if the pool is active.
		return m_rawMemory != NULL;
	}

	bool Allocate(inputType** _inputPointer)
	{
		//Check we have any room.
		if (m_allocatedCount < m_maxCount)
		{
			//If we have any free slots.
			if (m_locations.size())
			{
				//Always taking from the top of the pile - hoping the memory is still hot.
				(*_inputPointer) = m_locations.top();
				m_locations.pop();
				
				//increment the in use count.
				++m_allocatedCount;
				return true;
			}
		}
		return false;
	}

	//Separate Allocation when allocating something that needs a constructor.
	template <typename ...Args>
	bool Allocate_Construct(inputType** _inputPointer, Args&&... args)
	{
		//Check we have any room.
		if (m_allocatedCount < m_maxCount)
		{
			//If we have any free slots.
			if (m_locations.size())
			{
				//Always taking from the top of the pile - hoping the memory is still hot.
				inputType* addressPtr = m_locations.top();

				*_inputPointer = new((addressPtr)) inputType(std::forward<Args>(args)...);
				m_locations.pop();

				//increment the in use count.
				++m_allocatedCount;
				return true;
			}
		}
		return false;
	}

	bool Deallocate(inputType* _objectToRemove, bool _clear = false)
	{
		//Check the memory we are deleting is in the pool
		if ( (_objectToRemove > m_rawMemory) && (_objectToRemove < m_endOfMemory))
		{
			if (_clear)
			{
				memset(_objectToRemove, 0, m_typeSize);
			}

			//Add deleted object back to the list.
			m_locations.push(_objectToRemove);
			--m_allocatedCount;

			return true;
		}
		return false;
	}

	bool Deallocate_Destruct(inputType* _objectToRemove, bool _clear = false)
	{
		//Check the memory we are deleting is in the pool
		if ((_objectToRemove > m_rawMemory) && (_objectToRemove < m_endOfMemory))
		{
			//Call destructor allocated object.
			_objectToRemove->~inputType();

			//Add deleted object back to the list.
			m_locations.push(_objectToRemove);
			--m_allocatedCount;

			return true;
		}
		return false;
	}


	inputType*			m_rawMemory;
	inputType*			m_endOfMemory;
	unsigned int			m_allocatedCount;
	unsigned int			m_typeSize;
	unsigned int			m_maxCount;
	std::stack<inputType*>		m_locations;
};

int main()
{
	SimplePool<unsigned int> pool(1024u);

	//Test!
	//--------------------------------------------------------
	for (unsigned int i = 0; i < 1280; i++)
	{
		unsigned int* testOutput = 0;
		pool.Allocate_Construct(&testOutput, i);		
		pool.Deallocate_Destruct(testOutput, true);

		//pool.Allocate(&testOutput);		
		//pool.Deallocate(testOutput, true);
	}

	unsigned int* testdealloc = (unsigned int*)45656756765;
	pool.Deallocate(testdealloc, true);

	testdealloc = 0;
	pool.Deallocate(testdealloc, true);

	return 0;
}