use crate::{PGRXSharedMemory, PgSharedMemoryInitialization};
use core::ops::{Deref, DerefMut};
use std::cell::UnsafeCell;
use std::ffi::CStr;
pub struct PgLwLock<T> {
name: &'static CStr,
inner: UnsafeCell<*mut Shared<T>>,
}
unsafe impl<T: PGRXSharedMemory> Sync for PgLwLock<T> {}
impl<T> PgLwLock<T> {
pub const unsafe fn new(name: &'static CStr) -> Self {
Self { name, inner: UnsafeCell::new(std::ptr::null_mut()) }
}
pub const fn name(&self) -> &'static CStr {
self.name
}
}
impl<T: PGRXSharedMemory> PgLwLock<T> {
pub fn share(&self) -> PgLwLockShareGuard<'_, T> {
unsafe {
let shared = self.inner.get().read().as_ref().expect("PgLwLock was not initialized");
crate::pg_sys::LWLockAcquire(shared.lock, crate::pg_sys::LWLockMode::LW_SHARED);
PgLwLockShareGuard { data: &*shared.data.get(), lock: shared.lock }
}
}
pub fn exclusive(&self) -> PgLwLockExclusiveGuard<'_, T> {
unsafe {
let shared = self.inner.get().read().as_ref().expect("PgLwLock was not initialized");
crate::pg_sys::LWLockAcquire(shared.lock, crate::pg_sys::LWLockMode::LW_EXCLUSIVE);
PgLwLockExclusiveGuard { data: &mut *shared.data.get(), lock: shared.lock }
}
}
}
impl<T: PGRXSharedMemory> PgSharedMemoryInitialization for PgLwLock<T> {
type Value = T;
unsafe fn on_shmem_request(&'static self) {
unsafe {
crate::pg_sys::RequestAddinShmemSpace(size_of::<Shared<T>>());
crate::pg_sys::RequestNamedLWLockTranche(self.name.as_ptr(), 1);
}
}
unsafe fn on_shmem_startup(&'static self, value: T) {
unsafe {
use crate::pg_sys;
let shm_name = self.name;
let addin_shmem_init_lock = &raw mut (*pg_sys::MainLWLockArray.add(21)).lock;
pg_sys::LWLockAcquire(addin_shmem_init_lock, pg_sys::LWLockMode::LW_EXCLUSIVE);
let mut found = false;
let fv_shmem =
pg_sys::ShmemInitStruct(shm_name.as_ptr(), size_of::<Shared<T>>(), &mut found)
.cast::<Shared<T>>();
assert!(fv_shmem.is_aligned(), "shared memory is not aligned");
if !found {
fv_shmem.write(Shared {
data: UnsafeCell::new(value),
lock: &raw mut (*pg_sys::GetNamedLWLockTranche(shm_name.as_ptr())).lock,
});
}
*self.inner.get() = fv_shmem;
pg_sys::LWLockRelease(addin_shmem_init_lock);
}
}
}
#[repr(C)]
struct Shared<T> {
data: UnsafeCell<T>,
lock: *mut crate::pg_sys::LWLock,
}
pub struct PgLwLockShareGuard<'a, T> {
data: &'a T,
lock: *mut crate::pg_sys::LWLock,
}
unsafe impl<T: PGRXSharedMemory> Sync for PgLwLockShareGuard<'_, T> {}
impl<T> Drop for PgLwLockShareGuard<'_, T> {
fn drop(&mut self) {
unsafe { release_unless_elog_unwinding(self.lock) }
}
}
impl<T> Deref for PgLwLockShareGuard<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
self.data
}
}
pub struct PgLwLockExclusiveGuard<'a, T> {
data: &'a mut T,
lock: *mut crate::pg_sys::LWLock,
}
unsafe impl<T: PGRXSharedMemory> Sync for PgLwLockExclusiveGuard<'_, T> {}
impl<T> Deref for PgLwLockExclusiveGuard<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
self.data
}
}
impl<T> DerefMut for PgLwLockExclusiveGuard<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
self.data
}
}
impl<T> Drop for PgLwLockExclusiveGuard<'_, T> {
fn drop(&mut self) {
unsafe { release_unless_elog_unwinding(self.lock) }
}
}
unsafe fn release_unless_elog_unwinding(lock: *mut crate::pg_sys::LWLock) {
if crate::pg_sys::InterruptHoldoffCount > 0 {
crate::pg_sys::LWLockRelease(lock);
}
}