1cac7dca0Sopenharmony_ci// Copyright (c) 2023 Huawei Device Co., Ltd. 2cac7dca0Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License"); 3cac7dca0Sopenharmony_ci// you may not use this file except in compliance with the License. 4cac7dca0Sopenharmony_ci// You may obtain a copy of the License at 5cac7dca0Sopenharmony_ci// 6cac7dca0Sopenharmony_ci// http://www.apache.org/licenses/LICENSE-2.0 7cac7dca0Sopenharmony_ci// 8cac7dca0Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software 9cac7dca0Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS, 10cac7dca0Sopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11cac7dca0Sopenharmony_ci// See the License for the specific language governing permissions and 12cac7dca0Sopenharmony_ci// limitations under the License. 13cac7dca0Sopenharmony_ci 14cac7dca0Sopenharmony_ci//! The code inside a signal handler should be async-signal-safe, you can check 15cac7dca0Sopenharmony_ci//! the definition here: <https://man7.org/linux/man-pages/man7/signal-safety.7.html.> 16cac7dca0Sopenharmony_ci//! For short, a signal can be happened at anytime in a thread and the signal 17cac7dca0Sopenharmony_ci//! handler will be executed on the same exact thread. Therefore, if the signal 18cac7dca0Sopenharmony_ci//! handler function needs a resource that has been already acquired by the 19cac7dca0Sopenharmony_ci//! thread (like a nonreentrant mutex), it could cause deadlock. 20cac7dca0Sopenharmony_ci//! 21cac7dca0Sopenharmony_ci//! In this crate, the signal handler needs to read the action of a signal from 22cac7dca0Sopenharmony_ci//! a global singleton signal-manager. This signal-manager should be protected 23cac7dca0Sopenharmony_ci//! by a lock to ensure atomicity. However, we could not use the regular 24cac7dca0Sopenharmony_ci//! [`std::sync::RwLock`] because this lock is not async-signal-safe. 25cac7dca0Sopenharmony_ci//! 26cac7dca0Sopenharmony_ci//! Thus, we need to implement a spinning RwLock that provides non-block read 27cac7dca0Sopenharmony_ci//! method for the signal handler to use. 28cac7dca0Sopenharmony_ci 29cac7dca0Sopenharmony_ciuse std::hint; 30cac7dca0Sopenharmony_ciuse std::marker::PhantomData; 31cac7dca0Sopenharmony_ciuse std::ops::Deref; 32cac7dca0Sopenharmony_ciuse std::ptr::null_mut; 33cac7dca0Sopenharmony_ciuse std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; 34cac7dca0Sopenharmony_ciuse std::sync::{Mutex, MutexGuard}; 35cac7dca0Sopenharmony_ci 36cac7dca0Sopenharmony_ciconst VERSIONS: usize = 2; 37cac7dca0Sopenharmony_ciconst HOLDER_COUNT_MAX: usize = usize::MAX / 2; 38cac7dca0Sopenharmony_ci 39cac7dca0Sopenharmony_cipub(crate) struct SpinningRwLock<T> { 40cac7dca0Sopenharmony_ci version: AtomicUsize, 41cac7dca0Sopenharmony_ci data: [AtomicPtr<T>; VERSIONS], 42cac7dca0Sopenharmony_ci version_holder_count: [AtomicUsize; VERSIONS], 43cac7dca0Sopenharmony_ci write_lock: Mutex<()>, 44cac7dca0Sopenharmony_ci _phantom: PhantomData<T>, 45cac7dca0Sopenharmony_ci} 46cac7dca0Sopenharmony_ci 47cac7dca0Sopenharmony_ciimpl<T> SpinningRwLock<T> { 48cac7dca0Sopenharmony_ci pub(crate) fn new(data: T) -> Self { 49cac7dca0Sopenharmony_ci let val = Box::new(data); 50cac7dca0Sopenharmony_ci let val_ptr = Box::into_raw(val); 51cac7dca0Sopenharmony_ci 52cac7dca0Sopenharmony_ci let datas = [AtomicPtr::new(val_ptr), Default::default()]; 53cac7dca0Sopenharmony_ci 54cac7dca0Sopenharmony_ci SpinningRwLock { 55cac7dca0Sopenharmony_ci data: datas, 56cac7dca0Sopenharmony_ci version: Default::default(), 57cac7dca0Sopenharmony_ci version_holder_count: Default::default(), 58cac7dca0Sopenharmony_ci write_lock: Mutex::new(()), 59cac7dca0Sopenharmony_ci _phantom: Default::default(), 60cac7dca0Sopenharmony_ci } 61cac7dca0Sopenharmony_ci } 62cac7dca0Sopenharmony_ci 63cac7dca0Sopenharmony_ci pub(crate) fn read(&self) -> ReadGuard<T> { 64cac7dca0Sopenharmony_ci loop { 65cac7dca0Sopenharmony_ci let version = self.version.load(Ordering::SeqCst) % VERSIONS; 66cac7dca0Sopenharmony_ci let curr_count = &self.version_holder_count[version]; 67cac7dca0Sopenharmony_ci 68cac7dca0Sopenharmony_ci if curr_count.fetch_add(1, Ordering::SeqCst) > HOLDER_COUNT_MAX { 69cac7dca0Sopenharmony_ci // read function is called inside a signal handler, so we cannot return an error 70cac7dca0Sopenharmony_ci // or panic directly, instead we use libc::abort 71cac7dca0Sopenharmony_ci unsafe { libc::abort() }; 72cac7dca0Sopenharmony_ci } 73cac7dca0Sopenharmony_ci 74cac7dca0Sopenharmony_ci // This data could already be nullptr in the following execution order 75cac7dca0Sopenharmony_ci // 1. reader loads the current version 76cac7dca0Sopenharmony_ci // 2. writer increments the version 77cac7dca0Sopenharmony_ci // 3. writer sets old data to nullptr 78cac7dca0Sopenharmony_ci // 4. writer blocking waits until old version counter is 0 79cac7dca0Sopenharmony_ci // 5. reader increments the old version counter 80cac7dca0Sopenharmony_ci // 6. reader acquires the old data using the old version 81cac7dca0Sopenharmony_ci // In this case, reader should try again. 82cac7dca0Sopenharmony_ci let data = self.data[version].load(Ordering::SeqCst); 83cac7dca0Sopenharmony_ci if data.is_null() { 84cac7dca0Sopenharmony_ci curr_count.fetch_sub(1, Ordering::SeqCst); 85cac7dca0Sopenharmony_ci continue; 86cac7dca0Sopenharmony_ci } 87cac7dca0Sopenharmony_ci // this is safe because we just check the data is not nullptr, which means the 88cac7dca0Sopenharmony_ci // writer has not yet released this data. The reader adds the holder 89cac7dca0Sopenharmony_ci // count before acquire the data, the writer will not release the 90cac7dca0Sopenharmony_ci // data until the all readers get dropped. 91cac7dca0Sopenharmony_ci let data = unsafe { &*data }; 92cac7dca0Sopenharmony_ci 93cac7dca0Sopenharmony_ci return ReadGuard { 94cac7dca0Sopenharmony_ci data, 95cac7dca0Sopenharmony_ci version_holder_count: curr_count, 96cac7dca0Sopenharmony_ci }; 97cac7dca0Sopenharmony_ci } 98cac7dca0Sopenharmony_ci } 99cac7dca0Sopenharmony_ci 100cac7dca0Sopenharmony_ci pub(crate) fn write(&self) -> WriteGuard<T> { 101cac7dca0Sopenharmony_ci let guard = self.write_lock.lock().unwrap(); 102cac7dca0Sopenharmony_ci let version = self.version.load(Ordering::SeqCst); 103cac7dca0Sopenharmony_ci 104cac7dca0Sopenharmony_ci WriteGuard { 105cac7dca0Sopenharmony_ci lock: self, 106cac7dca0Sopenharmony_ci version, 107cac7dca0Sopenharmony_ci _guard: guard, 108cac7dca0Sopenharmony_ci } 109cac7dca0Sopenharmony_ci } 110cac7dca0Sopenharmony_ci 111cac7dca0Sopenharmony_ci pub(crate) fn wait_version_release(&self, version: usize) { 112cac7dca0Sopenharmony_ci let count = &self.version_holder_count[version]; 113cac7dca0Sopenharmony_ci while count.load(Ordering::SeqCst) != 0 { 114cac7dca0Sopenharmony_ci hint::spin_loop(); 115cac7dca0Sopenharmony_ci } 116cac7dca0Sopenharmony_ci } 117cac7dca0Sopenharmony_ci} 118cac7dca0Sopenharmony_ci 119cac7dca0Sopenharmony_cipub(crate) struct ReadGuard<'a, T: 'a> { 120cac7dca0Sopenharmony_ci pub(crate) data: &'a T, 121cac7dca0Sopenharmony_ci version_holder_count: &'a AtomicUsize, 122cac7dca0Sopenharmony_ci} 123cac7dca0Sopenharmony_ci 124cac7dca0Sopenharmony_ciimpl<'a, T> Drop for ReadGuard<'a, T> { 125cac7dca0Sopenharmony_ci fn drop(&mut self) { 126cac7dca0Sopenharmony_ci self.version_holder_count.fetch_sub(1, Ordering::SeqCst); 127cac7dca0Sopenharmony_ci } 128cac7dca0Sopenharmony_ci} 129cac7dca0Sopenharmony_ci 130cac7dca0Sopenharmony_ciimpl<'a, T> Deref for ReadGuard<'a, T> { 131cac7dca0Sopenharmony_ci type Target = T; 132cac7dca0Sopenharmony_ci 133cac7dca0Sopenharmony_ci fn deref(&self) -> &Self::Target { 134cac7dca0Sopenharmony_ci self.data 135cac7dca0Sopenharmony_ci } 136cac7dca0Sopenharmony_ci} 137cac7dca0Sopenharmony_ci 138cac7dca0Sopenharmony_cipub(crate) struct WriteGuard<'a, T: 'a> { 139cac7dca0Sopenharmony_ci lock: &'a SpinningRwLock<T>, 140cac7dca0Sopenharmony_ci version: usize, 141cac7dca0Sopenharmony_ci _guard: MutexGuard<'a, ()>, 142cac7dca0Sopenharmony_ci} 143cac7dca0Sopenharmony_ci 144cac7dca0Sopenharmony_ciimpl<'a, T> WriteGuard<'a, T> { 145cac7dca0Sopenharmony_ci pub(crate) fn store(&mut self, val: T) { 146cac7dca0Sopenharmony_ci let val = Box::new(val); 147cac7dca0Sopenharmony_ci let val_ptr = Box::into_raw(val); 148cac7dca0Sopenharmony_ci 149cac7dca0Sopenharmony_ci let old_version = self.version % VERSIONS; 150cac7dca0Sopenharmony_ci let new_version = (old_version + 1) % VERSIONS; 151cac7dca0Sopenharmony_ci self.lock.data[new_version].store(val_ptr, Ordering::SeqCst); 152cac7dca0Sopenharmony_ci self.lock.version.store(new_version, Ordering::SeqCst); 153cac7dca0Sopenharmony_ci 154cac7dca0Sopenharmony_ci let old_data = self.lock.data[old_version].swap(null_mut(), Ordering::SeqCst); 155cac7dca0Sopenharmony_ci self.lock.wait_version_release(old_version); 156cac7dca0Sopenharmony_ci self.version = new_version; 157cac7dca0Sopenharmony_ci 158cac7dca0Sopenharmony_ci // the old data is valid and currently no one is holding it, 159cac7dca0Sopenharmony_ci // therefore the drop is safe 160cac7dca0Sopenharmony_ci unsafe { 161cac7dca0Sopenharmony_ci drop(Box::from_raw(old_data)); 162cac7dca0Sopenharmony_ci } 163cac7dca0Sopenharmony_ci } 164cac7dca0Sopenharmony_ci} 165cac7dca0Sopenharmony_ci 166cac7dca0Sopenharmony_ciimpl<'a, T> Deref for WriteGuard<'a, T> { 167cac7dca0Sopenharmony_ci type Target = T; 168cac7dca0Sopenharmony_ci 169cac7dca0Sopenharmony_ci fn deref(&self) -> &Self::Target { 170cac7dca0Sopenharmony_ci let data = self.lock.data[self.version].load(Ordering::SeqCst); 171cac7dca0Sopenharmony_ci // the write guard always points to a valid data ptr 172cac7dca0Sopenharmony_ci unsafe { &*data } 173cac7dca0Sopenharmony_ci } 174cac7dca0Sopenharmony_ci} 175