HugeAllocator.hxx 5.57 KB
Newer Older
1
/*
2
 * Copyright (C) 2013-2017 Max Kellermann <max.kellermann@gmail.com>
3
 *
4 5 6
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
7
 *
8 9
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
10
 *
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
28 29
 */

30 31
#ifndef HUGE_ALLOCATOR_HXX
#define HUGE_ALLOCATOR_HXX
32

33
#include "WritableBuffer.hxx"
34
#include "Compiler.h"
35

36
#include <utility>
37

38 39 40 41 42 43 44 45
#include <stddef.h>

#ifdef __linux__

/**
 * Allocate a huge amount of memory.  This will be done in a way that
 * allows giving the memory back to the kernel as soon as we don't
 * need it anymore.  On the downside, this call is expensive.
46 47
 *
 * Throws std::bad_alloc on error
48 49 50 51
 *
 * @returns the allocated buffer with a size which may be rounded up
 * (to the next page size), so callers can take advantage of this
 * allocation overhead
52
 */
53
WritableBuffer<void>
54
HugeAllocate(size_t size);
55 56 57 58 59 60

/**
 * @param p an allocation returned by HugeAllocate()
 * @param size the allocation's size as passed to HugeAllocate()
 */
void
61
HugeFree(void *p, size_t size) noexcept;
62

63 64 65 66 67 68 69
/**
 * Control whether this allocation is copied to newly forked child
 * processes.  Disabling that makes forking a little bit cheaper.
 */
void
HugeForkCow(void *p, size_t size, bool enable) noexcept;

70 71 72 73 74 75 76 77 78
/**
 * Discard any data stored in the allocation and give the memory back
 * to the kernel.  After returning, the allocation still exists and
 * can be reused at any time, but its contents are undefined.
 *
 * @param p an allocation returned by HugeAllocate()
 * @param size the allocation's size as passed to HugeAllocate()
 */
void
79
HugeDiscard(void *p, size_t size) noexcept;
80

81
#elif defined(_WIN32)
82 83
#include <windows.h>

84
WritableBuffer<void>
85
HugeAllocate(size_t size);
86 87

static inline void
88
HugeFree(void *p, gcc_unused size_t size) noexcept
89 90 91 92
{
	VirtualFree(p, 0, MEM_RELEASE);
}

93 94 95 96 97
static inline void
HugeForkCow(void *, size_t, bool) noexcept
{
}

98
static inline void
99
HugeDiscard(void *p, size_t size) noexcept
100 101 102 103
{
	VirtualAlloc(p, size, MEM_RESET, PAGE_NOACCESS);
}

104 105 106 107
#else

/* not Linux: fall back to standard C calls */

108
#include <stdint.h>
109

110
static inline WritableBuffer<void>
111
HugeAllocate(size_t size)
112
{
113
	return {new uint8_t[size], size};
114 115 116
}

static inline void
117
HugeFree(void *_p, size_t) noexcept
118
{
119 120
	auto *p = (uint8_t *)_p;
	delete[] p;
121 122
}

123 124 125 126 127
static inline void
HugeForkCow(void *, size_t, bool) noexcept
{
}

128
static inline void
129
HugeDiscard(void *, size_t) noexcept
130 131 132 133 134
{
}

#endif

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 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
/**
 * Automatic memory management for a dynamic array in "huge" memory.
 */
template<typename T>
class HugeArray {
	typedef WritableBuffer<T> Buffer;
	Buffer buffer{nullptr};

public:
	typedef typename Buffer::size_type size_type;
	typedef typename Buffer::value_type value_type;
	typedef typename Buffer::reference_type reference;
	typedef typename Buffer::const_reference_type const_reference;
	typedef typename Buffer::iterator iterator;
	typedef typename Buffer::const_iterator const_iterator;

	constexpr HugeArray() = default;

	explicit HugeArray(size_type _size)
		:buffer(Buffer::FromVoidFloor(HugeAllocate(sizeof(value_type) * _size))) {}

	constexpr HugeArray(HugeArray &&other)
		:buffer(std::exchange(other.buffer, nullptr)) {}

	~HugeArray() {
		if (buffer != nullptr) {
			auto v = buffer.ToVoid();
			HugeFree(v.data, v.size);
		}
	}

	HugeArray &operator=(HugeArray &&other) {
		std::swap(buffer, other.buffer);
		return *this;
	}

	void ForkCow(bool enable) noexcept {
		auto v = buffer.ToVoid();
		HugeForkCow(v.data, v.size, enable);
	}

	void Discard() noexcept {
		auto v = buffer.ToVoid();
		HugeDiscard(v.data, v.size);
	}

	constexpr bool operator==(std::nullptr_t) const {
		return buffer == nullptr;
	}

	constexpr bool operator!=(std::nullptr_t) const {
		return buffer != nullptr;
	}

	/**
	 * Returns the number of allocated elements.
	 */
	constexpr size_type size() const {
		return buffer.size;
	}

	reference front() {
		return buffer.front();
	}

	const_reference front() const {
		return buffer.front();
	}

	reference back() {
		return buffer.back();
	}

	const_reference back() const {
		return buffer.back();
	}

	/**
	 * Returns one element.  No bounds checking.
	 */
	reference operator[](size_type i) {
		return buffer[i];
	}

	/**
	 * Returns one constant element.  No bounds checking.
	 */
	const_reference operator[](size_type i) const {
		return buffer[i];
	}

	iterator begin() {
		return buffer.begin();
	}

	constexpr const_iterator begin() const {
		return buffer.cbegin();
	}

	iterator end() {
		return buffer.end();
	}

	constexpr const_iterator end() const {
		return buffer.cend();
	}
};

243
#endif