| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License.. |
| |
| use sgx_types::{SysError, sgx_thread_t, SGX_THREAD_T_NULL}; |
| use sgx_trts::libc; |
| use crate::thread; |
| use crate::sync::SgxThreadMutex; |
| use crate::sync::SgxThreadCondvar; |
| use crate::sync::SgxThreadSpinlock; |
| use core::cell::UnsafeCell; |
| |
| struct SgxThreadRwLockInner { |
| readers_num: u32, |
| writers_num: u32, |
| busy: u32, |
| writer_thread: sgx_thread_t, |
| condvar: SgxThreadCondvar, |
| mutex: SgxThreadMutex, |
| spinlock: SgxThreadSpinlock, |
| } |
| |
| impl SgxThreadRwLockInner { |
| |
| const fn new() -> Self { |
| SgxThreadRwLockInner{ |
| readers_num: 0, |
| writers_num: 0, |
| busy: 0, |
| writer_thread: SGX_THREAD_T_NULL, |
| condvar: SgxThreadCondvar::new(), |
| mutex: SgxThreadMutex::new(), |
| spinlock: SgxThreadSpinlock::new(), |
| } |
| } |
| |
| unsafe fn ref_busy(&mut self) -> SysError { |
| let ret: SysError; |
| self.spinlock.lock(); |
| { |
| if self.busy == u32::max_value() { |
| ret = Err(libc::EAGAIN); |
| } else { |
| self.busy += 1; |
| ret = Ok(()); |
| } |
| } |
| self.spinlock.unlock(); |
| ret |
| } |
| |
| unsafe fn deref_busy(&mut self) -> SysError { |
| let ret: SysError; |
| self.spinlock.lock(); |
| { |
| if self.busy == 0 { |
| ret = Err(libc::EAGAIN); |
| } else { |
| self.busy -= 1; |
| ret = Ok(()); |
| } |
| } |
| self.spinlock.unlock(); |
| ret |
| } |
| |
| unsafe fn read(&mut self) -> SysError { |
| self.ref_busy()?; |
| self.mutex.lock(); |
| { |
| if self.writer_thread == thread::rsgx_thread_self() { |
| self.mutex.unlock(); |
| self.deref_busy(); |
| return Err(libc::EDEADLK); |
| } |
| if self.readers_num == u32::max_value() { |
| self.mutex.unlock(); |
| self.deref_busy(); |
| return Err(libc::EAGAIN); |
| } |
| while self.writers_num > 0 { |
| self.condvar.wait(&self.mutex); |
| } |
| self.readers_num += 1; |
| } |
| self.mutex.unlock(); |
| self.deref_busy(); |
| Ok(()) |
| } |
| |
| unsafe fn try_read(&mut self) -> SysError { |
| self.ref_busy()?; |
| self.mutex.lock(); |
| { |
| let mut ret = Ok(()); |
| if self.writer_thread == thread::rsgx_thread_self() { |
| ret = Err(libc::EDEADLK); |
| } |
| else if self.readers_num == u32::max_value() { |
| ret = Err(libc::EAGAIN); |
| } |
| else if self.writers_num > 0 { |
| ret = Err(libc::EBUSY); |
| } |
| match ret { |
| Ok(_) => {}, |
| Err(e) => { |
| self.mutex.unlock(); |
| self.deref_busy(); |
| return Err(e); |
| } |
| } |
| self.readers_num += 1; |
| } |
| self.mutex.unlock(); |
| self.deref_busy(); |
| Ok(()) |
| } |
| |
| unsafe fn write(&mut self) -> SysError { |
| self.ref_busy()?; |
| self.mutex.lock(); |
| { |
| if self.writer_thread == thread::rsgx_thread_self() { |
| self.mutex.unlock(); |
| self.deref_busy(); |
| return Err(libc::EDEADLK); |
| } |
| |
| if self.writers_num == u32::max_value() { |
| self.mutex.unlock(); |
| self.deref_busy(); |
| return Err(libc::EAGAIN); |
| } |
| |
| self.writers_num += 1; |
| while self.readers_num > 0 { |
| self.condvar.wait(&self.mutex); |
| } |
| while self.writer_thread != SGX_THREAD_T_NULL { |
| self.condvar.wait(&self.mutex); |
| } |
| self.writer_thread = thread::rsgx_thread_self(); |
| } |
| self.mutex.unlock(); |
| self.deref_busy(); |
| Ok(()) |
| } |
| |
| pub unsafe fn try_write(&mut self) -> SysError { |
| self.ref_busy()?; |
| self.mutex.lock(); |
| { |
| let mut ret = Ok(()); |
| if self.writer_thread == thread::rsgx_thread_self() { |
| ret = Err(libc::EDEADLK); |
| } |
| else if self.writers_num == u32::max_value() { |
| ret = Err(libc::EAGAIN); |
| } |
| else if self.readers_num > 0 || self.writer_thread != SGX_THREAD_T_NULL { |
| ret = Err(libc::EBUSY); |
| } |
| |
| match ret { |
| Ok(_) => {}, |
| Err(e) => { |
| self.mutex.unlock(); |
| self.deref_busy(); |
| return Err(e); |
| } |
| } |
| self.writers_num += 1; |
| self.writer_thread = thread::rsgx_thread_self(); |
| } |
| self.mutex.unlock(); |
| self.deref_busy(); |
| Ok(()) |
| } |
| |
| unsafe fn read_unlock(&mut self) -> SysError { |
| self.raw_unlock() |
| } |
| |
| unsafe fn write_unlock(&mut self) -> SysError { |
| self.raw_unlock() |
| } |
| |
| unsafe fn raw_unlock(&mut self) -> SysError { |
| self.mutex.lock(); |
| { |
| if self.readers_num > 0 { |
| self.readers_num -= 1; |
| if self.readers_num == 0 && self.writers_num > 0 { |
| self.condvar.broadcast(); |
| } |
| } else { |
| if self.writer_thread != thread::rsgx_thread_self() { |
| self.mutex.unlock(); |
| return Err(libc::EPERM); |
| } |
| self.writers_num -= 1; |
| self.writer_thread = SGX_THREAD_T_NULL; |
| if self.busy > 0 { |
| self.condvar.broadcast(); |
| } |
| } |
| } |
| self.mutex.unlock(); |
| Ok(()) |
| } |
| |
| unsafe fn destroy(&mut self) -> SysError { |
| self.mutex.lock(); |
| { |
| if self.readers_num > 0 || |
| self.writers_num > 0 || |
| self.busy > 0 { |
| |
| self.spinlock.unlock(); |
| return Err(libc::EBUSY); |
| } |
| |
| self.condvar.destroy(); |
| self.mutex.destroy(); |
| } |
| self.spinlock.unlock(); |
| Ok(()) |
| } |
| } |
| |
| /// An OS-based reader-writer lock. |
| /// |
| /// This structure is entirely unsafe and serves as the lowest layer of a |
| /// cross-platform binding of system rwlocks. It is recommended to use the |
| /// safer types at the top level of this crate instead of this type. |
| pub struct SgxThreadRwLock { |
| lock: UnsafeCell<SgxThreadRwLockInner>, |
| } |
| |
| impl SgxThreadRwLock { |
| /// Creates a new reader-writer lock for use. |
| pub const fn new() -> Self { |
| SgxThreadRwLock { |
| lock: UnsafeCell::new(SgxThreadRwLockInner::new()) |
| } |
| } |
| |
| /// Acquires shared access to the underlying lock, blocking the current |
| /// thread to do so. |
| #[inline] |
| pub unsafe fn read(&self) -> SysError { |
| let rwlock: &mut SgxThreadRwLockInner = &mut *self.lock.get(); |
| rwlock.read() |
| } |
| |
| /// Attempts to acquire shared access to this lock, returning whether it |
| /// succeeded or not. |
| /// |
| /// This function does not block the current thread. |
| #[inline] |
| pub unsafe fn try_read(&self) -> SysError { |
| let rwlock: &mut SgxThreadRwLockInner = &mut *self.lock.get(); |
| rwlock.try_read() |
| } |
| |
| /// Acquires write access to the underlying lock, blocking the current thread |
| /// to do so. |
| #[inline] |
| pub unsafe fn write(&self) -> SysError { |
| let rwlock: &mut SgxThreadRwLockInner = &mut *self.lock.get(); |
| rwlock.write() |
| } |
| |
| /// Attempts to acquire exclusive access to this lock, returning whether it |
| /// succeeded or not. |
| /// |
| /// This function does not block the current thread. |
| #[inline] |
| pub unsafe fn try_write(&self) -> SysError { |
| let rwlock: &mut SgxThreadRwLockInner = &mut *self.lock.get(); |
| rwlock.try_write() |
| } |
| |
| /// Unlocks previously acquired shared access to this lock. |
| #[inline] |
| pub unsafe fn read_unlock(&self) -> SysError { |
| let rwlock: &mut SgxThreadRwLockInner = &mut *self.lock.get(); |
| rwlock.read_unlock() |
| } |
| |
| /// Unlocks previously acquired exclusive access to this lock. |
| #[inline] |
| pub unsafe fn write_unlock(&self) -> SysError { |
| let rwlock: &mut SgxThreadRwLockInner = &mut *self.lock.get(); |
| rwlock.write_unlock() |
| } |
| |
| /// Destroys OS-related resources with this RWLock. |
| #[inline] |
| pub unsafe fn destroy(&self) -> SysError { |
| let rwlock: &mut SgxThreadRwLockInner = &mut *self.lock.get(); |
| rwlock.destroy() |
| } |
| } |