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