use std::borrow::Borrow;
use std::cell::UnsafeCell;
use std::ops::{Deref, DerefMut};
use std::ptr;
use crate::context::blocked::BlockedClient;
use crate::{raw, Context, RedisResult};
pub struct RedisGILGuardScope<'ctx, 'mutex, T, G: RedisLockIndicator> {
_context: &'ctx G,
mutex: &'mutex RedisGILGuard<T>,
}
impl<'ctx, 'mutex, T, G: RedisLockIndicator> Deref for RedisGILGuardScope<'ctx, 'mutex, T, G> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.mutex.obj.get() }
}
}
impl<'ctx, 'mutex, T, G: RedisLockIndicator> DerefMut for RedisGILGuardScope<'ctx, 'mutex, T, G> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.mutex.obj.get() }
}
}
pub unsafe trait RedisLockIndicator {}
pub struct RedisGILGuard<T> {
obj: UnsafeCell<T>,
}
impl<T> RedisGILGuard<T> {
pub fn new(obj: T) -> RedisGILGuard<T> {
RedisGILGuard {
obj: UnsafeCell::new(obj),
}
}
pub fn lock<'mutex, 'ctx, G: RedisLockIndicator>(
&'mutex self,
context: &'ctx G,
) -> RedisGILGuardScope<'ctx, 'mutex, T, G> {
RedisGILGuardScope {
_context: context,
mutex: self,
}
}
}
impl<T: Default> Default for RedisGILGuard<T> {
fn default() -> Self {
Self::new(T::default())
}
}
unsafe impl<T> Sync for RedisGILGuard<T> {}
unsafe impl<T> Send for RedisGILGuard<T> {}
pub struct ContextGuard {
ctx: Context,
}
unsafe impl RedisLockIndicator for ContextGuard {}
impl Drop for ContextGuard {
fn drop(&mut self) {
unsafe {
raw::RedisModule_ThreadSafeContextUnlock.unwrap()(self.ctx.ctx);
raw::RedisModule_FreeThreadSafeContext.unwrap()(self.ctx.ctx);
};
}
}
impl Deref for ContextGuard {
type Target = Context;
fn deref(&self) -> &Self::Target {
&self.ctx
}
}
impl Borrow<Context> for ContextGuard {
fn borrow(&self) -> &Context {
&self.ctx
}
}
pub struct DetachedFromClient;
pub struct ThreadSafeContext<B: Send> {
pub(crate) ctx: *mut raw::RedisModuleCtx,
#[allow(dead_code)]
blocked_client: B,
}
unsafe impl<B: Send> Send for ThreadSafeContext<B> {}
unsafe impl<B: Send> Sync for ThreadSafeContext<B> {}
impl ThreadSafeContext<DetachedFromClient> {
#[must_use]
pub fn new() -> Self {
let ctx = unsafe { raw::RedisModule_GetThreadSafeContext.unwrap()(ptr::null_mut()) };
Self {
ctx,
blocked_client: DetachedFromClient,
}
}
}
impl Default for ThreadSafeContext<DetachedFromClient> {
fn default() -> Self {
Self::new()
}
}
impl ThreadSafeContext<BlockedClient> {
#[must_use]
pub fn with_blocked_client(blocked_client: BlockedClient) -> Self {
let ctx = unsafe { raw::RedisModule_GetThreadSafeContext.unwrap()(blocked_client.inner) };
Self {
ctx,
blocked_client,
}
}
#[allow(clippy::must_use_candidate)]
pub fn reply(&self, r: RedisResult) -> raw::Status {
let ctx = Context::new(self.ctx);
ctx.reply(r)
}
}
impl<B: Send> ThreadSafeContext<B> {
pub fn lock(&self) -> ContextGuard {
unsafe { raw::RedisModule_ThreadSafeContextLock.unwrap()(self.ctx) };
let ctx = unsafe { raw::RedisModule_GetThreadSafeContext.unwrap()(ptr::null_mut()) };
let ctx = Context::new(ctx);
ContextGuard { ctx }
}
}
impl<B: Send> Drop for ThreadSafeContext<B> {
fn drop(&mut self) {
unsafe { raw::RedisModule_FreeThreadSafeContext.unwrap()(self.ctx) };
}
}