modify_comment
This commit is contained in:
parent
f54eabfa92
commit
3724a06714
@ -21,6 +21,11 @@ use sgx_tse::*;
|
|||||||
pub static mut INSTANCE_DIR: String = String::new();
|
pub static mut INSTANCE_DIR: String = String::new();
|
||||||
static mut ENCLAVE_PATH: String = String::new();
|
static mut ENCLAVE_PATH: String = String::new();
|
||||||
|
|
||||||
|
/// Note about memory ordering:
|
||||||
|
/// HAS_INIT need to synchronize the relevant resources in interrupt::init().
|
||||||
|
/// The read operation of HAS_INIT needs to see the change of the resources.
|
||||||
|
/// Just `Acquire` or `Release` needs to be used to make all the change of the
|
||||||
|
/// wakers visible to us.
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref INIT_ONCE: Once = Once::new();
|
static ref INIT_ONCE: Once = Once::new();
|
||||||
static ref HAS_INIT: AtomicBool = AtomicBool::new(false);
|
static ref HAS_INIT: AtomicBool = AtomicBool::new(false);
|
||||||
@ -49,7 +54,7 @@ pub extern "C" fn occlum_ecall_init(
|
|||||||
instance_dir: *const c_char,
|
instance_dir: *const c_char,
|
||||||
file_buffer: *const host_file_buffer,
|
file_buffer: *const host_file_buffer,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
if HAS_INIT.load(Ordering::SeqCst) == true {
|
if HAS_INIT.load(Ordering::Acquire) == true {
|
||||||
return ecall_errno!(EEXIST);
|
return ecall_errno!(EEXIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +106,7 @@ pub extern "C" fn occlum_ecall_init(
|
|||||||
|
|
||||||
interrupt::init();
|
interrupt::init();
|
||||||
|
|
||||||
HAS_INIT.store(true, Ordering::SeqCst);
|
HAS_INIT.store(true, Ordering::Release);
|
||||||
|
|
||||||
// Init boot up time stamp here.
|
// Init boot up time stamp here.
|
||||||
time::up_time::init();
|
time::up_time::init();
|
||||||
@ -135,7 +140,7 @@ pub extern "C" fn occlum_ecall_new_process(
|
|||||||
env: *const *const c_char,
|
env: *const *const c_char,
|
||||||
host_stdio_fds: *const HostStdioFds,
|
host_stdio_fds: *const HostStdioFds,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
if HAS_INIT.load(Ordering::SeqCst) == false {
|
if HAS_INIT.load(Ordering::Acquire) == false {
|
||||||
return ecall_errno!(EAGAIN);
|
return ecall_errno!(EAGAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +169,7 @@ pub extern "C" fn occlum_ecall_new_process(
|
|||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn occlum_ecall_exec_thread(libos_pid: i32, host_tid: i32) -> i32 {
|
pub extern "C" fn occlum_ecall_exec_thread(libos_pid: i32, host_tid: i32) -> i32 {
|
||||||
if HAS_INIT.load(Ordering::SeqCst) == false {
|
if HAS_INIT.load(Ordering::Acquire) == false {
|
||||||
return ecall_errno!(EAGAIN);
|
return ecall_errno!(EAGAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +189,7 @@ pub extern "C" fn occlum_ecall_exec_thread(libos_pid: i32, host_tid: i32) -> i32
|
|||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn occlum_ecall_kill(pid: i32, sig: i32) -> i32 {
|
pub extern "C" fn occlum_ecall_kill(pid: i32, sig: i32) -> i32 {
|
||||||
if HAS_INIT.load(Ordering::SeqCst) == false {
|
if HAS_INIT.load(Ordering::Acquire) == false {
|
||||||
return ecall_errno!(EAGAIN);
|
return ecall_errno!(EAGAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,7 +207,7 @@ pub extern "C" fn occlum_ecall_kill(pid: i32, sig: i32) -> i32 {
|
|||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn occlum_ecall_broadcast_interrupts() -> i32 {
|
pub extern "C" fn occlum_ecall_broadcast_interrupts() -> i32 {
|
||||||
if HAS_INIT.load(Ordering::SeqCst) == false {
|
if HAS_INIT.load(Ordering::Acquire) == false {
|
||||||
return ecall_errno!(EAGAIN);
|
return ecall_errno!(EAGAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +101,35 @@ impl Waker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Instruction rearrangement about control dependency
|
||||||
|
///
|
||||||
|
/// Such as the following code:
|
||||||
|
/// fn function(flag: bool, a: i32, b: i32) {
|
||||||
|
/// if flag { // 1
|
||||||
|
/// let i = a * b; // 2
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// Guidelines for compilation optimization:without changing the single-threaded semantics
|
||||||
|
/// of the program, the execution order of statements can be rearranged. There is a control
|
||||||
|
/// dependency between flag and i. When the instruction is reordered, step 2 will write the
|
||||||
|
/// result value to the hardware cache, and when judged to be true, the result value will be
|
||||||
|
/// written to the variable i. Therefore, controlling dependency does not prevent compiler
|
||||||
|
/// optimizations
|
||||||
|
///
|
||||||
|
/// Note about memory ordering:
|
||||||
|
/// Here is_woken needs to be synchronized with host_eventfd. The read operation of
|
||||||
|
/// is_woken needs to see the change of the host_eventfd field. Just `Acquire` or
|
||||||
|
/// `Release` needs to be used to make all the change of the host_eventfd visible to us.
|
||||||
|
///
|
||||||
|
/// The ordering in CAS operations can be `Relaxed`, `Acquire`, `AcqRel` or `SeqCst`,
|
||||||
|
/// The key is to consider the specific usage scenario. Here fail does not synchronize other
|
||||||
|
/// variables in the CAS operation, which can use `Relaxed`, and the host_enent needs
|
||||||
|
/// to be synchronized in success, so `Acquire` needs to be used so that we can see all the
|
||||||
|
/// changes in the host_eventfd after that.
|
||||||
|
///
|
||||||
|
/// Although it is correct to use AcqRel, here I think it is okay to use Acquire, because
|
||||||
|
/// you don't need to synchronize host_event before is_woken, only later.
|
||||||
struct Inner {
|
struct Inner {
|
||||||
is_woken: AtomicBool,
|
is_woken: AtomicBool,
|
||||||
host_eventfd: Arc<HostEventFd>,
|
host_eventfd: Arc<HostEventFd>,
|
||||||
@ -117,11 +146,11 @@ impl Inner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_woken(&self) -> bool {
|
pub fn is_woken(&self) -> bool {
|
||||||
self.is_woken.load(Ordering::SeqCst)
|
self.is_woken.load(Ordering::Acquire)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&self) {
|
pub fn reset(&self) {
|
||||||
self.is_woken.store(false, Ordering::SeqCst);
|
self.is_woken.store(false, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait(&self, timeout: Option<&Duration>) -> Result<()> {
|
pub fn wait(&self, timeout: Option<&Duration>) -> Result<()> {
|
||||||
@ -154,7 +183,7 @@ impl Inner {
|
|||||||
pub fn wake(&self) {
|
pub fn wake(&self) {
|
||||||
if self
|
if self
|
||||||
.is_woken
|
.is_woken
|
||||||
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
|
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
self.host_eventfd.write_u64(1);
|
self.host_eventfd.write_u64(1);
|
||||||
@ -167,7 +196,7 @@ impl Inner {
|
|||||||
.filter(|inner| {
|
.filter(|inner| {
|
||||||
inner
|
inner
|
||||||
.is_woken
|
.is_woken
|
||||||
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
|
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
})
|
})
|
||||||
.map(|inner| inner.host_eventfd.host_fd())
|
.map(|inner| inner.host_eventfd.host_fd())
|
||||||
|
@ -11,6 +11,21 @@ use crate::prelude::*;
|
|||||||
///
|
///
|
||||||
/// While the queue is conceptually for `Waiter`s, it internally maintains a list
|
/// While the queue is conceptually for `Waiter`s, it internally maintains a list
|
||||||
/// of `Waker`s.
|
/// of `Waker`s.
|
||||||
|
///
|
||||||
|
/// Note about memory ordering:
|
||||||
|
/// Here count needs to be synchronized with wakers. The read operation of count
|
||||||
|
/// needs to see the change of the waker field. Just `Acquire` or `Release` needs
|
||||||
|
/// to be used to make all the change of the wakers visible to us.
|
||||||
|
///
|
||||||
|
/// Regarding the usage of functions like fetch_add and fetch_sub, they perform
|
||||||
|
/// atomic addition or subtraction operations. The memory ordering parameter for
|
||||||
|
/// these functions can be chosen from options such as `Relaxed`, `Acquire`, `Release`,
|
||||||
|
/// `AcqRel` and `SeqCst`. It is important to select the appropriate memory ordering
|
||||||
|
/// based on the corresponding usage scenario.
|
||||||
|
///
|
||||||
|
/// In this code snippet, the count variable is synchronized with the wakers field.
|
||||||
|
/// In this case, we only need to ensure that waker.lock() occurs before count.
|
||||||
|
/// Although it is safer to use AcqRel,here using `Release` would be enough.
|
||||||
pub struct WaiterQueue {
|
pub struct WaiterQueue {
|
||||||
count: AtomicUsize,
|
count: AtomicUsize,
|
||||||
wakers: SgxMutex<VecDeque<Waker>>,
|
wakers: SgxMutex<VecDeque<Waker>>,
|
||||||
@ -27,7 +42,9 @@ impl WaiterQueue {
|
|||||||
|
|
||||||
/// Returns whether the queue is empty.
|
/// Returns whether the queue is empty.
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.count.load(Ordering::SeqCst) == 0
|
// Here is_empty function is only used in line 76 below. And when calling this, it
|
||||||
|
// doesn't need to synchronize with the wakers. Therefore, Relaxed can be enough.
|
||||||
|
self.count.load(Ordering::Relaxed) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset a waiter and enqueue it.
|
/// Reset a waiter and enqueue it.
|
||||||
@ -39,7 +56,7 @@ impl WaiterQueue {
|
|||||||
waiter.reset();
|
waiter.reset();
|
||||||
|
|
||||||
let mut wakers = self.wakers.lock().unwrap();
|
let mut wakers = self.wakers.lock().unwrap();
|
||||||
self.count.fetch_add(1, Ordering::SeqCst);
|
self.count.fetch_add(1, Ordering::Release);
|
||||||
wakers.push_back(waiter.waker());
|
wakers.push_back(waiter.waker());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +82,7 @@ impl WaiterQueue {
|
|||||||
let mut wakers = self.wakers.lock().unwrap();
|
let mut wakers = self.wakers.lock().unwrap();
|
||||||
let max_count = max_count.min(wakers.len());
|
let max_count = max_count.min(wakers.len());
|
||||||
let to_wake: Vec<Waker> = wakers.drain(..max_count).collect();
|
let to_wake: Vec<Waker> = wakers.drain(..max_count).collect();
|
||||||
self.count.fetch_sub(to_wake.len(), Ordering::SeqCst);
|
self.count.fetch_sub(to_wake.len(), Ordering::Release);
|
||||||
to_wake
|
to_wake
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -282,10 +282,12 @@ impl EpollFile {
|
|||||||
// A critical section protected by the lock of self.interest
|
// A critical section protected by the lock of self.interest
|
||||||
{
|
{
|
||||||
let mut interest_entries = self.interest.lock().unwrap();
|
let mut interest_entries = self.interest.lock().unwrap();
|
||||||
|
// There is a data-dependency, so this cannot be re-ordered,
|
||||||
|
// `Relaxed` should be enough.
|
||||||
let ep_entry = interest_entries
|
let ep_entry = interest_entries
|
||||||
.remove(&fd)
|
.remove(&fd)
|
||||||
.ok_or_else(|| errno!(ENOENT, "fd is not added"))?;
|
.ok_or_else(|| errno!(ENOENT, "fd is not added"))?;
|
||||||
ep_entry.is_deleted.store(true, Ordering::Release);
|
ep_entry.is_deleted.store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
let notifier = ep_entry.file.notifier().unwrap();
|
let notifier = ep_entry.file.notifier().unwrap();
|
||||||
let weak_observer = self.weak_self.clone() as Weak<dyn Observer<_>>;
|
let weak_observer = self.weak_self.clone() as Weak<dyn Observer<_>>;
|
||||||
@ -501,6 +503,11 @@ impl AsEpollFile for FileRef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Note about memory ordering:
|
||||||
|
/// Here is_deleted needs to be synchronized with entry. The read
|
||||||
|
/// operation of is_deleted needs to see the entry has been deleted
|
||||||
|
/// from the interest list. Just `Acquire` or `Release` needs to be
|
||||||
|
/// used to make all the change of the wakers visible to us.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct EpollEntry {
|
struct EpollEntry {
|
||||||
fd: FileDesc,
|
fd: FileDesc,
|
||||||
|
@ -420,6 +420,10 @@ impl FutexBucketVec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Note about memory ordering:
|
||||||
|
/// Here is_woken needs to be synchronized with thread. The read operation of is_woken
|
||||||
|
/// needs to see the change of the thread field. Just `Acquire` or `Release` needs
|
||||||
|
/// to be used to make all the change of the thread visible to us.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Waiter {
|
struct Waiter {
|
||||||
thread: *const c_void,
|
thread: *const c_void,
|
||||||
@ -441,9 +445,9 @@ impl Waiter {
|
|||||||
if current != self.thread {
|
if current != self.thread {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
while self.is_woken.load(Ordering::SeqCst) == false {
|
while self.is_woken.load(Ordering::Acquire) == false {
|
||||||
if let Err(e) = wait_event_timeout(self.thread, timeout) {
|
if let Err(e) = wait_event_timeout(self.thread, timeout) {
|
||||||
self.is_woken.store(true, Ordering::SeqCst);
|
self.is_woken.store(true, Ordering::Release);
|
||||||
return_errno!(e.errno(), "wait_timeout error");
|
return_errno!(e.errno(), "wait_timeout error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -451,7 +455,7 @@ impl Waiter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn wake(&self) {
|
pub fn wake(&self) {
|
||||||
if self.is_woken().fetch_or(true, Ordering::SeqCst) == false {
|
if self.is_woken().fetch_or(true, Ordering::Acquire) == false {
|
||||||
set_events(&[self.thread])
|
set_events(&[self.thread])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -470,7 +474,7 @@ impl Waiter {
|
|||||||
.filter_map(|waiter| {
|
.filter_map(|waiter| {
|
||||||
// Only wake up items that are not woken.
|
// Only wake up items that are not woken.
|
||||||
// Set the item to be woken if it is not woken.
|
// Set the item to be woken if it is not woken.
|
||||||
if waiter.is_woken().fetch_or(true, Ordering::SeqCst) == false {
|
if waiter.is_woken().fetch_or(true, Ordering::Acquire) == false {
|
||||||
Some(waiter.thread())
|
Some(waiter.thread())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -165,12 +165,18 @@ const FUTEX_OWNER_DIED: u32 = 0x4000_0000;
|
|||||||
const FUTEX_TID_MASK: u32 = 0x3FFF_FFFF;
|
const FUTEX_TID_MASK: u32 = 0x3FFF_FFFF;
|
||||||
|
|
||||||
/// Wakeup one robust futex owned by the thread
|
/// Wakeup one robust futex owned by the thread
|
||||||
|
///
|
||||||
|
/// Note about memory ordering:
|
||||||
|
/// Here futex_val is just a shared variables between threads and doesn't synchronize
|
||||||
|
/// with other variables. Compare_exchange guarantees that the operation is on the
|
||||||
|
/// "latest" value. Therefore, `Relaxed` can be used in both single-threaded and
|
||||||
|
/// multi-threaded environments.
|
||||||
pub fn wake_robust_futex(futex_addr: *const i32, tid: pid_t) -> Result<()> {
|
pub fn wake_robust_futex(futex_addr: *const i32, tid: pid_t) -> Result<()> {
|
||||||
let futex_val = {
|
let futex_val = {
|
||||||
check_ptr(futex_addr)?;
|
check_ptr(futex_addr)?;
|
||||||
unsafe { AtomicU32::from_mut(&mut *(futex_addr as *mut u32)) }
|
unsafe { AtomicU32::from_mut(&mut *(futex_addr as *mut u32)) }
|
||||||
};
|
};
|
||||||
let mut old_val = futex_val.load(Ordering::SeqCst);
|
let mut old_val = futex_val.load(Ordering::Relaxed);
|
||||||
loop {
|
loop {
|
||||||
// This futex may held by another thread, do nothing
|
// This futex may held by another thread, do nothing
|
||||||
if old_val & FUTEX_TID_MASK != tid {
|
if old_val & FUTEX_TID_MASK != tid {
|
||||||
@ -178,14 +184,14 @@ pub fn wake_robust_futex(futex_addr: *const i32, tid: pid_t) -> Result<()> {
|
|||||||
}
|
}
|
||||||
let new_val = (old_val & FUTEX_WAITERS) | FUTEX_OWNER_DIED;
|
let new_val = (old_val & FUTEX_WAITERS) | FUTEX_OWNER_DIED;
|
||||||
if let Err(cur_val) =
|
if let Err(cur_val) =
|
||||||
futex_val.compare_exchange(old_val, new_val, Ordering::SeqCst, Ordering::SeqCst)
|
futex_val.compare_exchange(old_val, new_val, Ordering::Relaxed, Ordering::Relaxed)
|
||||||
{
|
{
|
||||||
// The futex value has changed, let's retry with current value
|
// The futex value has changed, let's retry with current value
|
||||||
old_val = cur_val;
|
old_val = cur_val;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Wakeup one waiter
|
// Wakeup one waiter
|
||||||
if futex_val.load(Ordering::SeqCst) & FUTEX_WAITERS != 0 {
|
if futex_val.load(Ordering::Relaxed) & FUTEX_WAITERS != 0 {
|
||||||
debug!("wake robust futex addr: {:?}", futex_addr);
|
debug!("wake robust futex addr: {:?}", futex_addr);
|
||||||
super::do_futex::futex_wake(futex_addr, 1)?;
|
super::do_futex::futex_wake(futex_addr, 1)?;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,11 @@ pub use self::exec::{enqueue, enqueue_and_exec, exec};
|
|||||||
mod exec;
|
mod exec;
|
||||||
|
|
||||||
/// Note: this definition must be in sync with task.h
|
/// Note: this definition must be in sync with task.h
|
||||||
|
///
|
||||||
|
/// Note about memory ordering:
|
||||||
|
/// Here user_fs is just a signal and doesn't synchronize with other
|
||||||
|
/// variables. Therefore, `Relaxed` can be used in both single-threaded
|
||||||
|
/// and multi-threaded environments.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Task {
|
pub struct Task {
|
||||||
@ -51,10 +56,10 @@ impl Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn set_user_fs(&self, user_fs: usize) {
|
pub(super) fn set_user_fs(&self, user_fs: usize) {
|
||||||
self.user_fs.store(user_fs, Ordering::SeqCst);
|
self.user_fs.store(user_fs, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn user_fs(&self) -> usize {
|
pub fn user_fs(&self) -> usize {
|
||||||
self.user_fs.load(Ordering::SeqCst)
|
self.user_fs.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,10 @@ use crate::signal::SigNum;
|
|||||||
use sgx_tstd::sync::SgxMutex;
|
use sgx_tstd::sync::SgxMutex;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
|
/// Note about memory ordering:
|
||||||
|
/// Here exited needs to be synchronized with status. The read operation of exited
|
||||||
|
/// needs to see the change of the status field. Just `Acquire` or `Release` needs
|
||||||
|
/// to be used to make all the change of the status visible to us.
|
||||||
pub struct ForcedExitStatus {
|
pub struct ForcedExitStatus {
|
||||||
exited: AtomicBool,
|
exited: AtomicBool,
|
||||||
status: SgxMutex<Option<TermStatus>>,
|
status: SgxMutex<Option<TermStatus>>,
|
||||||
@ -18,13 +22,13 @@ impl ForcedExitStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_forced_to_exit(&self) -> bool {
|
pub fn is_forced_to_exit(&self) -> bool {
|
||||||
self.exited.load(Ordering::SeqCst)
|
self.exited.load(Ordering::Acquire)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn force_exit(&self, status: TermStatus) {
|
pub fn force_exit(&self, status: TermStatus) {
|
||||||
let mut old_status = self.status.lock().unwrap();
|
let mut old_status = self.status.lock().unwrap();
|
||||||
// set the bool after getting the status lock
|
// set the bool after getting the status lock
|
||||||
self.exited.store(true, Ordering::SeqCst);
|
self.exited.store(true, Ordering::Release);
|
||||||
old_status.get_or_insert(status);
|
old_status.get_or_insert(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,12 @@ use std::ptr::NonNull;
|
|||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
/// An memory allocator for slices, backed by a fixed-size, untrusted buffer
|
/// An memory allocator for slices, backed by a fixed-size, untrusted buffer
|
||||||
|
///
|
||||||
|
/// Note about memory ordering:
|
||||||
|
/// Here buf_pos is used here for counting, not to synchronize access to other
|
||||||
|
/// shared variables. Fetch_update guarantees that the operation is on the
|
||||||
|
/// "latest" value. Therefore, `Relaxed` can be used in both single-threaded and
|
||||||
|
/// multi-threaded environments.
|
||||||
pub struct UntrustedSliceAlloc {
|
pub struct UntrustedSliceAlloc {
|
||||||
/// The pointer to the untrusted buffer
|
/// The pointer to the untrusted buffer
|
||||||
buf_ptr: *mut u8,
|
buf_ptr: *mut u8,
|
||||||
@ -48,7 +54,7 @@ impl UntrustedSliceAlloc {
|
|||||||
// Move self.buf_pos forward if enough space _atomically_.
|
// Move self.buf_pos forward if enough space _atomically_.
|
||||||
let old_pos = self
|
let old_pos = self
|
||||||
.buf_pos
|
.buf_pos
|
||||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |old_pos| {
|
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old_pos| {
|
||||||
let new_pos = old_pos + new_slice_len;
|
let new_pos = old_pos + new_slice_len;
|
||||||
if new_pos <= self.buf_size {
|
if new_pos <= self.buf_size {
|
||||||
Some(new_pos)
|
Some(new_pos)
|
||||||
|
Loading…
Reference in New Issue
Block a user