Base implementation
This commit is contained in:
commit
6ca1d4e306
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[registries]
|
||||||
|
landgrafhomyak = { index = "sparse+https://cargo.landgrafhomyak.ru/" }
|
||||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/.idea/
|
||||||
|
Cargo.lock
|
||||||
|
target/
|
||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "dynamic-memory-api-0"]
|
||||||
|
path = dynamic-memory-api-0
|
||||||
|
url = https://git.landgrafhomyak.ru/MemoryManagement/dynamic-memory-api-0.git
|
||||||
19
Cargo.toml
Normal file
19
Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
name = "virtual-memory-windows-0"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
rust = { nonstandard_style = "allow", unsafe_op_in_unsafe_fn = "allow" }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
dynamic-memory-api-0 = { version = "0.0.0", registry = "landgrafhomyak" }
|
||||||
|
|
||||||
|
[dependencies.windows]
|
||||||
|
version = ">=0.41.0, <=0.62.2"
|
||||||
|
registry = "crates-io"
|
||||||
|
features = ["Win32_System_SystemInformation", "Win32_System_Memory"]
|
||||||
|
|
||||||
|
[patch.landgrafhomyak]
|
||||||
|
dynamic-memory-api-0 = { path = "dynamic-memory-api-0" }
|
||||||
|
|
||||||
1
dynamic-memory-api-0
Submodule
1
dynamic-memory-api-0
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 4d5f71df4bf191255cfcf1f5ae82d8b8c2183aa9
|
||||||
58
src/api.rs
Normal file
58
src/api.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
use crate::reservation::WindowsReservation;
|
||||||
|
use crate::winapi_wrappers;
|
||||||
|
use dynamic_memory_api_0::virtual_memory::{ReservationToExtend, VirtualMemoryApi};
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
pub struct WindowsVirtualMemoryApi {
|
||||||
|
pageSize: usize,
|
||||||
|
totalReservationsOwned: AtomicUsize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowsVirtualMemoryApi {
|
||||||
|
pub fn startScope() -> Self {
|
||||||
|
return Self {
|
||||||
|
pageSize: winapi_wrappers::getPageSize(),
|
||||||
|
totalReservationsOwned: AtomicUsize::new(0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn changeReservationsCount(&self, diff: isize) {
|
||||||
|
let mut current = self.totalReservationsOwned.load(Ordering::Relaxed);
|
||||||
|
loop {
|
||||||
|
let updated = current.strict_add_signed(diff);
|
||||||
|
match self.totalReservationsOwned.compare_exchange_weak(
|
||||||
|
current,
|
||||||
|
updated,
|
||||||
|
Ordering::Relaxed,
|
||||||
|
Ordering::Relaxed,
|
||||||
|
) {
|
||||||
|
Ok(_) => return,
|
||||||
|
Err(new) => current = new,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl VirtualMemoryApi for WindowsVirtualMemoryApi {
|
||||||
|
type Reservation<'s> = WindowsReservation<'s>;
|
||||||
|
|
||||||
|
fn getPageSize(&self) -> usize {
|
||||||
|
return self.pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reserveMemory<'s>(
|
||||||
|
&'s self,
|
||||||
|
extend: ReservationToExtend<&Self::Reservation<'s>>,
|
||||||
|
pagesCount: usize,
|
||||||
|
) -> Option<Self::Reservation<'s>> {
|
||||||
|
return WindowsReservation::tryReserve(self, extend, pagesCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for WindowsVirtualMemoryApi {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.totalReservationsOwned.load(Ordering::Relaxed) > 0 {
|
||||||
|
panic!("Not all reservations got during this scope was released");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/lib.rs
Normal file
13
src/lib.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
extern crate core;
|
||||||
|
|
||||||
|
mod api;
|
||||||
|
mod reservation;
|
||||||
|
mod winapi_wrappers;
|
||||||
|
|
||||||
|
pub use api::WindowsVirtualMemoryApi;
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
mod _target_check {
|
||||||
|
use core::compile_error;
|
||||||
|
compile_error!("This crate can be compiled only for windows targets");
|
||||||
|
}
|
||||||
132
src/reservation.rs
Normal file
132
src/reservation.rs
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
use crate::{winapi_wrappers, WindowsVirtualMemoryApi};
|
||||||
|
use dynamic_memory_api_0::virtual_memory::{Reservation, ReservationToExtend, VirtualMemoryApi};
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::ffi::c_void;
|
||||||
|
use std::mem::ManuallyDrop;
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
use std::slice::from_raw_parts_mut;
|
||||||
|
use std::thread::panicking;
|
||||||
|
|
||||||
|
pub struct WindowsReservation<'s> {
|
||||||
|
owner: &'s WindowsVirtualMemoryApi,
|
||||||
|
start: NonNull<c_void>,
|
||||||
|
pagesCount: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s> WindowsReservation<'s> {
|
||||||
|
pub(super) fn tryReserve(
|
||||||
|
owner: &'s WindowsVirtualMemoryApi,
|
||||||
|
extend: ReservationToExtend<&WindowsReservation<'s>>,
|
||||||
|
pagesCount: usize,
|
||||||
|
) -> Option<Self> {
|
||||||
|
let start: Option<NonNull<c_void>>;
|
||||||
|
match extend {
|
||||||
|
ReservationToExtend::ReserveAnywhere => start = None,
|
||||||
|
ReservationToExtend::TryExtend(prev) => unsafe {
|
||||||
|
start = Some(prev._offset(prev.pagesCount))
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let ptr_o;
|
||||||
|
unsafe {
|
||||||
|
ptr_o = winapi_wrappers::reserve(start, owner.getPageSize() * pagesCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ptr;
|
||||||
|
match ptr_o {
|
||||||
|
None => return None,
|
||||||
|
Some(ptr_nn) => ptr = unsafe { NonNull::new_unchecked(ptr_nn.as_ptr()) },
|
||||||
|
}
|
||||||
|
|
||||||
|
owner.changeReservationsCount(1);
|
||||||
|
|
||||||
|
let reservation = WindowsReservation {
|
||||||
|
owner: owner,
|
||||||
|
start: ptr,
|
||||||
|
pagesCount: pagesCount,
|
||||||
|
};
|
||||||
|
|
||||||
|
return Some(reservation);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn _offset(&self, index: usize) -> NonNull<c_void> {
|
||||||
|
return self.start.add(self.owner.getPageSize() * index);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn _size(&self, start: usize, size: usize) -> usize {
|
||||||
|
return self
|
||||||
|
._offset(start + size)
|
||||||
|
.offset_from_unsigned(self._offset(start));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _assertBounds(&self, start: usize, size: usize) {
|
||||||
|
if start >= self.pagesCount {
|
||||||
|
panic!("First page's index out of bounds")
|
||||||
|
}
|
||||||
|
if size == 0 {
|
||||||
|
panic!("Zero pages commited")
|
||||||
|
}
|
||||||
|
if start + size >= self.pagesCount {
|
||||||
|
panic!("Last page's index out of bounds")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn _release(&mut self) {
|
||||||
|
winapi_wrappers::release(self.start, self._size(0, self.pagesCount));
|
||||||
|
self.owner.changeReservationsCount(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for WindowsReservation<'_> {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
return self.start.cmp(&other.start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for WindowsReservation<'_> {}
|
||||||
|
|
||||||
|
impl PartialEq<Self> for WindowsReservation<'_> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
return self.start.eq(&other.start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd<Self> for WindowsReservation<'_> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
return self.start.partial_cmp(&other.start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for WindowsReservation<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
self._release();
|
||||||
|
}
|
||||||
|
if !panicking() {
|
||||||
|
panic!("Reserved virtual memory wasn't released explicitly");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Reservation for WindowsReservation<'_> {
|
||||||
|
unsafe fn commitPages(&mut self, indexOfFirst: usize, count: usize) -> *mut [u8] {
|
||||||
|
self._assertBounds(indexOfFirst, count);
|
||||||
|
let start = self._offset(indexOfFirst);
|
||||||
|
let size = self._size(indexOfFirst, count);
|
||||||
|
winapi_wrappers::commit(start, size);
|
||||||
|
return from_raw_parts_mut(start.as_ptr().cast(), size);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn decommitPages(&mut self, indexOfFirst: usize, count: usize) {
|
||||||
|
self._assertBounds(indexOfFirst, count);
|
||||||
|
winapi_wrappers::decommit(
|
||||||
|
self._offset(indexOfFirst),
|
||||||
|
self._size(indexOfFirst, count),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn release(mut self) {
|
||||||
|
self._release();
|
||||||
|
let _ = ManuallyDrop::new(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
87
src/winapi_wrappers.rs
Normal file
87
src/winapi_wrappers.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
use std::cmp::max;
|
||||||
|
use std::ffi::c_void;
|
||||||
|
use std::ptr::{NonNull, null_mut};
|
||||||
|
use windows::Win32::Foundation::{
|
||||||
|
ERROR_INVALID_ADDRESS, ERROR_NOT_ENOUGH_MEMORY, GetLastError, WIN32_ERROR,
|
||||||
|
};
|
||||||
|
use windows::Win32::System::Memory::{
|
||||||
|
MEM_COMMIT, MEM_DECOMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_NOACCESS, PAGE_READWRITE,
|
||||||
|
VirtualAlloc, VirtualFree,
|
||||||
|
};
|
||||||
|
use windows::Win32::System::SystemInformation::{GetSystemInfo, SYSTEM_INFO};
|
||||||
|
|
||||||
|
pub(super) unsafe fn reserve(
|
||||||
|
start: Option<NonNull<c_void>>,
|
||||||
|
size: usize,
|
||||||
|
) -> Option<NonNull<c_void>> {
|
||||||
|
let raw = VirtualAlloc(
|
||||||
|
start.map(|p| p.as_ptr() as *const c_void),
|
||||||
|
size,
|
||||||
|
MEM_RESERVE,
|
||||||
|
PAGE_NOACCESS,
|
||||||
|
);
|
||||||
|
if raw == null_mut::<c_void>() {
|
||||||
|
match GetLastError() {
|
||||||
|
ERROR_NOT_ENOUGH_MEMORY => return None,
|
||||||
|
ERROR_INVALID_ADDRESS => {
|
||||||
|
panic!("Requested address already in use (ERROR_INVALID_ADDRESS)")
|
||||||
|
}
|
||||||
|
WIN32_ERROR(code) => panic!("Unexpected Win32 API Error: code={}", code),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return NonNull::new(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) unsafe fn commit(start: NonNull<c_void>, size: usize) {
|
||||||
|
let raw = VirtualAlloc(
|
||||||
|
Some(start.as_ptr() as *const c_void),
|
||||||
|
size,
|
||||||
|
MEM_COMMIT,
|
||||||
|
PAGE_READWRITE,
|
||||||
|
);
|
||||||
|
if raw == null_mut::<c_void>() {
|
||||||
|
match GetLastError() {
|
||||||
|
ERROR_INVALID_ADDRESS => {
|
||||||
|
panic!("Some of pages being commited aren't reserved (ERROR_INVALID_ADDRESS)")
|
||||||
|
}
|
||||||
|
WIN32_ERROR(code) => panic!("Unexpected Win32 API Error: code={}", code),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) unsafe fn decommit(start: NonNull<c_void>, size: usize) {
|
||||||
|
let result = VirtualFree(start.as_ptr(), size, MEM_DECOMMIT);
|
||||||
|
|
||||||
|
if result.is_err() {
|
||||||
|
match GetLastError() {
|
||||||
|
ERROR_INVALID_ADDRESS => {
|
||||||
|
panic!("Some of pages being decommited aren't commited (ERROR_INVALID_ADDRESS)")
|
||||||
|
}
|
||||||
|
WIN32_ERROR(code) => panic!("Unexpected Win32 API Error: code={}", code),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) unsafe fn release(start: NonNull<c_void>, size: usize) {
|
||||||
|
let result = VirtualFree(start.as_ptr(), size, MEM_RELEASE);
|
||||||
|
|
||||||
|
if result.is_err() {
|
||||||
|
match GetLastError() {
|
||||||
|
ERROR_INVALID_ADDRESS => {
|
||||||
|
panic!(
|
||||||
|
"Some of pages being released aren't reserved or still commited (ERROR_INVALID_ADDRESS)"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
WIN32_ERROR(code) => panic!("Unexpected Win32 API Error: code={}", code),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn getPageSize() -> usize {
|
||||||
|
let mut si = SYSTEM_INFO::default();
|
||||||
|
unsafe {
|
||||||
|
GetSystemInfo(&mut si as *mut _);
|
||||||
|
}
|
||||||
|
return max(si.dwPageSize, si.dwAllocationGranularity) as usize;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user