use std::ptr;
use std::sync::atomic::Ordering::*;
use super::g::current_g;
use super::m::current_m;
use super::p::{PRUNNING, PSYSCALL};
use super::sched::{sched, startm};
pub(crate) unsafe fn entersyscall() {
let m = current_m();
if m.is_null() { return; }
let p = unsafe { (*m).p };
if p.is_null() { return; }
let old = unsafe {
(*p).status.compare_exchange(PRUNNING, PSYSCALL, AcqRel, Relaxed)
};
if old.is_err() { return; }
unsafe { (*p).syscalltick.fetch_add(1, Relaxed) };
unsafe {
(*m).oldp = p;
(*m).p = ptr::null_mut();
}
}
pub(crate) unsafe fn exitsyscall() {
let m = current_m();
if m.is_null() { return; }
let oldp = unsafe { (*m).oldp };
if !oldp.is_null() {
let ok = unsafe {
(*oldp)
.status
.compare_exchange(PSYSCALL, PRUNNING, AcqRel, Relaxed)
.is_ok()
};
if ok {
unsafe {
(*m).p = oldp;
(*m).oldp = ptr::null_mut();
(*oldp).m = m;
}
return; }
unsafe { (*m).oldp = ptr::null_mut() };
}
unsafe { exitsyscall0() };
}
unsafe fn exitsyscall0() {
let m = current_m();
let sc = sched();
let p = {
let mut inner = sc.inner.lock().unwrap();
let p = inner.idle_p;
if !p.is_null() {
inner.idle_p = unsafe { (*p).link };
unsafe { (*p).link = ptr::null_mut() };
}
p
};
if !p.is_null() {
unsafe {
(*p).status.store(PRUNNING, Release);
(*p).m = m;
(*m).p = p;
}
return;
}
let gp = current_g();
if !gp.is_null() {
unsafe {
(*gp).atomicstatus
.store(crate::runtime::g::GRUNNABLE, Release);
(*gp).m = ptr::null_mut();
}
sc.global_run_q.push_batch(gp, gp, 1);
unsafe { startm(ptr::null_mut()) };
}
unsafe { park_m_no_p(m) };
}
unsafe fn park_m_no_p(m: *mut super::m::M) {
let sc = sched();
{
let mut inner = sc.inner.lock().unwrap();
unsafe {
(*m).schedlink = inner.idle_m;
inner.idle_m = m;
inner.nmidle += 1;
}
}
unsafe { (*m).park_m() }; }
pub fn with_syscall<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
unsafe { entersyscall() };
let r = f();
unsafe { exitsyscall() };
r
}
#[cfg(all(test, not(loom)))]
mod tests {
use super::*;
use crate::runtime::p::{P, PIDLE, PRUNNING, PSYSCALL};
use crate::runtime::sched::run_impl;
use std::sync::atomic::Ordering::Relaxed;
use std::sync::Arc;
unsafe fn make_running_p() -> (*mut super::super::p::P, *mut super::super::m::M) {
use crate::runtime::m::M;
let m = Box::into_raw(unsafe { M::new(999) });
let p = Box::into_raw(P::new(0i32));
unsafe {
(*p).status.store(PRUNNING, Release);
(*p).m = m;
(*m).p = p;
}
(p, m)
}
#[test]
fn entersyscall_transitions_p() {
use crate::runtime::m::set_current_m;
let (p, m) = unsafe { make_running_p() };
unsafe { set_current_m(m) };
unsafe { entersyscall() };
assert_eq!(
unsafe { (*p).status.load(Relaxed) },
PSYSCALL,
"P must be PSYSCALL after entersyscall"
);
assert!(
unsafe { (*m).p.is_null() },
"M.p must be null after entersyscall"
);
assert_eq!(
unsafe { (*m).oldp },
p,
"M.oldp must point to the old P"
);
unsafe {
(*p).status.store(PRUNNING, Release);
(*m).p = p;
(*m).oldp = std::ptr::null_mut();
set_current_m(std::ptr::null_mut());
let _ = Box::from_raw(p);
let _ = Box::from_raw(m);
}
}
#[test]
fn exitsyscall_fast_path() {
use crate::runtime::m::set_current_m;
let (p, m) = unsafe { make_running_p() };
unsafe {
set_current_m(m);
(*p).status.store(PSYSCALL, Release);
(*m).oldp = p;
(*m).p = std::ptr::null_mut();
}
unsafe { exitsyscall() };
assert_eq!(
unsafe { (*p).status.load(Relaxed) },
PRUNNING,
"P must be PRUNNING after exitsyscall fast path"
);
assert_eq!(
unsafe { (*m).p },
p,
"M.p must be re-attached after exitsyscall fast path"
);
assert!(
unsafe { (*m).oldp.is_null() },
"M.oldp must be cleared"
);
unsafe {
set_current_m(std::ptr::null_mut());
let _ = Box::from_raw(p);
let _ = Box::from_raw(m);
}
}
#[test]
fn with_syscall_transparent() {
let result = with_syscall(|| 42_i32);
assert_eq!(result, 42);
}
#[test]
fn with_syscall_in_goroutine() {
use std::sync::atomic::{AtomicU32, Ordering};
let saw_psyscall = Arc::new(AtomicU32::new(0));
let saw2 = Arc::clone(&saw_psyscall);
run_impl(move || {
let status_during = with_syscall(|| {
let m = current_m();
if m.is_null() { return PIDLE; }
let p = unsafe { (*m).oldp };
if p.is_null() { return PIDLE; }
unsafe { (*p).status.load(Ordering::Acquire) }
});
saw2.store(status_during, Ordering::Relaxed);
});
assert_eq!(
saw_psyscall.load(std::sync::atomic::Ordering::Acquire),
PSYSCALL,
"P must be in PSYSCALL during with_syscall"
);
}
}