mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-05 05:38:31 +00:00
This is a workaround for a lack of an ability to tell TSan that it should clear any state it has about certain memory ranges to prevent false positives.
108 lines
3.8 KiB
Odin
108 lines
3.8 KiB
Odin
package runtime
|
|
|
|
ODIN_VIRTUAL_MEMORY_SUPPORTED :: VIRTUAL_MEMORY_SUPPORTED
|
|
|
|
// Virtually all MMUs supported by Odin should have a 4KiB page size.
|
|
PAGE_SIZE :: 4 * Kilobyte
|
|
|
|
when ODIN_ARCH == .arm32 {
|
|
SUPERPAGE_SIZE :: 1 * Megabyte
|
|
} else {
|
|
// All other architectures should have support for 2MiB pages.
|
|
// i386 supports it in PAE mode.
|
|
// amd64, arm64, and riscv64 support it by default.
|
|
SUPERPAGE_SIZE :: 2 * Megabyte
|
|
}
|
|
|
|
#assert(SUPERPAGE_SIZE & (SUPERPAGE_SIZE-1) == 0, "SUPERPAGE_SIZE must be a power of two.")
|
|
|
|
/*
|
|
Allocate virtual memory from the operating system.
|
|
|
|
The address returned is guaranteed to point to data that is at least `size`
|
|
bytes large but may be larger, due to rounding `size` to the page size of the
|
|
system.
|
|
*/
|
|
@(require_results)
|
|
allocate_virtual_memory :: proc "contextless" (size: int) -> rawptr {
|
|
return _allocate_virtual_memory(size)
|
|
}
|
|
|
|
/*
|
|
Allocate a superpage of virtual memory from the operating system.
|
|
|
|
This is a contiguous block of memory larger than what is normally distributed
|
|
by the operating system, sometimes with special performance properties related
|
|
to the Translation Lookaside Buffer.
|
|
|
|
The address will be a multiple of the `SUPERPAGE_SIZE` constant, and the memory
|
|
pointed to will be at least as long as that very same constant.
|
|
|
|
The name derives from the superpage concept on the *BSD operating systems,
|
|
where it is known as huge pages on Linux and large pages on Windows.
|
|
*/
|
|
@(require_results)
|
|
allocate_virtual_memory_superpage :: proc "contextless" () -> rawptr {
|
|
return _allocate_virtual_memory_superpage()
|
|
}
|
|
|
|
/*
|
|
Allocate virtual memory from the operating system.
|
|
|
|
The address returned is guaranteed to be a multiple of `alignment` and point to
|
|
data that is at least `size` bytes large but may be larger, due to rounding
|
|
`size` to the page size of the system.
|
|
|
|
`alignment` must be a power of two.
|
|
*/
|
|
@(require_results)
|
|
allocate_virtual_memory_aligned :: proc "contextless" (size: int, alignment: int) -> rawptr {
|
|
assert_contextless(is_power_of_two(alignment))
|
|
return _allocate_virtual_memory_aligned(size, alignment)
|
|
}
|
|
|
|
/*
|
|
Free virtual memory allocated by any of the `allocate_*` procs.
|
|
*/
|
|
free_virtual_memory :: proc "contextless" (ptr: rawptr, size: int) {
|
|
// TODO: There is currently no good way to tell ThreadSanitizer that we're
|
|
// done with a region of memory and to clear any information it has about
|
|
// it, so that when it's enabled, we simply do not release any memory back
|
|
// to the operating system.
|
|
//
|
|
// This prevents all false positive warnings when one thread inevitably
|
|
// gives up some of its memory that is then re-assigned to a different
|
|
// thread by the operating system.
|
|
//
|
|
// This is a workaround for the time being.
|
|
when .Thread not_in ODIN_SANITIZER_FLAGS {
|
|
_free_virtual_memory(ptr, size)
|
|
}
|
|
}
|
|
|
|
/*
|
|
Resize virtual memory allocated by `allocate_virtual_memory`.
|
|
|
|
**Caveats:**
|
|
|
|
- `new_size` must not be zero.
|
|
- If `old_size` and `new_size` are the same, nothing happens.
|
|
- The resulting behavior is undefined if `old_size` is incorrect.
|
|
- If the address is changed, `alignment` will be ensured.
|
|
- `alignment` should be the same value used when the memory was allocated.
|
|
- Resizing memory returned by `allocate_virtual_memory_superpage` is not
|
|
well-defined. The memory may be resized, but it may no longer be backed by a
|
|
superpage.
|
|
*/
|
|
@(require_results)
|
|
resize_virtual_memory :: proc "contextless" (ptr: rawptr, old_size: int, new_size: int, alignment: int = 0) -> rawptr {
|
|
// * This is due to a restriction of mremap on Linux.
|
|
assert_contextless(new_size != 0, "Cannot resize virtual memory address to zero.")
|
|
// * The statement about undefined behavior of incorrect `old_size` is due to
|
|
// how VirtualFree works on Windows.
|
|
if old_size == new_size {
|
|
return ptr
|
|
}
|
|
return _resize_virtual_memory(ptr, old_size, new_size, alignment)
|
|
}
|