Skip to content

Commit 78e7f26

Browse files
committed
mmap for windows
1 parent c6086cb commit 78e7f26

File tree

1 file changed

+67
-29
lines changed

1 file changed

+67
-29
lines changed

crates/stdlib/src/mmap.rs

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,12 @@ mod mmap {
5353
| libc::MADV_SEQUENTIAL
5454
| libc::MADV_WILLNEED
5555
| libc::MADV_DONTNEED => Ok(advice),
56-
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios"))]
56+
#[cfg(any(
57+
target_os = "linux",
58+
target_os = "macos",
59+
target_os = "ios",
60+
target_os = "freebsd"
61+
))]
5762
libc::MADV_FREE => Ok(advice),
5863
#[cfg(target_os = "linux")]
5964
libc::MADV_DONTFORK
@@ -66,6 +71,12 @@ mod mmap {
6671
| libc::MADV_DONTDUMP
6772
| libc::MADV_DODUMP
6873
| libc::MADV_HWPOISON => Ok(advice),
74+
#[cfg(target_os = "freebsd")]
75+
libc::MADV_NOSYNC
76+
| libc::MADV_AUTOSYNC
77+
| libc::MADV_NOCORE
78+
| libc::MADV_CORE
79+
| libc::MADV_PROTECT => Ok(advice),
6980
_ => Err(vm.new_value_error("Not a valid Advice value")),
7081
}
7182
}
@@ -96,7 +107,7 @@ mod mmap {
96107
#[pyattr]
97108
use libc::{
98109
MADV_DONTNEED, MADV_NORMAL, MADV_RANDOM, MADV_SEQUENTIAL, MADV_WILLNEED, MAP_ANON,
99-
MAP_ANONYMOUS, MAP_PRIVATE, MAP_SHARED, PROT_READ, PROT_WRITE,
110+
MAP_ANONYMOUS, MAP_PRIVATE, MAP_SHARED, PROT_EXEC, PROT_READ, PROT_WRITE,
100111
};
101112

102113
#[cfg(target_os = "macos")]
@@ -146,6 +157,16 @@ mod mmap {
146157
#[pyattr]
147158
use libc::{MAP_DENYWRITE, MAP_EXECUTABLE, MAP_POPULATE};
148159

160+
// MAP_STACK is available on Linux, OpenBSD, and NetBSD
161+
#[cfg(any(target_os = "linux", target_os = "openbsd", target_os = "netbsd"))]
162+
#[pyattr]
163+
use libc::MAP_STACK;
164+
165+
// FreeBSD-specific MADV constants
166+
#[cfg(target_os = "freebsd")]
167+
#[pyattr]
168+
use libc::{MADV_AUTOSYNC, MADV_CORE, MADV_NOCORE, MADV_NOSYNC, MADV_PROTECT};
169+
149170
#[pyattr]
150171
const ACCESS_DEFAULT: u32 = AccessMode::Default as u32;
151172
#[pyattr]
@@ -404,6 +425,15 @@ mod mmap {
404425
};
405426

406427
let fd = unsafe { crt_fd::Borrowed::try_borrow_raw(fd) };
428+
429+
// macOS: Issue #11277: fsync(2) is not enough on OS X - a special, OS X specific
430+
// fcntl(2) is necessary to force DISKSYNC and get around mmap(2) bug
431+
#[cfg(target_os = "macos")]
432+
if let Ok(fd) = fd {
433+
use std::os::fd::AsRawFd;
434+
unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_FULLFSYNC) };
435+
}
436+
407437
if let Ok(fd) = fd {
408438
let metadata = fstat(fd)
409439
.map_err(|err| io::Error::from_raw_os_error(err as i32).to_pyexception(vm))?;
@@ -538,7 +568,10 @@ mod mmap {
538568
map_size = (file_len - offset) as usize;
539569
} else {
540570
// If map_size > file_len, extend the file (Windows behavior)
541-
let required_size = offset + map_size as i64;
571+
let required_size = offset.checked_add(map_size as i64).ok_or_else(|| {
572+
unsafe { CloseHandle(duplicated_handle) };
573+
vm.new_overflow_error("mmap size would cause file size overflow")
574+
})?;
542575
if required_size > file_len {
543576
// Extend file using SetFilePointerEx + SetEndOfFile
544577
let result = unsafe {
@@ -799,8 +832,9 @@ mod mmap {
799832

800833
let sub = &options.sub;
801834

835+
// returns start position for empty string
802836
if sub.is_empty() {
803-
return Ok(PyInt::from(0isize));
837+
return Ok(PyInt::from(start as isize));
804838
}
805839

806840
let mmap = self.check_valid(vm)?;
@@ -815,8 +849,9 @@ mod mmap {
815849
let (start, end) = self.get_find_range(options.clone());
816850

817851
let sub = &options.sub;
852+
// returns start position for empty string
818853
if sub.is_empty() {
819-
return Ok(PyInt::from(0isize));
854+
return Ok(PyInt::from(start as isize));
820855
}
821856

822857
let mmap = self.check_valid(vm)?;
@@ -850,18 +885,20 @@ mod mmap {
850885
#[cfg(all(unix, not(target_os = "redox")))]
851886
#[pymethod]
852887
fn madvise(&self, options: AdviseOptions, vm: &VirtualMachine) -> PyResult<()> {
853-
let (option, _start, _length) = options.values(self.__len__(), vm)?;
888+
let (option, start, length) = options.values(self.__len__(), vm)?;
854889
let advice = validate_advice(vm, option)?;
855890

856891
let guard = self.check_valid(vm)?;
857892
let mmap = guard.deref().as_ref().unwrap();
858-
let (ptr, len) = match mmap {
859-
MmapObj::Read(m) => (m.as_ptr(), m.len()),
860-
MmapObj::Write(m) => (m.as_ptr(), m.len()),
893+
let ptr = match mmap {
894+
MmapObj::Read(m) => m.as_ptr(),
895+
MmapObj::Write(m) => m.as_ptr(),
861896
};
862897

863-
// Call libc::madvise directly
864-
let result = unsafe { libc::madvise(ptr as *mut libc::c_void, len, advice) };
898+
// Apply madvise to the specified range (start, length)
899+
let ptr_with_offset = unsafe { ptr.add(start) };
900+
let result =
901+
unsafe { libc::madvise(ptr_with_offset as *mut libc::c_void, length, advice) };
865902
if result != 0 {
866903
return Err(io::Error::last_os_error().to_pyexception(vm));
867904
}
@@ -1062,30 +1099,14 @@ mod mmap {
10621099
if result == 0 {
10631100
// Restore original mmap on error
10641101
let err = io::Error::last_os_error();
1065-
let old_size = self.size.load();
1066-
if let Ok(mmap) = Self::create_mmap_windows(
1067-
handle as HANDLE,
1068-
self.offset,
1069-
old_size,
1070-
&self.access,
1071-
) {
1072-
*mmap_guard = Some(mmap);
1073-
}
1102+
self.try_restore_mmap(&mut mmap_guard, handle as HANDLE, self.size.load());
10741103
return Err(err.to_pyexception(vm));
10751104
}
10761105

10771106
let result = unsafe { SetEndOfFile(handle as HANDLE) };
10781107
if result == 0 {
10791108
let err = io::Error::last_os_error();
1080-
let old_size = self.size.load();
1081-
if let Ok(mmap) = Self::create_mmap_windows(
1082-
handle as HANDLE,
1083-
self.offset,
1084-
old_size,
1085-
&self.access,
1086-
) {
1087-
*mmap_guard = Some(mmap);
1088-
}
1109+
self.try_restore_mmap(&mut mmap_guard, handle as HANDLE, self.size.load());
10891110
return Err(err.to_pyexception(vm));
10901111
}
10911112

@@ -1248,6 +1269,12 @@ mod mmap {
12481269
fn __exit__(zelf: &Py<Self>, _args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
12491270
zelf.close(vm)
12501271
}
1272+
1273+
#[cfg(windows)]
1274+
#[pymethod]
1275+
fn __sizeof__(&self) -> usize {
1276+
std::mem::size_of::<Self>()
1277+
}
12511278
}
12521279

12531280
impl PyMmap {
@@ -1281,6 +1308,17 @@ mod mmap {
12811308
result
12821309
}
12831310

1311+
/// Try to restore mmap after a failed resize operation.
1312+
/// Returns true if restoration succeeded, false otherwise.
1313+
/// If restoration fails, marks the mmap as closed.
1314+
#[cfg(windows)]
1315+
fn try_restore_mmap(&self, mmap_guard: &mut Option<MmapObj>, handle: HANDLE, size: usize) {
1316+
match Self::create_mmap_windows(handle, self.offset, size, &self.access) {
1317+
Ok(mmap) => *mmap_guard = Some(mmap),
1318+
Err(_) => self.closed.store(true),
1319+
}
1320+
}
1321+
12841322
fn getitem_by_index(&self, i: isize, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
12851323
let i = i
12861324
.wrapped_at(self.__len__())

0 commit comments

Comments
 (0)