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