packages feed

moffy-0.1.0.0: src/Control/Moffy/Event/Lock/Internal.hs

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE DataKinds, TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module Control.Moffy.Event.Lock.Internal (
	-- * Type Synonym
	LockEv, GetThreadIdNewLockId, GetThreadIdGetLock, SingletonUnlock,
	-- * Event Type
	NewLockId(..), pattern OccNewLockId, GetLock(..), pattern OccGetLock,
	Unlock(..), pattern OccUnlock, LockId(..),
	-- * Event
	newLockId, withLock, withLockSig ) where

import Control.Moffy (Sig, React, Request(..), Adjustable, adjust, await, waitFor, adjustSig)
import Control.Moffy.Event.ThreadId (GetThreadId, getThreadId, ThreadId)
import Data.Type.Set (numbered, pattern Nil, Singleton, (:-), (:+:))
import Data.OneOrMore (Selectable(..))
import Data.Bool (bool)

---------------------------------------------------------------------------

-- * LOCK ID
-- * EVENT
--	+ NEW LOCK ID
--	+ GET LOCK
--	+ UNLOCK
-- * WITH LOCK

---------------------------------------------------------------------------
-- LOCK ID
---------------------------------------------------------------------------

newtype LockId = LockId Int deriving (Show, Eq)

---------------------------------------------------------------------------
-- EVENT
---------------------------------------------------------------------------

-- NEW LOCK ID

newtype NewLockId = NewLockIdReq ThreadId deriving (Show, Eq)
numbered [t| NewLockId |]
instance Selectable NewLockId where l `select` _r = l
instance Request NewLockId where
	data Occurred NewLockId = OccNewLockId LockId ThreadId

type GetThreadIdNewLockId = GetThreadId :- NewLockId :- 'Nil

newLockId :: React s GetThreadIdNewLockId LockId
newLockId = adjust getThreadId >>= \t -> newLockId `maybe` pure
	=<< (adjust . await (NewLockIdReq t))
		\(OccNewLockId i t') -> bool Nothing (Just i) $ t == t'

-- GET LOCK

data GetLock = GetLockReq LockId ThreadId RetryTime deriving (Show, Eq)
type RetryTime = Int
numbered [t| GetLock |]
instance Selectable GetLock where
	l@(GetLockReq _ _ rtl) `select` r@(GetLockReq _ _ rtr)
		| rtl >= rtr = l | otherwise = r
instance Request GetLock where
	data Occurred GetLock = OccGetLock LockId ThreadId

type GetThreadIdGetLock = GetThreadId :- GetLock :- 'Nil

getLock :: LockId -> RetryTime -> React s GetThreadIdGetLock ()
getLock i rt = adjust getThreadId >>= \t -> getLock i (rt + 1) `bool` pure ()
	=<< (adjust . await (GetLockReq i t rt))
		\(OccGetLock i' t') -> i == i' && t == t'

-- UNLOCK

newtype Unlock = UnlockReq LockId deriving Show
numbered [t| Unlock |]
instance Selectable Unlock where l `select` _r = l
instance Request Unlock where data Occurred Unlock = OccUnlock

type SingletonUnlock = Singleton Unlock

unlock :: LockId -> React s (Singleton Unlock) ()
unlock l = await (UnlockReq l) \OccUnlock -> ()

---------------------------------------------------------------------------
-- WITH LOCK
---------------------------------------------------------------------------

type LockEv = NewLockId :- GetLock :- Unlock :- 'Nil

withLock :: (
	(es :+: es') ~ es',
	(GetThreadIdGetLock :+: es') ~ es', (SingletonUnlock :+: es') ~ es',
	Adjustable es es',
	Adjustable GetThreadIdGetLock es', Adjustable SingletonUnlock es' ) =>
	LockId -> React s es a -> React s es' a
withLock l act = adjust (getLock l 0) >> adjust act <* adjust (unlock l)

withLockSig :: (
	(es :+: es') ~ es',
	(GetThreadIdGetLock :+: es') ~ es', (SingletonUnlock :+: es') ~ es',
	Adjustable es es',
	Adjustable GetThreadIdGetLock es', Adjustable SingletonUnlock es' ) =>
	LockId -> Sig s es a r -> Sig s es' a r
withLockSig l s = do
	waitFor . adjust $ getLock l 0
	adjustSig s <* waitFor (adjust $ unlock l)