@@ -184,7 +184,8 @@ pub(crate) fn result_is_mapped(result: Result<()>) -> bool {
184184 Ok ( _) => false ,
185185 Err ( err) => {
186186 // ERROR_INVALID_ADDRESS may be returned if the address is already mapped or invalid
187- err. raw_os_error ( ) . unwrap ( ) == 487 // ERROR_INVALID_ADDRESS
187+ err. raw_os_error ( ) . unwrap ( )
188+ == windows_sys:: Win32 :: Foundation :: ERROR_INVALID_ADDRESS as i32
188189 }
189190 }
190191}
@@ -339,60 +340,101 @@ fn mmap_fixed(
339340
340341 #[ cfg( target_os = "windows" ) ]
341342 {
342- use windows_sys:: Win32 :: System :: Memory :: * ;
343+ use std:: io;
344+ use windows_sys:: Win32 :: System :: Memory :: {
345+ VirtualAlloc , VirtualQuery , MEMORY_BASIC_INFORMATION , MEM_COMMIT , MEM_FREE , MEM_RESERVE ,
346+ } ;
343347
344- let ptr = start. to_mut_ptr ( ) ;
348+ let ptr = start. to_mut_ptr ( ) as * mut u8 ;
345349 let prot = strategy. prot . into_native_flags ( ) ;
350+
351+ // Has to COMMIT inmediately if:
352+ // - not MAP_NORESERVE
353+ // - and protection is not NoAccess
346354 let commit =
347355 ( flags & MAP_NORESERVE ) == 0 && !matches ! ( strategy. prot, MmapProtection :: NoAccess ) ;
348356
349- // Query the state of the memory region
350- let mut mbi: MEMORY_BASIC_INFORMATION = unsafe { std:: mem:: zeroed ( ) } ;
351- let query_res = unsafe {
352- VirtualQuery (
353- ptr,
354- & mut mbi,
355- std:: mem:: size_of :: < MEMORY_BASIC_INFORMATION > ( ) ,
356- )
357- } ;
358- if query_res == 0 {
359- return Err ( std:: io:: Error :: last_os_error ( ) ) ;
360- }
357+ // Scan the region [ptr, ptr + size) to understand its current state
358+ unsafe {
359+ let mut addr = ptr;
360+ let end = ptr. add ( size) ;
361+
362+ let mut saw_free = false ;
363+ let mut saw_reserved = false ;
364+ let mut saw_committed = false ;
365+
366+ while addr < end {
367+ let mut mbi: MEMORY_BASIC_INFORMATION = std:: mem:: zeroed ( ) ;
368+ let q = VirtualQuery (
369+ addr as * const _ ,
370+ & mut mbi,
371+ std:: mem:: size_of :: < MEMORY_BASIC_INFORMATION > ( ) ,
372+ ) ;
373+ if q == 0 {
374+ return Err ( io:: Error :: last_os_error ( ) . into ( ) ) ;
375+ }
361376
362- let res = unsafe {
363- match mbi. State {
364- MEM_FREE => {
365- let mut allocation_type = MEM_RESERVE ;
366- if commit {
367- allocation_type |= MEM_COMMIT ;
377+ let region_base = mbi. BaseAddress as * mut u8 ;
378+ let region_size = mbi. RegionSize ;
379+ let region_end = region_base. add ( region_size) ;
380+
381+ // Calculate the intersection of [addr, end) and [region_base, region_end)
382+ let _sub_begin = if addr > region_base {
383+ addr
384+ } else {
385+ region_base
386+ } ;
387+ let _sub_end = if end < region_end { end } else { region_end } ;
388+
389+ match mbi. State {
390+ MEM_FREE => saw_free = true ,
391+ MEM_RESERVE => saw_reserved = true ,
392+ MEM_COMMIT => saw_committed = true ,
393+ _ => {
394+ return Err (
395+ io:: Error :: other ( "Unexpected memory state in mmap_fixed" ) . into ( )
396+ ) ;
368397 }
369- VirtualAlloc ( ptr, size, allocation_type, prot)
370398 }
371- MEM_RESERVE => {
372- if commit {
373- VirtualAlloc ( ptr, size, MEM_COMMIT , prot)
374- } else {
375- // Already reserved, nothing to do
376- ptr
377- }
399+
400+ // Jump to the next region (VirtualQuery always returns "continuous regions with the same attributes")
401+ addr = region_end;
402+ }
403+
404+ // 1. All FREE: make a new mapping in the region
405+ // 2. All RESERVE/COMMIT: treat as an existing mapping, can just COMMIT or succeed directly
406+ // 3. MIX of FREE + others: not allowed (semantically similar to MAP_FIXED_NOREPLACE)
407+ if saw_free && ( saw_reserved || saw_committed) {
408+ return Err ( io:: Error :: other (
409+ "mmap_fixed: region partially free and partially in use on Windows" ,
410+ ) ) ;
411+ }
412+
413+ if saw_free && !saw_reserved && !saw_committed {
414+ // All FREE: make a new mapping in the region
415+ let mut allocation_type = MEM_RESERVE ;
416+ if commit {
417+ allocation_type |= MEM_COMMIT ;
378418 }
379- MEM_COMMIT => {
380- // Already committed, just return success.
381- // We could change protection here if needed, but VirtualAlloc returns success
382- // if the memory is already committed.
383- ptr
419+
420+ let res = VirtualAlloc ( ptr as * mut _ , size, allocation_type, prot) ;
421+ if res. is_null ( ) {
422+ return Err ( io:: Error :: last_os_error ( ) ) ;
384423 }
385- _ => {
386- // Should not happen
387- return Err ( std:: io:: Error :: other ( "Unexpected memory state" ) ) ;
424+
425+ Ok ( ( ) )
426+ } else {
427+ // This behavior is similar to mmap with MAP_FIXED on Linux.
428+ // If the region is already mapped, we just ensure the required commitment.
429+ // If commit is not needed, we just return Ok.
430+ if commit {
431+ let res = VirtualAlloc ( ptr as * mut _ , size, MEM_COMMIT , prot) ;
432+ if res. is_null ( ) {
433+ return Err ( io:: Error :: last_os_error ( ) ) ;
434+ }
388435 }
436+ Ok ( ( ) )
389437 }
390- } ;
391-
392- if res. is_null ( ) {
393- Err ( std:: io:: Error :: last_os_error ( ) )
394- } else {
395- Ok ( ( ) )
396438 }
397439 }
398440}
@@ -459,7 +501,7 @@ pub fn handle_mmap_error<VM: VMBinding>(
459501 unreachable ! ( )
460502 }
461503 #[ cfg( target_os = "windows" ) ]
462- if os_errno == 8 {
504+ if os_errno == windows_sys :: Win32 :: Foundation :: ERROR_NOT_ENOUGH_MEMORY as i32 {
463505 // ERROR_NOT_ENOUGH_MEMORY
464506 trace ! ( "Signal MmapOutOfMemory!" ) ;
465507 VM :: VMCollection :: out_of_memory ( tls, AllocationError :: MmapOutOfMemory ) ;
@@ -474,7 +516,7 @@ pub fn handle_mmap_error<VM: VMBinding>(
474516 #[ cfg( target_os = "windows" ) ]
475517 if let Some ( os_errno) = error. raw_os_error ( ) {
476518 // If it is invalid address, we provide a more specific panic message.
477- if os_errno == 487 {
519+ if os_errno == windows_sys :: Win32 :: Foundation :: ERROR_INVALID_ADDRESS as i32 {
478520 // ERROR_INVALID_ADDRESS
479521 trace ! ( "Signal MmapOutOfMemory!" ) ;
480522 VM :: VMCollection :: out_of_memory ( tls, AllocationError :: MmapOutOfMemory ) ;
0 commit comments