knight/shared/KeLumpAllocator.cpp
author Matt Woodrow <pred@alliedmods.net>
Sun Oct 19 16:18:22 2008 +1300 (2008-10-19)
changeset 2366 ef798e137fe7
parent 2226 d10b38524be3
child 2541 0352326a1f3f
permissions -rw-r--r--
Updated rockthevote to use new OnClientConnected forward (bug 3311)
dvander@2226
     1
#include "KePlatform.h"
dvander@2226
     2
#include <KeLumpAllocator.h>
dvander@2226
     3
#include <KeVector.h>
dvander@2226
     4
dvander@2226
     5
using namespace Knight;
dvander@2226
     6
dvander@2226
     7
/**
dvander@2226
     8
 * :TODO: don't make this part of the page, because touching it means 
dvander@2226
     9
 * dirtying a page.  Instead, we should have a separate linked list.
dvander@2226
    10
 * Maybe that linked list itself could be marshalled from one page.
dvander@2226
    11
 */
dvander@2226
    12
struct KeLumpRegion
dvander@2226
    13
{
dvander@2226
    14
	char *base;
dvander@2226
    15
	char *cur;
dvander@2226
    16
	size_t size;
dvander@2226
    17
	size_t avail;
dvander@2226
    18
	KeLumpRegion *next;
dvander@2226
    19
	KeLumpRegion *prev;
dvander@2226
    20
};
dvander@2226
    21
dvander@2226
    22
class KeLumpAllocator
dvander@2226
    23
{
dvander@2226
    24
public:
dvander@2226
    25
	KeLumpAllocator() : m_pUsableRegions(NULL), m_pUnusableRegions(NULL)
dvander@2226
    26
	{
dvander@2226
    27
		m_DefLumpSize = 65536;
dvander@2226
    28
dvander@2226
    29
#if defined KE_PLATFORM_WINDOWS
dvander@2226
    30
		SYSTEM_INFO info;
dvander@2226
    31
dvander@2226
    32
		GetSystemInfo(&info);
dvander@2226
    33
		if (info.dwAllocationGranularity > m_DefLumpSize)
dvander@2226
    34
		{
dvander@2226
    35
			m_DefLumpSize = info.dwAllocationGranularity;
dvander@2226
    36
		}
dvander@2226
    37
#endif
dvander@2226
    38
	}
dvander@2226
    39
dvander@2226
    40
	~KeLumpAllocator()
dvander@2226
    41
	{
dvander@2226
    42
		FreeRegionChain(m_pUsableRegions);
dvander@2226
    43
		FreeRegionChain(m_pUnusableRegions);
dvander@2226
    44
	}
dvander@2226
    45
dvander@2226
    46
	void Reset()
dvander@2226
    47
	{
dvander@2226
    48
		KeLumpRegion *region;
dvander@2226
    49
dvander@2226
    50
		/* Find the tail of the usable regions. */
dvander@2226
    51
		region = m_pUsableRegions;
dvander@2226
    52
		while (region != NULL)
dvander@2226
    53
		{
dvander@2226
    54
			if (region->next == NULL)
dvander@2226
    55
			{
dvander@2226
    56
				break;
dvander@2226
    57
			}
dvander@2226
    58
		}
dvander@2226
    59
dvander@2226
    60
		/* Link the unusable chain into the usable chain. */
dvander@2226
    61
		if (region == NULL)
dvander@2226
    62
		{
dvander@2226
    63
			m_pUsableRegions = m_pUnusableRegions;
dvander@2226
    64
		}
dvander@2226
    65
		else
dvander@2226
    66
		{
dvander@2226
    67
			region->next = m_pUnusableRegions;
dvander@2226
    68
			m_pUnusableRegions->prev = region;
dvander@2226
    69
		}
dvander@2226
    70
		m_pUnusableRegions = NULL;
dvander@2226
    71
dvander@2226
    72
		region = m_pUsableRegions;
dvander@2226
    73
		while (region != NULL)
dvander@2226
    74
		{
dvander@2226
    75
			region->avail = region->size;
dvander@2226
    76
			region->cur = region->base;
dvander@2226
    77
			region = region->next;
dvander@2226
    78
		}
dvander@2226
    79
	}
dvander@2226
    80
dvander@2226
    81
	void FreeRegionChain(KeLumpRegion *region)
dvander@2226
    82
	{
dvander@2226
    83
		KeLumpRegion *next;
dvander@2226
    84
dvander@2226
    85
		while (region != NULL)
dvander@2226
    86
		{
dvander@2226
    87
			next = region->next;
dvander@2226
    88
			
dvander@2226
    89
#if defined KE_PLATFORM_WINDOWS
dvander@2226
    90
			VirtualFree(region, 0, MEM_RELEASE);
dvander@2226
    91
#endif
dvander@2226
    92
			region = next;
dvander@2226
    93
		}
dvander@2226
    94
	}
dvander@2226
    95
dvander@2226
    96
	void *Alloc(size_t size)
dvander@2226
    97
	{
dvander@2226
    98
		char *blob;
dvander@2226
    99
		KeLumpRegion *region;
dvander@2226
   100
dvander@2226
   101
		if (size % 8 != 0)
dvander@2226
   102
		{
dvander@2226
   103
			size += 8;
dvander@2226
   104
			size -= size % 8;
dvander@2226
   105
		}
dvander@2226
   106
dvander@2226
   107
		region = FindRegionForSize(size);
dvander@2226
   108
dvander@2226
   109
		blob = region->cur;
dvander@2226
   110
		region->avail -= size;
dvander@2226
   111
		region->cur += size;
dvander@2226
   112
dvander@2226
   113
		/** 
dvander@2226
   114
		 * Technically we could make one last small allocation, but 
dvander@2226
   115
		 * this edge case is not worth the extra work.
dvander@2226
   116
		 */
dvander@2226
   117
		if (region->avail < 8)
dvander@2226
   118
		{
dvander@2226
   119
			/* Unlink us from the usable list */
dvander@2226
   120
			if (region == m_pUsableRegions)
dvander@2226
   121
			{
dvander@2226
   122
				m_pUsableRegions = m_pUsableRegions->next;
dvander@2226
   123
				m_pUsableRegions->prev = NULL;
dvander@2226
   124
			}
dvander@2226
   125
			else
dvander@2226
   126
			{
dvander@2226
   127
				region->prev->next = region->next;
dvander@2226
   128
				if (region->next != NULL)
dvander@2226
   129
				{
dvander@2226
   130
					region->next->prev = region->prev;
dvander@2226
   131
				}
dvander@2226
   132
			}
dvander@2226
   133
dvander@2226
   134
			/* Link us into the unusable list */
dvander@2226
   135
			region->prev = NULL;
dvander@2226
   136
			region->next = m_pUnusableRegions;
dvander@2226
   137
dvander@2226
   138
			if (m_pUnusableRegions != NULL)
dvander@2226
   139
			{
dvander@2226
   140
				m_pUnusableRegions->prev = region;
dvander@2226
   141
			}
dvander@2226
   142
dvander@2226
   143
			m_pUnusableRegions = region;
dvander@2226
   144
		}
dvander@2226
   145
dvander@2226
   146
		return blob;
dvander@2226
   147
	}
dvander@2226
   148
dvander@2226
   149
private:
dvander@2226
   150
	KeLumpRegion *FindRegionForSize(size_t size)
dvander@2226
   151
	{
dvander@2226
   152
		char *base;
dvander@2226
   153
		KeLumpRegion *region;
dvander@2226
   154
		size_t size_of_region;
dvander@2226
   155
dvander@2226
   156
		/**
dvander@2226
   157
		 * :TODO: replace this with a priority queue or something 
dvander@2226
   158
		 * that's actually fast.  Even worse is we dirty pages by 
dvander@2226
   159
		 * doing this.  Ouch!
dvander@2226
   160
		 */
dvander@2226
   161
		region = m_pUsableRegions;
dvander@2226
   162
		while (region != NULL)
dvander@2226
   163
		{
dvander@2226
   164
			if (region->avail >= size)
dvander@2226
   165
			{
dvander@2226
   166
				return region;
dvander@2226
   167
			}
dvander@2226
   168
			region = region->next;
dvander@2226
   169
		}
dvander@2226
   170
dvander@2226
   171
		/* Make sure regions end at 8-byte alignment. */
dvander@2226
   172
		size_of_region = sizeof(KeLumpRegion);
dvander@2226
   173
		if (size_of_region % 8 != 0)
dvander@2226
   174
		{
dvander@2226
   175
			size_of_region += 8;
dvander@2226
   176
			size_of_region -= size_of_region % 8;
dvander@2226
   177
		}
dvander@2226
   178
dvander@2226
   179
		/* If the size is too big, fix that. */
dvander@2226
   180
		if (size > m_DefLumpSize - size_of_region)
dvander@2226
   181
		{
dvander@2226
   182
			size += m_DefLumpSize;
dvander@2226
   183
			size -= size % m_DefLumpSize;
dvander@2226
   184
		}
dvander@2226
   185
		else
dvander@2226
   186
		{
dvander@2226
   187
			size = m_DefLumpSize;
dvander@2226
   188
		}
dvander@2226
   189
dvander@2226
   190
#if defined KE_PLATFORM_WINDOWS
dvander@2226
   191
		base = (char *)VirtualAlloc(
dvander@2226
   192
			NULL,
dvander@2226
   193
			m_DefLumpSize,
dvander@2226
   194
			MEM_COMMIT|MEM_RESERVE,
dvander@2226
   195
			PAGE_READWRITE);
dvander@2226
   196
#endif
dvander@2226
   197
dvander@2226
   198
		/* Initialize the region */
dvander@2226
   199
		region = (KeLumpRegion *)base;
dvander@2226
   200
		region->base = &base[size_of_region];
dvander@2226
   201
		region->size = size - size_of_region;
dvander@2226
   202
		region->cur = region->base;
dvander@2226
   203
		region->avail = region->size;
dvander@2226
   204
		region->prev = NULL;
dvander@2226
   205
		region->next = m_pUsableRegions;
dvander@2226
   206
dvander@2226
   207
		if (m_pUsableRegions != NULL)
dvander@2226
   208
		{
dvander@2226
   209
			m_pUsableRegions->prev = region;
dvander@2226
   210
		}
dvander@2226
   211
dvander@2226
   212
		m_pUsableRegions = region;
dvander@2226
   213
dvander@2226
   214
		return region;
dvander@2226
   215
	}
dvander@2226
   216
dvander@2226
   217
private:
dvander@2226
   218
	KeLumpRegion *m_pUsableRegions;
dvander@2226
   219
	KeLumpRegion *m_pUnusableRegions;
dvander@2226
   220
	size_t m_DefLumpSize;
dvander@2226
   221
};
dvander@2226
   222
dvander@2226
   223
inline KeLumpAllocator *ke_LumpFromAllocator(ke_allocator_t *arena)
dvander@2226
   224
{
dvander@2226
   225
	return (KeLumpAllocator *)arena->user;
dvander@2226
   226
}
dvander@2226
   227
dvander@2226
   228
void *ke_LumpAlloc(ke_allocator_t *arena, size_t size)
dvander@2226
   229
{
dvander@2226
   230
	return ke_LumpFromAllocator(arena)->Alloc(size);
dvander@2226
   231
}
dvander@2226
   232
dvander@2226
   233
void ke_LumpFree(ke_allocator_t *arena, void *ptr)
dvander@2226
   234
{
dvander@2226
   235
}
dvander@2226
   236
dvander@2226
   237
ke_allocator_t * KE_LINK Knight::KE_CreateLumpAllocator()
dvander@2226
   238
{
dvander@2226
   239
	ke_allocator_t *alloc;
dvander@2226
   240
dvander@2226
   241
	alloc = new ke_allocator_t;
dvander@2226
   242
	alloc->alloc = ke_LumpAlloc;
dvander@2226
   243
	alloc->dealloc = ke_LumpFree;
dvander@2226
   244
	alloc->user = new KeLumpAllocator();
dvander@2226
   245
dvander@2226
   246
	return alloc;
dvander@2226
   247
}
dvander@2226
   248
dvander@2226
   249
void KE_LINK Knight::KE_DestroyLumpAllocator(ke_allocator_t *alloc)
dvander@2226
   250
{
dvander@2226
   251
	delete ke_LumpFromAllocator(alloc);
dvander@2226
   252
	delete alloc;
dvander@2226
   253
}
dvander@2226
   254
dvander@2226
   255
void KE_LINK Knight::KE_ResetLumpAllocator(ke_allocator_t *alloc)
dvander@2226
   256
{
dvander@2226
   257
	ke_LumpFromAllocator(alloc)->Reset();
dvander@2226
   258
}