use core::marker::PhantomData;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use tracing::{debug, trace};
use crate::{
active_messaging::RemotePtr,
darc::{Darc, DarcCommPtr, DarcInner, DarcMode, __NetworkDarc},
lamellae::{CommAllocAddr, CommMem},
lamellar_team::{IntoLamellarTeam, LamellarTeamRT},
LamellarEnv, LamellarTeam,
};
use super::handle::{
GlobalRwDarcCollectiveWriteHandle, GlobalRwDarcHandle, GlobalRwDarcReadHandle,
GlobalRwDarcWriteHandle, IntoDarcHandle, IntoLocalRwDarcHandle,
};
use super::CommAlloc;
#[derive(serde::Serialize, serde::Deserialize, Debug)]
enum LockType {
Read,
Write,
CollectiveWrite(usize),
}
#[derive(Debug)]
#[repr(C)]
pub(crate) struct DistRwLock<T> {
readers: AtomicUsize,
writer: AtomicUsize,
lock_arrivals: AtomicUsize,
unlock_arrivals: AtomicUsize,
collective_cnt: AtomicUsize,
team: Darc<LamellarTeamRT>,
data: std::cell::UnsafeCell<T>,
}
unsafe impl<T: Send> Send for DistRwLock<T> {}
unsafe impl<T: Send> Sync for DistRwLock<T> {}
impl<T> DistRwLock<T> {
pub(crate) fn new<U: Into<IntoLamellarTeam>>(data: T, team: U) -> DistRwLock<T> {
let team = team.into().team.clone();
DistRwLock {
readers: AtomicUsize::new(0),
writer: AtomicUsize::new(team.num_pes),
lock_arrivals: AtomicUsize::new(0),
unlock_arrivals: AtomicUsize::new(0),
collective_cnt: AtomicUsize::new(team.num_pes + 1),
team,
data: std::cell::UnsafeCell::new(data),
}
}
pub(crate) fn into_inner(self) -> T {
self.data.into_inner()
}
pub(crate) fn dirty_num_locks(&self) -> usize {
let mut locks = 0;
locks += self.readers.load(Ordering::SeqCst);
if self.writer.load(Ordering::SeqCst) != self.team.num_pes {
locks += 1;
}
if self.lock_arrivals.load(Ordering::SeqCst) != self.unlock_arrivals.load(Ordering::SeqCst)
{
locks += 1;
}
locks
}
fn collective_barrier_target(&self, collective_cnt: usize) -> usize {
(collective_cnt - self.team.num_pes) * self.team.num_pes
}
async fn async_reader_lock(&self, _pe: usize) {
loop {
while self.writer.load(Ordering::SeqCst) != self.team.num_pes {
async_std::task::yield_now().await;
}
self.readers.fetch_add(1, Ordering::SeqCst);
if self.writer.load(Ordering::SeqCst) == self.team.num_pes {
break;
}
self.readers.fetch_sub(1, Ordering::SeqCst);
}
}
async fn async_writer_lock(&self, pe: usize) {
while let Err(_) =
self.writer
.compare_exchange(self.team.num_pes, pe, Ordering::SeqCst, Ordering::SeqCst)
{
async_std::task::yield_now().await;
}
while self.readers.load(Ordering::SeqCst) != 0 {
async_std::task::yield_now().await;
}
}
async fn async_collective_writer_lock(&self, pe: usize, collective_cnt: usize) {
debug!(target: "darc::global_rw", pe, collective_cnt, writer = self.writer.load(Ordering::SeqCst), lock_arrivals = self.lock_arrivals.load(Ordering::SeqCst), "async_collective_writer_lock: entering");
if pe == 0 {
self.async_writer_lock(collective_cnt).await;
} else {
while self.writer.load(Ordering::SeqCst) != collective_cnt {
async_std::task::yield_now().await;
}
while self.readers.load(Ordering::SeqCst) != 0 {
async_std::task::yield_now().await;
}
}
let target = self.collective_barrier_target(collective_cnt);
let arrived = self.lock_arrivals.fetch_add(1, Ordering::SeqCst) + 1;
debug!(target: "darc::global_rw", pe, collective_cnt, arrived, target, "async_collective_writer_lock: entered collective barrier, waiting for all PEs");
while self.lock_arrivals.load(Ordering::SeqCst) < target {
async_std::task::yield_now().await;
}
debug!(target: "darc::global_rw", pe, collective_cnt, "async_collective_writer_lock: all PEs entered, lock acquired");
}
unsafe fn reader_unlock(&self, _pe: usize) {
self.readers.fetch_sub(1, Ordering::SeqCst);
}
unsafe fn writer_unlock(&self, pe: usize) {
if let Err(val) =
self.writer
.compare_exchange(pe, self.team.num_pes, Ordering::SeqCst, Ordering::SeqCst)
{
panic!(
"should not be trying to unlock another pes lock {:?} {:?}",
pe, val
);
}
}
async unsafe fn collective_writer_unlock(&self, pe: usize, collective_cnt: usize) {
debug!(target: "darc::global_rw", pe, collective_cnt, writer = self.writer.load(Ordering::SeqCst), unlock_arrivals = self.unlock_arrivals.load(Ordering::SeqCst), "collective_writer_unlock: entering");
if collective_cnt != self.writer.load(Ordering::SeqCst) {
panic!(
"ERROR: Mismatched collective lock calls {collective_cnt} {:?}",
self.writer.load(Ordering::SeqCst)
);
}
let target = self.collective_barrier_target(collective_cnt);
let arrived = self.unlock_arrivals.fetch_add(1, Ordering::SeqCst) + 1;
debug!(target: "darc::global_rw", pe, collective_cnt, arrived, target, "collective_writer_unlock: entered collective barrier, waiting for all PEs");
while self.unlock_arrivals.load(Ordering::SeqCst) < target {
async_std::task::yield_now().await;
}
debug!(target: "darc::global_rw", pe, collective_cnt, "collective_writer_unlock: all PEs entered unlock");
if pe == 0 {
if let Err(val) = self.writer.compare_exchange(
collective_cnt,
self.team.num_pes,
Ordering::SeqCst,
Ordering::SeqCst,
) {
panic!(
"should not be trying to unlock another pes lock {:?} {:?}",
pe, val
);
}
}
}
}
#[lamellar_impl::AmDataRT(Debug)]
struct LockAm {
rwlock_addr: CommAllocAddr,
orig_pe: usize, lock_type: LockType,
}
#[lamellar_impl::rt_am]
impl LamellarAM for LockAm {
async fn exec() {
let inner: &DarcInner<DistRwLock<()>> = unsafe { &*(self.rwlock_addr.as_ptr()) };
let rwlock = inner.item(); match self.lock_type {
LockType::Read => {
rwlock.async_reader_lock(self.orig_pe).await;
}
LockType::Write => {
rwlock.async_writer_lock(self.orig_pe).await;
}
LockType::CollectiveWrite(cnt) => {
rwlock.async_collective_writer_lock(self.orig_pe, cnt).await;
}
}
}
}
#[lamellar_impl::AmDataRT(Debug)]
struct UnlockAm {
rwlock_addr: CommAllocAddr,
orig_pe: usize, lock_type: LockType,
}
#[lamellar_impl::rt_am]
impl LamellarAM for UnlockAm {
async fn exec() {
let inner: &DarcInner<DistRwLock<()>> = unsafe { &*(self.rwlock_addr.as_ptr()) }; let rwlock = inner.item();
unsafe {
match self.lock_type {
LockType::Read => rwlock.reader_unlock(self.orig_pe),
LockType::Write => rwlock.writer_unlock(self.orig_pe),
LockType::CollectiveWrite(cnt) => {
rwlock.collective_writer_unlock(self.orig_pe, cnt).await
}
}
}
}
}
pub struct GlobalRwDarcReadGuard<T: 'static> {
pub(crate) darc: GlobalRwDarc<T>,
pub(crate) marker: PhantomData<&'static mut T>,
pub(crate) local_cnt: Arc<AtomicUsize>, }
impl<T> Deref for GlobalRwDarcReadGuard<T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.darc.darc.data.get() }
}
}
impl<T> Clone for GlobalRwDarcReadGuard<T> {
fn clone(&self) -> Self {
tracing::trace!(
"glboal_rw_darc_read_guard[{:?}] deserialize_update_cnts {:?}",
self.darc.darc.id,
self.darc.inner()
);
self.local_cnt.fetch_add(1, Ordering::SeqCst);
GlobalRwDarcReadGuard {
darc: self.darc.clone(),
marker: PhantomData,
local_cnt: self.local_cnt.clone(),
}
}
}
impl<T: 'static> Drop for GlobalRwDarcReadGuard<T> {
fn drop(&mut self) {
trace!(target: "drop", "begin drop GlobalRwDarcReadGuard");
if self.local_cnt.fetch_sub(1, Ordering::SeqCst) == 1 {
let inner = self.darc.inner();
let team = inner.darc_rt_team();
let remote_rwlock_addr = team.lamellae.comm().remote_addr(
0,
inner as *const DarcInner<DistRwLock<T>> as *const () as usize,
);
let _am = team.spawn_am_pe_tg(
0,
UnlockAm {
rwlock_addr: remote_rwlock_addr,
orig_pe: team.team_pe.expect("darcs cant exist on non team members"),
lock_type: LockType::Read,
},
Some(inner.am_counters()),
);
}
trace!(target: "drop", "end drop GlobalRwDarcReadGuard");
}
}
impl<T: fmt::Debug> fmt::Debug for GlobalRwDarcReadGuard<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe { fmt::Debug::fmt(&self.darc.darc.data.get().as_ref(), f) }
}
}
pub struct GlobalRwDarcWriteGuard<T: 'static> {
pub(crate) darc: GlobalRwDarc<T>,
pub(crate) marker: PhantomData<&'static mut T>,
}
impl<T> Deref for GlobalRwDarcWriteGuard<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &*self.darc.darc.data.get() }
}
}
impl<T> DerefMut for GlobalRwDarcWriteGuard<T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.darc.darc.data.get() }
}
}
impl<T: 'static> Drop for GlobalRwDarcWriteGuard<T> {
fn drop(&mut self) {
trace!(target: "drop", "begin drop GlobalRwDarcWriteGuard");
let inner = self.darc.inner();
let team = inner.darc_rt_team();
let remote_rwlock_addr = team.lamellae.comm().remote_addr(
0,
inner as *const DarcInner<DistRwLock<T>> as *const () as usize,
);
let _am = team.spawn_am_pe_tg(
0,
UnlockAm {
rwlock_addr: remote_rwlock_addr,
orig_pe: team.team_pe.expect("darcs cant exist on non team members"),
lock_type: LockType::Write,
},
Some(inner.am_counters()),
);
trace!(target: "drop", "end drop GlobalRwDarcWriteGuard");
}
}
impl<T: fmt::Debug> fmt::Debug for GlobalRwDarcWriteGuard<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe { fmt::Debug::fmt(&self.darc.darc.data.get().as_ref(), f) }
}
}
pub struct GlobalRwDarcCollectiveWriteGuard<T: 'static> {
pub(crate) darc: GlobalRwDarc<T>,
pub(crate) collective_cnt: usize,
pub(crate) marker: PhantomData<&'static mut T>,
}
impl<T> Deref for GlobalRwDarcCollectiveWriteGuard<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &*self.darc.darc.data.get() }
}
}
impl<T> DerefMut for GlobalRwDarcCollectiveWriteGuard<T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.darc.darc.data.get() }
}
}
impl<T: 'static> Drop for GlobalRwDarcCollectiveWriteGuard<T> {
fn drop(&mut self) {
trace!(target: "drop", "begin drop GlobalRwDarcCollectiveWriteGuard");
let inner = self.darc.inner();
let team = inner.darc_rt_team();
let remote_rwlock_addr = team.lamellae.comm().remote_addr(
0,
inner as *const DarcInner<DistRwLock<T>> as *const () as usize,
);
let _am = team.spawn_am_pe_tg(
0,
UnlockAm {
rwlock_addr: remote_rwlock_addr,
orig_pe: team.team_pe.expect("darcs cant exist on non team members"),
lock_type: LockType::CollectiveWrite(self.collective_cnt),
},
Some(inner.am_counters()),
);
trace!(target: "drop", "end drop GlobalRwDarcCollectiveWriteGuard");
}
}
impl<T: fmt::Debug> fmt::Debug for GlobalRwDarcCollectiveWriteGuard<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe { fmt::Debug::fmt(&self.darc.darc.data.get().as_ref(), f) }
}
}
#[derive(serde::Serialize, serde::Deserialize, Debug)]
pub struct GlobalRwDarc<T: 'static> {
#[serde(
serialize_with = "globalrw_serialize2",
deserialize_with = "globalrw_from_ndarc2"
)]
pub(crate) darc: Darc<DistRwLock<T>>,
}
unsafe impl<T: Send> Send for GlobalRwDarc<T> {} unsafe impl<T: Send> Sync for GlobalRwDarc<T> {}
impl<T> LamellarEnv for GlobalRwDarc<T> {
fn my_pe(&self) -> usize {
self.darc.my_pe()
}
fn num_pes(&self) -> usize {
self.darc.num_pes()
}
fn num_threads_per_pe(&self) -> usize {
self.darc.num_threads_per_pe()
}
fn world(&self) -> Arc<LamellarTeam> {
self.darc.world()
}
fn team(&self) -> Arc<LamellarTeam> {
self.darc.team().team()
}
}
impl<T> crate::active_messaging::DarcSerde for GlobalRwDarc<T> {
fn ser(&self, num_pes: usize, darcs: &mut Vec<RemotePtr>) {
self.darc.serialize_update_cnts(num_pes);
darcs.push(RemotePtr::NetworkDarc(self.darc.clone().into()));
}
}
impl<T> GlobalRwDarc<T> {
pub(crate) fn inner(&self) -> &DarcInner<DistRwLock<T>> {
self.darc.inner()
}
#[doc(hidden)]
pub fn serialize_update_cnts(&self, cnt: usize) {
self.inner()
.dist_cnt
.fetch_add(cnt, std::sync::atomic::Ordering::SeqCst);
}
#[doc(hidden)]
pub fn deserialize_update_cnts(&self) {
self.inner().inc_pe_ref_count(self.darc.src_pe, 1); }
#[doc(hidden)]
pub fn print(&self) {
println!(
"--------\norig: {:?} 0x{:x} {:?}\n--------",
self.darc.src_pe,
self.darc.inner.addr(),
self.inner()
);
}
#[doc(alias("One-sided", "onesided"))]
pub fn read(&self) -> GlobalRwDarcReadHandle<T> {
let inner = self.inner();
let team = inner.darc_rt_team();
let remote_rwlock_addr = team.lamellae.comm().remote_addr(
0,
inner as *const DarcInner<DistRwLock<T>> as *const () as usize,
);
let am = team.exec_am_pe_tg(
0,
LockAm {
rwlock_addr: remote_rwlock_addr,
orig_pe: team.team_pe.expect("darcs cant exist on non team members"),
lock_type: LockType::Read,
},
Some(inner.am_counters()),
);
GlobalRwDarcReadHandle {
darc: self.clone(),
lock_am: am,
}
}
#[doc(alias("One-sided", "onesided"))]
pub fn write(&self) -> GlobalRwDarcWriteHandle<T> {
let inner = self.inner();
let team = inner.darc_rt_team();
let remote_rwlock_addr = team.lamellae.comm().remote_addr(
0,
inner as *const DarcInner<DistRwLock<T>> as *const () as usize,
);
let am = team.exec_am_pe_tg(
0,
LockAm {
rwlock_addr: remote_rwlock_addr,
orig_pe: team.team_pe.expect("darcs cant exist on non team members"),
lock_type: LockType::Write,
},
Some(inner.am_counters()),
);
GlobalRwDarcWriteHandle {
darc: self.clone(),
lock_am: am,
}
}
#[doc(alias("Collective"))]
pub fn collective_write(&self) -> GlobalRwDarcCollectiveWriteHandle<T> {
let inner = self.inner();
let team = inner.darc_rt_team();
let remote_rwlock_addr = team.lamellae.comm().remote_addr(
0,
inner as *const DarcInner<DistRwLock<T>> as *const () as usize,
);
let collective_cnt = inner.item().collective_cnt.fetch_add(1, Ordering::SeqCst);
let am = team.exec_am_pe_tg(
0,
LockAm {
rwlock_addr: remote_rwlock_addr,
orig_pe: team.team_pe.expect("darcs cant exist on non team members"),
lock_type: LockType::CollectiveWrite(collective_cnt),
},
Some(inner.am_counters()),
);
GlobalRwDarcCollectiveWriteHandle {
darc: self.clone(),
collective_cnt,
lock_am: am,
}
}
}
impl<T: Send> GlobalRwDarc<T> {
#[doc(alias = "Collective")]
pub fn new<U: Clone + Into<IntoLamellarTeam>>(team: U, item: T) -> GlobalRwDarcHandle<T> {
let team = team.into().team.clone();
let locked_item = DistRwLock::new(item, team.clone());
GlobalRwDarcHandle {
team: team.clone(),
launched: false,
creation_future: Box::pin(Darc::async_try_new_with_drop(
team.clone(),
locked_item,
DarcMode::GlobalRw,
Some(GlobalRwDarc::drop),
)),
}
}
pub(crate) fn drop(lock: &mut DistRwLock<T>) -> bool {
lock.dirty_num_locks() == 0
}
#[doc(alias = "Collective")]
pub fn into_darc(self) -> IntoDarcHandle<T> {
let inner = self.darc.inner.clone();
let wrapped_lock = DarcCommPtr {
alloc: CommAlloc {
inner_alloc: inner.alloc.inner_alloc.clone(),
},
_phantom: PhantomData::<DarcInner<DistRwLock<T>>>,
};
let team = self.darc.inner().darc_rt_team();
IntoDarcHandle {
darc: self.into(),
team,
launched: false,
outstanding_future: Box::pin(async move {
while wrapped_lock.item().dirty_num_locks() != 0 {
async_std::task::yield_now().await;
}
DarcInner::block_on_outstanding(inner, DarcMode::Darc, 0).await;
}),
}
}
#[doc(alias = "Collective")]
pub fn into_localrw(self) -> IntoLocalRwDarcHandle<T> {
let inner = self.darc.inner.clone();
let wrapped_lock = DarcCommPtr {
alloc: CommAlloc {
inner_alloc: inner.alloc.inner_alloc.clone(),
},
_phantom: PhantomData::<DarcInner<DistRwLock<T>>>,
};
let team = self.darc.inner().darc_rt_team();
IntoLocalRwDarcHandle {
darc: self.into(),
team,
launched: false,
outstanding_future: Box::pin(async move {
while wrapped_lock.item().dirty_num_locks() != 0 {
async_std::task::yield_now().await;
}
DarcInner::block_on_outstanding(inner, DarcMode::LocalRw, 0).await;
}),
}
}
}
impl<T> Clone for GlobalRwDarc<T> {
fn clone(&self) -> Self {
GlobalRwDarc {
darc: self.darc.clone(),
}
}
}
impl<T: fmt::Display + Send> fmt::Display for GlobalRwDarc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe { fmt::Display::fmt(&self.inner().item().data.get().as_ref().unwrap(), f) }
}
}
pub(crate) fn globalrw_serialize2<S, T>(
globalrw: &Darc<DistRwLock<T>>,
s: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
__NetworkDarc::from(globalrw).serialize(s)
}
pub(crate) fn globalrw_from_ndarc2<'de, D, T>(
deserializer: D,
) -> Result<Darc<DistRwLock<T>>, D::Error>
where
D: Deserializer<'de>,
{
let ndarc: __NetworkDarc = Deserialize::deserialize(deserializer)?;
Ok(Darc::from(ndarc))
}