Skip to content

Commit 7895c88

Browse files
committed
feat: add Windows platform support
1 parent 94b75bf commit 7895c88

File tree

12 files changed

+432
-105
lines changed

12 files changed

+432
-105
lines changed

.github/scripts/ci-common.sh

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,12 @@ init_non_exclusive_features() {
6767
IFS='='; feature=($line); unset IFS;
6868
if [[ ! -z "$feature" ]]; then
6969
# Trim whitespaces
70-
features[i]=$(echo $feature)
70+
feature_name=$(echo $feature)
71+
# jemalloc does not support Windows
72+
if [[ $os == "windows" && $feature_name == "malloc_jemalloc" ]]; then
73+
continue
74+
fi
75+
features[i]=$feature_name
7176
let "i++"
7277
fi
7378
fi
@@ -118,6 +123,11 @@ init_exclusive_features() {
118123
if [[ ! -z "$feature" ]]; then
119124
# Trim whitespaces
120125
features[i]=$(echo $feature)
126+
# jemalloc does not support Windows
127+
if [[ $os == "windows" && $feature_name == "malloc_jemalloc" ]]; then
128+
continue
129+
fi
130+
features[i]=$feature_name
121131
let "i++"
122132
fi
123133
fi

.github/workflows/minimal-tests-core.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,16 @@ jobs:
3737
- { os: ubuntu-22.04, triple: x86_64-unknown-linux-gnu }
3838
- { os: ubuntu-22.04, triple: i686-unknown-linux-gnu }
3939
- { os: macos-15, triple: x86_64-apple-darwin }
40+
- { os: windows-latest, triple: x86_64-pc-windows-msvc }
4041
rust: ${{ fromJson(needs.setup-test-matrix.outputs.rust )}}
4142

4243
name: minimal-tests-core/${{ matrix.target.triple }}/${{ matrix.rust }}
4344
runs-on: ${{ matrix.target.os }}
4445

46+
defaults:
47+
run:
48+
shell: bash
49+
4550
env:
4651
# This determines the default target which cargo-build, cargo-test, etc. use.
4752
CARGO_BUILD_TARGET: "${{ matrix.target.triple }}"
@@ -69,6 +74,7 @@ jobs:
6974

7075
# Setup Environments
7176
- name: Setup Environments
77+
if: runner.os != 'Windows'
7278
run: ./.github/scripts/ci-setup-${{ matrix.target.triple }}.sh
7379

7480
# Build
@@ -88,11 +94,16 @@ jobs:
8894
- { os: ubuntu-22.04, triple: x86_64-unknown-linux-gnu }
8995
- { os: ubuntu-22.04, triple: i686-unknown-linux-gnu }
9096
- { os: macos-15, triple: x86_64-apple-darwin }
97+
- { os: windows-latest, triple: x86_64-pc-windows-msvc }
9198
rust: ${{ fromJson(needs.setup-test-matrix.outputs.rust )}}
9299

93100
name: style-check/${{ matrix.target.triple }}/${{ matrix.rust }}
94101
runs-on: ${{ matrix.target.os }}
95102

103+
defaults:
104+
run:
105+
shell: bash
106+
96107
env:
97108
# This determines the default target which cargo-build, cargo-test, etc. use.
98109
CARGO_BUILD_TARGET: "${{ matrix.target.triple }}"
@@ -120,6 +131,7 @@ jobs:
120131

121132
# Setup Environments
122133
- name: Setup Environments
134+
if: runner.os != 'Windows'
123135
run: ./.github/scripts/ci-setup-${{ matrix.target.triple }}.sh
124136

125137
# Style checks

Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ strum = "0.27.1"
5454
strum_macros = "0.27.1"
5555
sysinfo = "0.33.1"
5656

57+
[target.'cfg(windows)'.dependencies]
58+
windows-sys = { version = "0.61", features = [
59+
"Win32_Foundation",
60+
"Win32_System_Memory",
61+
"Win32_System_SystemInformation",
62+
"Win32_System_Threading",
63+
"Win32_System_Diagnostics_Debug",
64+
] }
65+
5766
[dev-dependencies]
5867
paste = "1.0.8"
5968
rand = "0.9.0"

src/policy/copyspace.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use crate::util::object_forwarding;
1313
use crate::util::{copy::*, object_enum};
1414
use crate::util::{Address, ObjectReference};
1515
use crate::vm::*;
16-
use libc::{mprotect, PROT_EXEC, PROT_NONE, PROT_READ, PROT_WRITE};
1716
use std::sync::atomic::{AtomicBool, Ordering};
1817
use std::sync::Arc;
1918

@@ -293,8 +292,8 @@ impl<VM: VMBinding> CopySpace<VM> {
293292
}
294293
let start = self.common().start;
295294
let extent = self.common().extent;
296-
unsafe {
297-
mprotect(start.to_mut_ptr(), extent, PROT_NONE);
295+
if let Err(e) = crate::util::memory::mprotect(start, extent) {
296+
panic!("Failed to protect memory: {:?}", e);
298297
}
299298
trace!("Protect {:x} {:x}", start, start + extent);
300299
}
@@ -308,12 +307,12 @@ impl<VM: VMBinding> CopySpace<VM> {
308307
}
309308
let start = self.common().start;
310309
let extent = self.common().extent;
311-
unsafe {
312-
mprotect(
313-
start.to_mut_ptr(),
314-
extent,
315-
PROT_READ | PROT_WRITE | PROT_EXEC,
316-
);
310+
if let Err(e) = crate::util::memory::munprotect(
311+
start,
312+
extent,
313+
crate::util::memory::MmapProtection::ReadWriteExec,
314+
) {
315+
panic!("Failed to unprotect memory: {:?}", e);
317316
}
318317
trace!("Unprotect {:x} {:x}", start, start + extent);
319318
}

src/scheduler/affinity.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,17 @@ pub fn get_total_num_cpus() -> u16 {
2020
}
2121
}
2222

23-
#[cfg(not(target_os = "linux"))]
23+
#[cfg(target_os = "windows")]
24+
/// Return the total number of cores allocated to the program.
25+
pub fn get_total_num_cpus() -> u16 {
26+
unsafe {
27+
windows_sys::Win32::System::Threading::GetActiveProcessorCount(
28+
windows_sys::Win32::System::Threading::ALL_PROCESSOR_GROUPS,
29+
) as u16
30+
}
31+
}
32+
33+
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
2434
/// Return the total number of cores allocated to the program.
2535
pub fn get_total_num_cpus() -> u16 {
2636
unimplemented!()
@@ -59,7 +69,18 @@ fn bind_current_thread_to_core(cpu: CoreId) {
5969
}
6070
}
6171

62-
#[cfg(not(target_os = "linux"))]
72+
#[cfg(target_os = "windows")]
73+
/// Bind the current thread to the specified core.
74+
fn bind_current_thread_to_core(cpu: CoreId) {
75+
unsafe {
76+
windows_sys::Win32::System::Threading::SetThreadAffinityMask(
77+
windows_sys::Win32::System::Threading::GetCurrentThread(),
78+
1 << cpu,
79+
);
80+
}
81+
}
82+
83+
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
6384
/// Bind the current thread to the specified core.
6485
fn bind_current_thread_to_core(_cpu: CoreId) {
6586
unimplemented!()
@@ -79,7 +100,22 @@ fn bind_current_thread_to_cpuset(cpuset: &[CoreId]) {
79100
}
80101
}
81102

82-
#[cfg(not(any(target_os = "linux", target_os = "android")))]
103+
#[cfg(target_os = "windows")]
104+
/// Bind the current thread to the specified core.
105+
fn bind_current_thread_to_cpuset(cpuset: &[CoreId]) {
106+
let mut mask = 0;
107+
for cpu in cpuset {
108+
mask |= 1 << cpu;
109+
}
110+
unsafe {
111+
windows_sys::Win32::System::Threading::SetThreadAffinityMask(
112+
windows_sys::Win32::System::Threading::GetCurrentThread(),
113+
mask,
114+
);
115+
}
116+
}
117+
118+
#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "windows")))]
83119
/// Bind the current thread to the specified core.
84120
fn bind_current_thread_to_cpuset(_cpuset: &[CoreId]) {
85121
unimplemented!()

src/util/malloc/library.rs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@
22

33
#[cfg(feature = "malloc_jemalloc")]
44
pub use self::jemalloc::*;
5-
#[cfg(not(any(feature = "malloc_jemalloc", feature = "malloc_mimalloc",)))]
5+
#[cfg(all(
6+
not(target_os = "windows"),
7+
not(any(feature = "malloc_jemalloc", feature = "malloc_mimalloc"))
8+
))]
69
pub use self::libc_malloc::*;
710
#[cfg(feature = "malloc_mimalloc")]
811
pub use self::mimalloc::*;
12+
#[cfg(all(
13+
target_os = "windows",
14+
not(any(feature = "malloc_jemalloc", feature = "malloc_mimalloc"))
15+
))]
16+
pub use self::win_malloc::*;
917

1018
/// When we count page usage of library malloc, we assume they allocate in pages. For some malloc implementations,
1119
/// they may use a larger page (e.g. mimalloc's 64K page). For libraries that we are not sure, we assume they use
@@ -43,7 +51,10 @@ mod mimalloc {
4351
}
4452

4553
/// If no malloc lib is specified, use the libc implementation
46-
#[cfg(not(any(feature = "malloc_jemalloc", feature = "malloc_mimalloc",)))]
54+
#[cfg(all(
55+
not(target_os = "windows"),
56+
not(any(feature = "malloc_jemalloc", feature = "malloc_mimalloc"))
57+
))]
4758
mod libc_malloc {
4859
// Normal 4K page
4960
pub const LOG_BYTES_IN_MALLOC_PAGE: u8 = crate::util::constants::LOG_BYTES_IN_PAGE;
@@ -61,3 +72,42 @@ mod libc_malloc {
6172
#[cfg(target_os = "macos")]
6273
pub use self::malloc_size as malloc_usable_size;
6374
}
75+
76+
/// Windows malloc implementation using HeapAlloc
77+
#[cfg(all(
78+
target_os = "windows",
79+
not(any(feature = "malloc_jemalloc", feature = "malloc_mimalloc"))
80+
))]
81+
mod win_malloc {
82+
// Normal 4K page
83+
pub const LOG_BYTES_IN_MALLOC_PAGE: u8 = crate::util::constants::LOG_BYTES_IN_PAGE;
84+
85+
use std::ffi::c_void;
86+
use windows_sys::Win32::System::Memory::*;
87+
88+
pub unsafe fn malloc(size: usize) -> *mut c_void {
89+
HeapAlloc(GetProcessHeap(), 0, size)
90+
}
91+
92+
pub unsafe fn free(ptr: *mut c_void) {
93+
if !ptr.is_null() {
94+
HeapFree(GetProcessHeap(), 0, ptr);
95+
}
96+
}
97+
98+
pub unsafe fn calloc(nmemb: usize, size: usize) -> *mut c_void {
99+
let total = nmemb * size;
100+
HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, total)
101+
}
102+
103+
pub unsafe fn realloc(ptr: *mut c_void, size: usize) -> *mut c_void {
104+
if ptr.is_null() {
105+
return malloc(size);
106+
}
107+
HeapReAlloc(GetProcessHeap(), 0, ptr, size)
108+
}
109+
110+
pub unsafe fn malloc_usable_size(ptr: *const c_void) -> usize {
111+
HeapSize(GetProcessHeap(), 0, ptr)
112+
}
113+
}

src/util/malloc/malloc_ms_util.rs

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ use crate::util::Address;
44
use crate::vm::VMBinding;
55

66
/// Allocate with alignment. This also guarantees the memory is zero initialized.
7+
#[cfg(all(
8+
not(target_os = "windows"),
9+
not(any(feature = "malloc_jemalloc", feature = "malloc_mimalloc"))
10+
))]
711
pub fn align_alloc(size: usize, align: usize) -> Address {
812
let mut ptr = std::ptr::null_mut::<libc::c_void>();
913
let ptr_ptr = std::ptr::addr_of_mut!(ptr);
@@ -71,28 +75,37 @@ pub fn alloc<VM: VMBinding>(size: usize, align: usize, offset: usize) -> (Addres
7175
let mut is_offset_malloc = false;
7276
// malloc returns 16 bytes aligned address.
7377
// So if the alignment is smaller than 16 bytes, we do not need to align.
74-
if align <= 16 && offset == 0 {
75-
let raw = unsafe { calloc(1, size) };
76-
address = Address::from_mut_ptr(raw);
77-
debug_assert!(address.is_aligned_to(align));
78-
} else if align > 16 && offset == 0 {
79-
address = align_alloc(size, align);
80-
debug_assert!(
81-
address.is_aligned_to(align),
82-
"Address: {:x} is not aligned to the given alignment: {}",
83-
address,
84-
align
85-
);
86-
} else {
87-
address = align_offset_alloc::<VM>(size, align, offset);
88-
is_offset_malloc = true;
89-
debug_assert!(
90-
(address + offset).is_aligned_to(align),
91-
"Address: {:x} is not aligned to the given alignment: {} at offset: {}",
92-
address,
93-
align,
94-
offset
95-
);
78+
79+
match (align, offset) {
80+
(a, 0) if a <= 16 => {
81+
let raw = unsafe { calloc(1, size) };
82+
address = Address::from_mut_ptr(raw);
83+
debug_assert!(address.is_aligned_to(align));
84+
}
85+
#[cfg(all(
86+
not(target_os = "windows"),
87+
not(any(feature = "malloc_jemalloc", feature = "malloc_mimalloc"))
88+
))]
89+
(a, 0) if a > 16 => {
90+
address = align_alloc(size, align);
91+
debug_assert!(
92+
address.is_aligned_to(align),
93+
"Address: {:x} is not aligned to the given alignment: {}",
94+
address,
95+
align
96+
);
97+
}
98+
_ => {
99+
address = align_offset_alloc::<VM>(size, align, offset);
100+
is_offset_malloc = true;
101+
debug_assert!(
102+
(address + offset).is_aligned_to(align),
103+
"Address: {:x} is not aligned to the given alignment: {} at offset: {}",
104+
address,
105+
align,
106+
offset
107+
);
108+
}
96109
}
97110
(address, is_offset_malloc)
98111
}

0 commit comments

Comments
 (0)