1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
//! Types related to the PHP executor globals.
use std::ops::{Deref, DerefMut};
use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard};
use crate::boxed::ZBox;
use crate::ffi::{_zend_executor_globals, ext_php_rs_executor_globals};
use crate::types::{ZendHashTable, ZendObject};
/// Stores global variables used in the PHP executor.
pub type ExecutorGlobals = _zend_executor_globals;
impl ExecutorGlobals {
/// Returns a reference to the PHP executor globals.
///
/// The executor globals are guarded by a RwLock. There can be multiple
/// immutable references at one time but only ever one mutable reference.
/// Attempting to retrieve the globals while already holding the global
/// guard will lead to a deadlock. Dropping the globals guard will release
/// the lock.
pub fn get() -> GlobalReadGuard<Self> {
// SAFETY: PHP executor globals are statically declared therefore should never
// return an invalid pointer.
let globals = unsafe { ext_php_rs_executor_globals().as_ref() }
.expect("Static executor globals were invalid");
let guard = GLOBALS_LOCK.read();
GlobalReadGuard { globals, guard }
}
/// Returns a mutable reference to the PHP executor globals.
///
/// The executor globals are guarded by a RwLock. There can be multiple
/// immutable references at one time but only ever one mutable reference.
/// Attempting to retrieve the globals while already holding the global
/// guard will lead to a deadlock. Dropping the globals guard will release
/// the lock.
fn get_mut() -> GlobalWriteGuard<Self> {
// SAFETY: PHP executor globals are statically declared therefore should never
// return an invalid pointer.
let globals = unsafe { ext_php_rs_executor_globals().as_mut() }
.expect("Static executor globals were invalid");
let guard = GLOBALS_LOCK.write();
GlobalWriteGuard { globals, guard }
}
/// Attempts to retrieve the global class hash table.
pub fn class_table(&self) -> Option<&ZendHashTable> {
unsafe { self.class_table.as_ref() }
}
/// Attempts to extract the last PHP exception captured by the interpreter.
/// Returned inside a [`ZBox`].
///
/// This function requires the executor globals to be mutably held, which
/// could lead to a deadlock if the globals are already borrowed immutably
/// or mutably.
pub fn take_exception() -> Option<ZBox<ZendObject>> {
let mut globals = Self::get_mut();
let mut exception_ptr = std::ptr::null_mut();
std::mem::swap(&mut exception_ptr, &mut globals.exception);
// SAFETY: `as_mut` checks for null.
Some(unsafe { ZBox::from_raw(exception_ptr.as_mut()?) })
}
}
/// Executor globals rwlock.
///
/// PHP provides no indication if the executor globals are being accessed so
/// this is only effective on the Rust side.
static GLOBALS_LOCK: RwLock<()> = const_rwlock(());
/// Wrapper guard that contains a reference to a given type `T`. Dropping a
/// guard releases the lock on the relevant rwlock.
pub struct GlobalReadGuard<T: 'static> {
globals: &'static T,
#[allow(dead_code)]
guard: RwLockReadGuard<'static, ()>,
}
impl<T> Deref for GlobalReadGuard<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.globals
}
}
/// Wrapper guard that contains a mutable reference to a given type `T`.
/// Dropping a guard releases the lock on the relevant rwlock.
pub struct GlobalWriteGuard<T: 'static> {
globals: &'static mut T,
#[allow(dead_code)]
guard: RwLockWriteGuard<'static, ()>,
}
impl<T> Deref for GlobalWriteGuard<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.globals
}
}
impl<T> DerefMut for GlobalWriteGuard<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.globals
}
}