mirror of
https://github.com/bkaradzic/bx.git
synced 2026-06-08 03:03:48 +00:00
Added more functionality to memoryMap/Unmap. (#384)
This commit is contained in:
committed by
GitHub
parent
3ea49f98d6
commit
8d98acc6f7
120
include/bx/os.h
120
include/bx/os.h
@@ -21,54 +21,168 @@ BX_ERROR_RESULT(kErrorMemoryUnmapFailed, BX_MAKEFOURCC('b', 'x', '8', '1') );
|
||||
|
||||
namespace bx
|
||||
{
|
||||
/// Virtual memory flags used by `memoryMap`, `memoryUnmap`, and `memoryPageSize`.
|
||||
/// Flags are grouped into mutually exclusive fields (State, Protection, Page, Advise).
|
||||
/// Zero in a field means "no change" to that aspect of an existing region.
|
||||
struct Memory
|
||||
{
|
||||
enum Enum : uint32_t
|
||||
{
|
||||
// State (bits 0..3) - zero = no change to existing region.
|
||||
Reserve = 0x00000001, //!< Reserve address range (no backing).
|
||||
Commit = 0x00000003, //!< Reserve + back with physical pages.
|
||||
Decommit = 0x00000004, //!< Drop physical pages (keep reservation).
|
||||
StateMask = 0x0000000f,
|
||||
|
||||
// Protection (bits 4..7) - zero = no change to existing region. Mutually exclusive values.
|
||||
ProtectNone = 0x00000010, //!< No access.
|
||||
ProtectRead = 0x00000020, //!< Read-only.
|
||||
ProtectReadWrite = 0x00000030, //!< Read/Write.
|
||||
ProtectReadExec = 0x00000040, //!< Read/Execute.
|
||||
ProtectMask = 0x000000f0,
|
||||
|
||||
// Page size (bits 8..11) - zero = default system page size. Applied only on fresh reservation.
|
||||
PageLarge = 0x00000100, //!< Large/huge pages (~2 MiB).
|
||||
PageHuge = 0x00000200, //!< Gigantic pages (~1 GiB) where supported.
|
||||
PageMask = 0x00000f00,
|
||||
|
||||
// Access advice (bits 12..15) - zero = no change. Mutually exclusive values.
|
||||
Normal = 0x00001000, //!< Reset access pattern to default.
|
||||
SequentialAccess = 0x00002000, //!< Hint: memory is accessed sequentially.
|
||||
RandomAccess = 0x00003000, //!< Hint: memory is accessed randomly.
|
||||
Prefetch = 0x00004000, //!< Fault-in / prefault pages.
|
||||
DontNeed = 0x00005000, //!< Can drop pages opportunistically.
|
||||
AdviseMask = 0x0000f000,
|
||||
|
||||
ReadWrite = Commit | ProtectReadWrite, //!< Reserve + commit + read/write.
|
||||
};
|
||||
};
|
||||
|
||||
/// Suspend calling thread for at least `_ms` milliseconds.
|
||||
///
|
||||
/// @param[in] _ms Minimum sleep duration, in milliseconds.
|
||||
///
|
||||
void sleep(uint32_t _ms);
|
||||
|
||||
///
|
||||
/// Yield remainder of the current thread's time slice to another runnable thread.
|
||||
void yield();
|
||||
|
||||
/// Get the OS-level identifier of the calling thread.
|
||||
///
|
||||
/// @returns Platform-specific thread id.
|
||||
///
|
||||
uint32_t getTid();
|
||||
|
||||
/// Get resident memory used by the current process, in bytes.
|
||||
///
|
||||
/// @returns Process resident memory footprint in bytes, or 0 if unavailable.
|
||||
///
|
||||
size_t getProcessMemoryUsed();
|
||||
|
||||
/// Open a dynamic library.
|
||||
///
|
||||
/// @param[in] _filePath Path to the library (extension may be platform-specific, see `BX_DL_EXT`).
|
||||
/// @returns Handle to the loaded library, or NULL on failure.
|
||||
///
|
||||
void* dlopen(const FilePath& _filePath);
|
||||
|
||||
/// Close a dynamic library previously opened with `dlopen`.
|
||||
///
|
||||
/// @param[in] _handle Handle returned by `dlopen`.
|
||||
///
|
||||
void dlclose(void* _handle);
|
||||
|
||||
/// Look up a symbol in a dynamic library.
|
||||
///
|
||||
/// @param[in] _handle Handle returned by `dlopen`.
|
||||
/// @param[in] _symbol Symbol name to resolve.
|
||||
/// @returns Address of the symbol, or NULL if not found.
|
||||
///
|
||||
void* dlsym(void* _handle, const StringView& _symbol);
|
||||
|
||||
/// Look up a typed symbol in a dynamic library.
|
||||
///
|
||||
/// @param[in] _handle Handle returned by `dlopen`.
|
||||
/// @param[in] _symbol Symbol name to resolve.
|
||||
/// @returns Address of the symbol cast to `ProtoT`, or NULL if not found.
|
||||
///
|
||||
template<typename ProtoT>
|
||||
ProtoT dlsym(void* _handle, const StringView& _symbol);
|
||||
|
||||
/// Read an environment variable.
|
||||
///
|
||||
/// @param[out] _out Buffer that receives the value (may be NULL to query required size).
|
||||
/// @param[in,out] _inOutSize On input the capacity of `_out`, on output the required size including terminator.
|
||||
/// @param[in] _name Name of the environment variable.
|
||||
/// @returns true if the variable exists and its value fits in `_out`.
|
||||
///
|
||||
bool getEnv(char* _out, uint32_t* _inOutSize, const StringView& _name);
|
||||
|
||||
/// Set an environment variable.
|
||||
///
|
||||
/// @param[in] _name Name of the environment variable.
|
||||
/// @param[in] _value Value to assign.
|
||||
///
|
||||
void setEnv(const StringView& _name, const StringView& _value);
|
||||
|
||||
/// Change current working directory of the process.
|
||||
///
|
||||
/// @param[in] _path Target directory path.
|
||||
/// @returns 0 on success, non-zero on failure.
|
||||
///
|
||||
int chdir(const char* _path);
|
||||
|
||||
/// Execute a program in a child process.
|
||||
///
|
||||
/// @param[in] _argv NULL-terminated argument vector; `_argv[0]` is the executable path.
|
||||
/// @returns Platform-specific process handle, or NULL on failure.
|
||||
///
|
||||
void* exec(const char* const* _argv);
|
||||
|
||||
/// Terminate the current process.
|
||||
///
|
||||
/// @param[in] _exitCode Exit code returned to the OS.
|
||||
/// @param[in] _cleanup When true, run cleanup (atexit handlers, etc.); when false, exit immediately.
|
||||
///
|
||||
[[noreturn]] void exit(int32_t _exitCode, bool _cleanup = true);
|
||||
|
||||
/// Map, reconfigure, or reshape a virtual memory region.
|
||||
///
|
||||
void* memoryMap(void* _address, size_t _size, Error* _err);
|
||||
/// - If `_address` is NULL: reserves a new region of `_size`, aligned to `_alignment`
|
||||
/// (0 = page size), and applies the requested state/protect/advise flags. Flags
|
||||
/// must include `Memory::Reserve`.
|
||||
/// - If `_address` is non-NULL: reconfigures the existing region according to the
|
||||
/// flags present. Each flag group (State / Protection / Advise) is only touched
|
||||
/// when its bits are non-zero, allowing targeted changes.
|
||||
///
|
||||
/// Page-size bits select which page size to use for a fresh reservation; they
|
||||
/// are ignored for in-place reconfiguration of an existing region.
|
||||
///
|
||||
/// @param[in] _address Existing base, or NULL to allocate a new region.
|
||||
/// @param[in] _size Size in bytes, rounded up to the selected page size.
|
||||
/// @param[in] _alignment Base alignment in bytes, 0 = page size.
|
||||
/// @param[in] _flags OR-combination of `Memory::Enum` values.
|
||||
/// @param[out] _err Error state.
|
||||
/// @returns Base address of the region (same as `_address` for reconfiguration), or NULL on failure.
|
||||
///
|
||||
void* memoryMap(void* _address, size_t _size, size_t _alignment, uint32_t _flags, Error* _err);
|
||||
|
||||
/// Release a region previously returned by `memoryMap`. Pages are decommitted and
|
||||
/// the reservation is returned to the OS.
|
||||
///
|
||||
/// @param[in] _address Base address returned by `memoryMap`.
|
||||
/// @param[in] _size Size in bytes of the region to release.
|
||||
/// @param[out] _err Error state.
|
||||
///
|
||||
void memoryUnmap(void* _address, size_t _size, Error* _err);
|
||||
|
||||
/// Query virtual memory page size.
|
||||
///
|
||||
size_t memoryPageSize();
|
||||
/// @param[in] _flags Pass `Memory::PageLarge` / `Memory::PageHuge` to query large/huge
|
||||
/// page sizes; 0 returns the default system page size.
|
||||
/// @returns Page size in bytes, or 0 when the requested page kind is unsupported.
|
||||
///
|
||||
size_t memoryPageSize(uint32_t _flags = 0);
|
||||
|
||||
} // namespace bx
|
||||
|
||||
|
||||
260
src/os.cpp
260
src/os.cpp
@@ -375,37 +375,228 @@ namespace bx
|
||||
#endif // BX_PLATFORM_*
|
||||
}
|
||||
|
||||
void* memoryMap(void* _address, size_t _size, Error* _err)
|
||||
namespace
|
||||
{
|
||||
#if BX_PLATFORM_LINUX || BX_PLATFORM_OSX
|
||||
static int32_t toPosixProt(uint32_t _protect)
|
||||
{
|
||||
switch (_protect)
|
||||
{
|
||||
case Memory::ProtectRead: return PROT_READ;
|
||||
case Memory::ProtectReadWrite: return PROT_READ | PROT_WRITE;
|
||||
case Memory::ProtectReadExec: return PROT_READ | PROT_EXEC;
|
||||
case Memory::ProtectNone: // fall through
|
||||
default: return PROT_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t toPosixAdvice(uint32_t _advise)
|
||||
{
|
||||
switch (_advise)
|
||||
{
|
||||
case Memory::Normal: return MADV_NORMAL;
|
||||
case Memory::SequentialAccess: return MADV_SEQUENTIAL;
|
||||
case Memory::RandomAccess: return MADV_RANDOM;
|
||||
case Memory::Prefetch: return MADV_WILLNEED;
|
||||
case Memory::DontNeed: return MADV_DONTNEED;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
#elif BX_PLATFORM_WINDOWS
|
||||
static DWORD toWinProtect(uint32_t _protect)
|
||||
{
|
||||
switch (_protect)
|
||||
{
|
||||
case Memory::ProtectRead: return PAGE_READONLY;
|
||||
case Memory::ProtectReadWrite: return PAGE_READWRITE;
|
||||
case Memory::ProtectReadExec: return PAGE_EXECUTE_READ;
|
||||
case Memory::ProtectNone: // fall through
|
||||
default: return PAGE_NOACCESS;
|
||||
}
|
||||
}
|
||||
#endif // BX_PLATFORM_*
|
||||
} // namespace
|
||||
|
||||
void* memoryMap(void* _address, size_t _size, size_t _alignment, uint32_t _flags, Error* _err)
|
||||
{
|
||||
BX_ERROR_SCOPE(_err);
|
||||
|
||||
const uint32_t state = _flags & Memory::StateMask;
|
||||
const uint32_t protect = _flags & Memory::ProtectMask;
|
||||
const uint32_t advise = _flags & Memory::AdviseMask;
|
||||
const uint32_t page = _flags & Memory::PageMask;
|
||||
|
||||
const size_t pageSize = memoryPageSize();
|
||||
BX_ASSERT(_alignment <= pageSize, "Alignments greater than the page size are not implemented (requested %zu, page %zu).", _alignment, pageSize);
|
||||
BX_UNUSED(_alignment, pageSize);
|
||||
|
||||
#if BX_PLATFORM_LINUX || BX_PLATFORM_OSX
|
||||
constexpr int32_t flags = 0
|
||||
| MAP_ANON
|
||||
| MAP_PRIVATE
|
||||
;
|
||||
|
||||
void* result = mmap(_address, _size, PROT_READ | PROT_WRITE, flags, -1 /*fd*/, 0 /*offset*/);
|
||||
|
||||
if (MAP_FAILED == result)
|
||||
if (NULL == _address)
|
||||
{
|
||||
BX_ERROR_SET(
|
||||
_err
|
||||
, kErrorMemoryMapFailed
|
||||
, "kErrorMemoryMapFailed"
|
||||
);
|
||||
|
||||
if (0 == (state & Memory::Reserve) )
|
||||
{
|
||||
BX_ERROR_SET(_err, kErrorMemoryMapFailed, "memoryMap: Memory::Reserve required for fresh allocation.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
#elif BX_PLATFORM_WINDOWS
|
||||
void* result = VirtualAlloc(_address, _size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
uint32_t effectiveProtect = protect;
|
||||
if (0 == effectiveProtect)
|
||||
{
|
||||
effectiveProtect = (Memory::Commit == (state & Memory::Commit) ) ? Memory::ProtectReadWrite : Memory::ProtectNone;
|
||||
}
|
||||
|
||||
int32_t mmapFlags = MAP_ANON | MAP_PRIVATE;
|
||||
# if BX_PLATFORM_LINUX
|
||||
if (0 != (page & Memory::PageHuge) )
|
||||
{
|
||||
mmapFlags |= MAP_HUGETLB;
|
||||
# ifdef MAP_HUGE_1GB
|
||||
mmapFlags |= MAP_HUGE_1GB;
|
||||
# endif // MAP_HUGE_1GB
|
||||
}
|
||||
else if (0 != (page & Memory::PageLarge) )
|
||||
{
|
||||
mmapFlags |= MAP_HUGETLB;
|
||||
# ifdef MAP_HUGE_2MB
|
||||
mmapFlags |= MAP_HUGE_2MB;
|
||||
# endif // MAP_HUGE_2MB
|
||||
}
|
||||
# else
|
||||
BX_UNUSED(page);
|
||||
# endif // BX_PLATFORM_LINUX
|
||||
|
||||
void* result = mmap(_address, _size, toPosixProt(effectiveProtect), mmapFlags, -1 /*fd*/, 0 /*offset*/);
|
||||
|
||||
if (MAP_FAILED == result)
|
||||
{
|
||||
BX_ERROR_SET(_err, kErrorMemoryMapFailed, "memoryMap: mmap failed.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (0 != advise)
|
||||
{
|
||||
const int32_t adv = toPosixAdvice(advise);
|
||||
if (-1 != adv)
|
||||
{
|
||||
madvise(result, _size, adv);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Reconfigure existing region.
|
||||
if (0 != (state & Memory::Decommit) )
|
||||
{
|
||||
// Drop physical pages, then revoke access so use-after-free faults.
|
||||
madvise(_address, _size, MADV_DONTNEED);
|
||||
const int32_t prot = (0 != protect) ? toPosixProt(protect) : PROT_NONE;
|
||||
if (0 != mprotect(_address, _size, prot) )
|
||||
{
|
||||
BX_ERROR_SET(_err, kErrorMemoryMapFailed, "memoryMap: mprotect (decommit) failed.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (Memory::Commit == (state & Memory::Commit) )
|
||||
{
|
||||
const uint32_t effectiveProtect = (0 != protect) ? protect : Memory::ProtectReadWrite;
|
||||
if (0 != mprotect(_address, _size, toPosixProt(effectiveProtect) ) )
|
||||
{
|
||||
BX_ERROR_SET(_err, kErrorMemoryMapFailed, "memoryMap: mprotect (commit) failed.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (0 != protect)
|
||||
{
|
||||
if (0 != mprotect(_address, _size, toPosixProt(protect) ) )
|
||||
{
|
||||
BX_ERROR_SET(_err, kErrorMemoryMapFailed, "memoryMap: mprotect failed.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != advise)
|
||||
{
|
||||
const int32_t adv = toPosixAdvice(advise);
|
||||
if (-1 != adv)
|
||||
{
|
||||
madvise(_address, _size, adv);
|
||||
}
|
||||
}
|
||||
|
||||
return _address;
|
||||
#elif BX_PLATFORM_WINDOWS
|
||||
if (NULL == _address)
|
||||
{
|
||||
if (0 == (state & Memory::Reserve) )
|
||||
{
|
||||
BX_ERROR_SET(_err, kErrorMemoryMapFailed, "memoryMap: Memory::Reserve required for fresh allocation.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DWORD allocType = MEM_RESERVE;
|
||||
DWORD winProtect = PAGE_NOACCESS;
|
||||
|
||||
if (Memory::Commit == (state & Memory::Commit) )
|
||||
{
|
||||
allocType |= MEM_COMMIT;
|
||||
winProtect = toWinProtect( (0 != protect) ? protect : Memory::ProtectReadWrite);
|
||||
}
|
||||
else if (0 != protect)
|
||||
{
|
||||
winProtect = toWinProtect(protect);
|
||||
}
|
||||
|
||||
if (0 != (page & (Memory::PageLarge | Memory::PageHuge) ) )
|
||||
{
|
||||
allocType |= MEM_LARGE_PAGES;
|
||||
}
|
||||
|
||||
void* result = VirtualAlloc(_address, _size, allocType, winProtect);
|
||||
|
||||
if (NULL == result)
|
||||
{
|
||||
BX_ERROR_SET(_err, kErrorMemoryMapFailed, "memoryMap: VirtualAlloc failed.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BX_UNUSED(advise);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Reconfigure existing region.
|
||||
if (0 != (state & Memory::Decommit) )
|
||||
{
|
||||
if (!VirtualFree(_address, _size, MEM_DECOMMIT) )
|
||||
{
|
||||
BX_ERROR_SET(_err, kErrorMemoryMapFailed, "memoryMap: VirtualFree(MEM_DECOMMIT) failed.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (Memory::Commit == (state & Memory::Commit) )
|
||||
{
|
||||
const DWORD winProtect = toWinProtect( (0 != protect) ? protect : Memory::ProtectReadWrite);
|
||||
if (NULL == VirtualAlloc(_address, _size, MEM_COMMIT, winProtect) )
|
||||
{
|
||||
BX_ERROR_SET(_err, kErrorMemoryMapFailed, "memoryMap: VirtualAlloc(MEM_COMMIT) failed.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (0 != protect)
|
||||
{
|
||||
DWORD oldProtect = 0;
|
||||
if (!VirtualProtect(_address, _size, toWinProtect(protect), &oldProtect) )
|
||||
{
|
||||
BX_ERROR_SET(_err, kErrorMemoryMapFailed, "memoryMap: VirtualProtect failed.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BX_UNUSED(advise);
|
||||
return _address;
|
||||
#else
|
||||
BX_UNUSED(_address, _size);
|
||||
BX_ERROR_SET(_err, kErrorMemoryMapFailed, "Not implemented!");
|
||||
BX_UNUSED(_address, _size, _alignment, _flags, state, protect, advise, page, pageSize);
|
||||
BX_ERROR_SET(_err, kErrorMemoryMapFailed, "memoryMap: Not implemented!");
|
||||
return NULL;
|
||||
#endif // BX_PLATFORM_*
|
||||
}
|
||||
@@ -426,7 +617,8 @@ namespace bx
|
||||
);
|
||||
}
|
||||
#elif BX_PLATFORM_WINDOWS
|
||||
if (!VirtualFree(_address, _size, MEM_RELEASE) )
|
||||
BX_UNUSED(_size);
|
||||
if (!VirtualFree(_address, 0, MEM_RELEASE) )
|
||||
{
|
||||
BX_ERROR_SET(
|
||||
_err
|
||||
@@ -440,21 +632,33 @@ namespace bx
|
||||
#endif // BX_PLATFORM_*
|
||||
}
|
||||
|
||||
size_t memoryPageSize()
|
||||
size_t memoryPageSize(uint32_t _flags)
|
||||
{
|
||||
size_t pageSize;
|
||||
const uint32_t page = _flags & Memory::PageMask;
|
||||
|
||||
#if BX_PLATFORM_LINUX || BX_PLATFORM_OSX
|
||||
pageSize = sysconf(_SC_PAGESIZE);
|
||||
if (0 != (page & Memory::PageHuge) )
|
||||
{
|
||||
return 1ull << 30; // 1 GiB nominal
|
||||
}
|
||||
if (0 != (page & Memory::PageLarge) )
|
||||
{
|
||||
return 2ull << 20; // 2 MiB nominal
|
||||
}
|
||||
return sysconf(_SC_PAGESIZE);
|
||||
#elif BX_PLATFORM_WINDOWS
|
||||
if (0 != (page & (Memory::PageLarge | Memory::PageHuge) ) )
|
||||
{
|
||||
return ::GetLargePageMinimum();
|
||||
}
|
||||
SYSTEM_INFO si;
|
||||
memSet(&si, 0, sizeof(si) );
|
||||
::GetSystemInfo(&si);
|
||||
pageSize = si.dwAllocationGranularity;
|
||||
return si.dwAllocationGranularity;
|
||||
#else
|
||||
pageSize = 16<<10;
|
||||
BX_UNUSED(page);
|
||||
return 16<<10;
|
||||
#endif // BX_PLATFORM_WINDOWS
|
||||
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
} // namespace bx
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <bx/semaphore.h>
|
||||
#include <bx/timer.h>
|
||||
|
||||
TEST_CASE("getProcessMemoryUsed", "")
|
||||
TEST_CASE("getProcessMemoryUsed", "[os]")
|
||||
{
|
||||
if (BX_ENABLED(BX_PLATFORM_EMSCRIPTEN) )
|
||||
{
|
||||
@@ -20,7 +20,7 @@ TEST_CASE("getProcessMemoryUsed", "")
|
||||
|
||||
#if BX_CONFIG_SUPPORTS_THREADING
|
||||
|
||||
TEST_CASE("semaphore_timeout", "")
|
||||
TEST_CASE("semaphore_timeout", "[os]")
|
||||
{
|
||||
bx::Semaphore sem;
|
||||
|
||||
@@ -34,3 +34,100 @@ TEST_CASE("semaphore_timeout", "")
|
||||
}
|
||||
|
||||
#endif // BX_CONFIG_SUPPORTS_THREADING
|
||||
|
||||
TEST_CASE("memoryPageSize", "[os]")
|
||||
{
|
||||
const size_t pageSize = bx::memoryPageSize();
|
||||
REQUIRE(0 != pageSize);
|
||||
// Page sizes are always powers of two on supported platforms.
|
||||
REQUIRE(0 == (pageSize & (pageSize - 1) ) );
|
||||
|
||||
// Large/huge page queries must not return a value smaller than the base page size.
|
||||
const size_t large = bx::memoryPageSize(bx::Memory::PageLarge);
|
||||
const size_t huge = bx::memoryPageSize(bx::Memory::PageHuge);
|
||||
REQUIRE( (0 == large || large >= pageSize) );
|
||||
REQUIRE( (0 == huge || huge >= pageSize) );
|
||||
}
|
||||
|
||||
TEST_CASE("memoryMap-readWrite-roundtrip", "[os]")
|
||||
{
|
||||
const size_t size = 4 * bx::memoryPageSize();
|
||||
|
||||
bx::Error err;
|
||||
void* addr = bx::memoryMap(NULL, size, 0, bx::Memory::ReadWrite, &err);
|
||||
REQUIRE(err.isOk() );
|
||||
REQUIRE(nullptr != addr);
|
||||
|
||||
uint8_t* bytes = (uint8_t*)addr;
|
||||
for (size_t ii = 0; ii < size; ++ii)
|
||||
{
|
||||
bytes[ii] = uint8_t(ii & 0xff);
|
||||
}
|
||||
for (size_t ii = 0; ii < size; ++ii)
|
||||
{
|
||||
REQUIRE(uint8_t(ii & 0xff) == bytes[ii]);
|
||||
}
|
||||
|
||||
bx::memoryUnmap(addr, size, &err);
|
||||
REQUIRE(err.isOk() );
|
||||
}
|
||||
|
||||
TEST_CASE("memoryMap-reserve-then-commit", "[os]")
|
||||
{
|
||||
const size_t pageSize = bx::memoryPageSize();
|
||||
const size_t size = 8 * pageSize;
|
||||
|
||||
bx::Error err;
|
||||
void* addr = bx::memoryMap(NULL, size, 0, bx::Memory::Reserve | bx::Memory::ProtectNone, &err);
|
||||
REQUIRE(err.isOk() );
|
||||
REQUIRE(nullptr != addr);
|
||||
|
||||
// Commit one page inside the reservation and write to it.
|
||||
uint8_t* bytes = (uint8_t*)addr;
|
||||
bx::memoryMap(bytes + pageSize, pageSize, 0, bx::Memory::Commit | bx::Memory::ProtectReadWrite, &err);
|
||||
REQUIRE(err.isOk() );
|
||||
|
||||
for (size_t ii = 0; ii < pageSize; ++ii)
|
||||
{
|
||||
bytes[pageSize + ii] = uint8_t( (ii * 7) & 0xff);
|
||||
}
|
||||
for (size_t ii = 0; ii < pageSize; ++ii)
|
||||
{
|
||||
REQUIRE(uint8_t( (ii * 7) & 0xff) == bytes[pageSize + ii]);
|
||||
}
|
||||
|
||||
// Decommit the page; reservation stays, but physical backing is dropped.
|
||||
bx::memoryMap(bytes + pageSize, pageSize, 0, bx::Memory::Decommit | bx::Memory::ProtectNone, &err);
|
||||
REQUIRE(err.isOk() );
|
||||
|
||||
// Release the whole reservation.
|
||||
bx::memoryUnmap(addr, size, &err);
|
||||
REQUIRE(err.isOk() );
|
||||
}
|
||||
|
||||
TEST_CASE("memoryMap-protection-change", "[os]")
|
||||
{
|
||||
const size_t size = bx::memoryPageSize();
|
||||
|
||||
bx::Error err;
|
||||
void* addr = bx::memoryMap(NULL, size, 0, bx::Memory::ReadWrite, &err);
|
||||
REQUIRE(err.isOk() );
|
||||
REQUIRE(nullptr != addr);
|
||||
|
||||
uint8_t* bytes = (uint8_t*)addr;
|
||||
bytes[0] = 0xab;
|
||||
REQUIRE(0xab == bytes[0]);
|
||||
|
||||
// Flip to read-only, then back to read/write. Neither call should fail.
|
||||
bx::memoryMap(addr, size, 0, bx::Memory::ProtectRead, &err);
|
||||
REQUIRE(err.isOk() );
|
||||
REQUIRE(0xab == bytes[0]);
|
||||
|
||||
bx::memoryMap(addr, size, 0, bx::Memory::ProtectReadWrite, &err);
|
||||
REQUIRE(err.isOk() );
|
||||
bytes[0] = 0xcd;
|
||||
REQUIRE(0xcd == bytes[0]);
|
||||
|
||||
bx::memoryUnmap(addr, size, &err);
|
||||
REQUIRE(err.isOk() );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user