diff options
author | Ben Gamari <ben@smart-cactus.org> | 2022-08-04 15:24:31 -0400 |
---|---|---|
committer | GHC GitLab CI <ghc-ci@gitlab-haskell.org> | 2022-08-05 13:09:27 -0400 |
commit | 19bb7ea83b0599dcd214d2edc0f15bfc840bfccf (patch) | |
tree | ebe483eaa8ab3b4d272ef34d205bdabfd8fdacda | |
parent | f3b6d424e05f80e9ab964df7d3ecabc584955229 (diff) | |
download | haskell-wip/kill-ioport.tar.gz |
Kill IOPort#wip/kill-ioport
24 files changed, 19 insertions, 503 deletions
diff --git a/compiler/GHC/Builtin/Types/Prim.hs b/compiler/GHC/Builtin/Types/Prim.hs index 2eec67613d..c842b97a44 100644 --- a/compiler/GHC/Builtin/Types/Prim.hs +++ b/compiler/GHC/Builtin/Types/Prim.hs @@ -71,7 +71,6 @@ module GHC.Builtin.Types.Prim( mutVarPrimTyCon, mkMutVarPrimTy, mVarPrimTyCon, mkMVarPrimTy, - ioPortPrimTyCon, mkIOPortPrimTy, tVarPrimTyCon, mkTVarPrimTy, stablePtrPrimTyCon, mkStablePtrPrimTy, stableNamePrimTyCon, mkStableNamePrimTy, @@ -182,7 +181,6 @@ exposedPrimTyCons , mutableByteArrayPrimTyCon , smallMutableArrayPrimTyCon , mVarPrimTyCon - , ioPortPrimTyCon , tVarPrimTyCon , mutVarPrimTyCon , realWorldTyCon @@ -227,7 +225,7 @@ charPrimTyConName, intPrimTyConName, int8PrimTyConName, int16PrimTyConName, int3 arrayPrimTyConName, smallArrayPrimTyConName, byteArrayPrimTyConName, mutableArrayPrimTyConName, mutableByteArrayPrimTyConName, smallMutableArrayPrimTyConName, mutVarPrimTyConName, mVarPrimTyConName, - ioPortPrimTyConName, tVarPrimTyConName, stablePtrPrimTyConName, + tVarPrimTyConName, stablePtrPrimTyConName, stableNamePrimTyConName, compactPrimTyConName, bcoPrimTyConName, weakPrimTyConName, threadIdPrimTyConName, eqPrimTyConName, eqReprPrimTyConName, eqPhantPrimTyConName, @@ -259,7 +257,6 @@ mutableArrayPrimTyConName = mkPrimTc (fsLit "MutableArray#") mutableArrayPri mutableByteArrayPrimTyConName = mkPrimTc (fsLit "MutableByteArray#") mutableByteArrayPrimTyConKey mutableByteArrayPrimTyCon smallMutableArrayPrimTyConName= mkPrimTc (fsLit "SmallMutableArray#") smallMutableArrayPrimTyConKey smallMutableArrayPrimTyCon mutVarPrimTyConName = mkPrimTc (fsLit "MutVar#") mutVarPrimTyConKey mutVarPrimTyCon -ioPortPrimTyConName = mkPrimTc (fsLit "IOPort#") ioPortPrimTyConKey ioPortPrimTyCon mVarPrimTyConName = mkPrimTc (fsLit "MVar#") mVarPrimTyConKey mVarPrimTyCon tVarPrimTyConName = mkPrimTc (fsLit "TVar#") tVarPrimTyConKey tVarPrimTyCon stablePtrPrimTyConName = mkPrimTc (fsLit "StablePtr#") stablePtrPrimTyConKey stablePtrPrimTyCon @@ -1019,20 +1016,6 @@ mkMutVarPrimTy s elt = TyConApp mutVarPrimTyCon [getLevity elt, s, elt] {- ************************************************************************ * * -\subsection[TysPrim-io-port-var]{The synchronizing I/O Port type} -* * -************************************************************************ --} - -ioPortPrimTyCon :: TyCon -ioPortPrimTyCon = pcPrimTyCon_LevPolyLastArg ioPortPrimTyConName [Nominal, Representational] unliftedRepTy - -mkIOPortPrimTy :: Type -> Type -> Type -mkIOPortPrimTy s elt = TyConApp ioPortPrimTyCon [getLevity elt, s, elt] - -{- -************************************************************************ -* * The synchronizing variable type \subsection[TysPrim-synch-var]{The synchronizing variable type} * * diff --git a/compiler/GHC/Builtin/primops.txt.pp b/compiler/GHC/Builtin/primops.txt.pp index ac03c20dbd..ac5f1d5934 100644 --- a/compiler/GHC/Builtin/primops.txt.pp +++ b/compiler/GHC/Builtin/primops.txt.pp @@ -2800,43 +2800,6 @@ primop IsEmptyMVarOp "isEmptyMVar#" GenPrimOp ------------------------------------------------------------------------ -section "Synchronized I/O Ports" - {Operations on 'IOPort#'s. } ------------------------------------------------------------------------- - -primtype IOPort# s a - { A shared I/O port is almost the same as an 'MVar#'. - The main difference is that IOPort has no deadlock detection or - deadlock breaking code that forcibly releases the lock. } - -primop NewIOPortOp "newIOPort#" GenPrimOp - State# s -> (# State# s, IOPort# s v #) - {Create new 'IOPort#'; initially empty.} - with - out_of_line = True - has_side_effects = True - -primop ReadIOPortOp "readIOPort#" GenPrimOp - IOPort# s v -> State# s -> (# State# s, v #) - {If 'IOPort#' is empty, block until it becomes full. - Then remove and return its contents, and set it empty. - Throws an 'IOPortException' if another thread is already - waiting to read this 'IOPort#'.} - with - out_of_line = True - has_side_effects = True - -primop WriteIOPortOp "writeIOPort#" GenPrimOp - IOPort# s v -> v -> State# s -> (# State# s, Int# #) - {If 'IOPort#' is full, immediately return with integer 0, - throwing an 'IOPortException'. - Otherwise, store value arg as 'IOPort#''s new contents, - and return with integer 1. } - with - out_of_line = True - has_side_effects = True - ------------------------------------------------------------------------- section "Delay/wait operations" ------------------------------------------------------------------------ diff --git a/compiler/GHC/StgToCmm/Prim.hs b/compiler/GHC/StgToCmm/Prim.hs index 5d459ba7ad..c4e6726411 100644 --- a/compiler/GHC/StgToCmm/Prim.hs +++ b/compiler/GHC/StgToCmm/Prim.hs @@ -1584,9 +1584,6 @@ emitPrimOp cfg primop = ReadMVarOp -> alwaysExternal TryReadMVarOp -> alwaysExternal IsEmptyMVarOp -> alwaysExternal - NewIOPortOp -> alwaysExternal - ReadIOPortOp -> alwaysExternal - WriteIOPortOp -> alwaysExternal DelayOp -> alwaysExternal WaitReadOp -> alwaysExternal WaitWriteOp -> alwaysExternal diff --git a/libraries/base/GHC/Event/Windows.hsc b/libraries/base/GHC/Event/Windows.hsc index 778d6e08e5..ba4985211b 100644 --- a/libraries/base/GHC/Event/Windows.hsc +++ b/libraries/base/GHC/Event/Windows.hsc @@ -110,7 +110,6 @@ import qualified GHC.Event.Array as A import GHC.Base import GHC.Conc.Sync import GHC.IO -import GHC.IOPort import GHC.Num import GHC.Real import GHC.Enum (maxBound) @@ -164,7 +163,7 @@ import {-# SOURCE #-} Debug.Trace (traceEventIO) -- fact that something else has finished the remainder of their queue or must -- have a guarantee to never block. In this implementation we strive to -- never block. This is achieved by not having the worker threads call out --- to any user code, and to have the IOPort synchronization primitive never +-- to any user code, and to have the MVar synchronization primitive never -- block. This means if the port is full the message is lost, however we -- have an invariant that the port can never be full and have a waiting -- receiver. As such, dropping the message does not change anything as there @@ -535,11 +534,11 @@ withOverlappedEx :: forall a. -> CompletionCallback (IOResult a) -> IO (IOResult a) withOverlappedEx mgr fname h async offset startCB completionCB = do - signal <- newEmptyIOPort :: IO (IOPort (IOResult a)) + signal <- newEmptyMVar :: IO (MVar (IOResult a)) let signalReturn a = failIfFalse_ (dbgMsg "signalReturn") $ - writeIOPort signal (IOSuccess a) + tryPutMVar signal (IOSuccess a) signalThrow ex = failIfFalse_ (dbgMsg "signalThrow") $ - writeIOPort signal (IOFailed ex) + tryPutMVar signal (IOFailed ex) mask_ $ do let completionCB' e b = do result <- completionCB e b @@ -683,7 +682,7 @@ withOverlappedEx mgr fname h async offset startCB completionCB = do registerAlertableWait delay return $ IOFailed Nothing let runner = do debugIO $ (dbgMsg ":: waiting ") ++ " | " ++ show lpol - res <- readIOPort signal `catch` cancel + res <- readMVar signal `catch` cancel debugIO $ dbgMsg ":: signaled " case res of IOFailed err -> FFI.throwWinErr fname (maybe 0 fromIntegral err) @@ -716,7 +715,7 @@ withOverlappedEx mgr fname h async offset startCB completionCB = do let err' = fromIntegral err debugIO $ dbgMsg $ ":: done callback: " ++ show err' ++ " - " ++ show numBytes completionCB err' (fromIntegral numBytes) - else readIOPort signal + else readMVar signal CbError err -> do reqs3 <- removeRequest debugIO $ "-1.. " ++ show reqs3 ++ " requests queued." @@ -736,10 +735,10 @@ withOverlappedEx mgr fname h async offset startCB completionCB = do -- Uses an inline definition of threadDelay to prevent an import -- cycle. let usecs = 250 -- 0.25ms - m <- newEmptyIOPort + m <- newEmptyMVar reg <- registerTimeout mgr usecs $ - writeIOPort m () >> return () - readIOPort m `onException` unregisterTimeout mgr reg + putMVar m () >> return () + readMVar m `onException` unregisterTimeout mgr reg | otherwise = sleepBlock 1 -- 1 ms waitForCompletion :: HANDLE -> Ptr FFI.OVERLAPPED -> IO (CbResult Int) waitForCompletion fhndl lpol = do diff --git a/libraries/base/GHC/Event/Windows/Thread.hs b/libraries/base/GHC/Event/Windows/Thread.hs index 57faa9de80..96319ecd7c 100644 --- a/libraries/base/GHC/Event/Windows/Thread.hs +++ b/libraries/base/GHC/Event/Windows/Thread.hs @@ -11,7 +11,7 @@ import GHC.Conc.Sync import GHC.Base import GHC.Event.Windows import GHC.IO -import GHC.IOPort +import GHC.MVar ensureIOManagerIsRunning :: IO () ensureIOManagerIsRunning = wakeupIOManager @@ -21,10 +21,10 @@ interruptIOManager = interruptSystemManager threadDelay :: Int -> IO () threadDelay usecs = mask_ $ do - m <- newEmptyIOPort + m <- newEmptyMVar mgr <- getSystemManager - reg <- registerTimeout mgr usecs $ writeIOPort m () >> return () - readIOPort m `onException` unregisterTimeout mgr reg + reg <- registerTimeout mgr usecs $ putMVar m () >> return () + readMVar m `onException` unregisterTimeout mgr reg registerDelay :: Int -> IO (TVar Bool) registerDelay usecs = do diff --git a/libraries/base/GHC/Exts.hs b/libraries/base/GHC/Exts.hs index f5b2498542..45c4b0b622 100755 --- a/libraries/base/GHC/Exts.hs +++ b/libraries/base/GHC/Exts.hs @@ -58,7 +58,6 @@ module GHC.Exts sameMVar#, sameMutVar#, sameTVar#, - sameIOPort#, -- ** Compat wrapper atomicModifyMutVar#, diff --git a/libraries/base/GHC/IO/Buffer.hs b/libraries/base/GHC/IO/Buffer.hs index 992733d645..888eaaffb9 100644 --- a/libraries/base/GHC/IO/Buffer.hs +++ b/libraries/base/GHC/IO/Buffer.hs @@ -218,10 +218,10 @@ data BufferState = ReadBuffer | WriteBuffer deriving Eq -- ^ @since 4.2.0.0 withBuffer :: Buffer e -> (Ptr e -> IO a) -> IO a -withBuffer Buffer{ bufRaw=raw } f = withForeignPtr (castForeignPtr raw) f +withBuffer Buffer{ bufRaw=raw } f = unsafeWithForeignPtr (castForeignPtr raw) f withRawBuffer :: RawBuffer e -> (Ptr e -> IO a) -> IO a -withRawBuffer raw f = withForeignPtr (castForeignPtr raw) f +withRawBuffer raw f = unsafeWithForeignPtr (castForeignPtr raw) f isEmptyBuffer :: Buffer e -> Bool isEmptyBuffer Buffer{ bufL=l, bufR=r } = l == r diff --git a/libraries/base/GHC/IO/Windows/Handle.hsc b/libraries/base/GHC/IO/Windows/Handle.hsc index 54667b8e85..43dd59efd5 100644 --- a/libraries/base/GHC/IO/Windows/Handle.hsc +++ b/libraries/base/GHC/IO/Windows/Handle.hsc @@ -921,8 +921,7 @@ openFile' filepath iomode non_blocking tmp_opts = -- handle. For WinIO we always use FILE_FLAG_OVERLAPPED, which -- means we always issue asynchronous file operation using an -- OVERLAPPED structure. All blocking, if required must be done - -- on the Haskell side by using existing mechanisms such as MVar - -- or IOPorts. + -- on the Haskell side by using existing mechanisms such as MVars. then #{const FILE_FLAG_OVERLAPPED} -- I believe most haskell programs do sequential scans, so -- optimize for the common case. Though ideally, this would diff --git a/libraries/base/GHC/IOPort.hs b/libraries/base/GHC/IOPort.hs index 46a553ca51..e69de29bb2 100644 --- a/libraries/base/GHC/IOPort.hs +++ b/libraries/base/GHC/IOPort.hs @@ -1,122 +0,0 @@ -{-# LANGUAGE Unsafe #-} -{-# LANGUAGE NoImplicitPrelude, MagicHash, UnboxedTuples #-} -{-# OPTIONS_GHC -funbox-strict-fields #-} -{-# OPTIONS_HADDOCK hide #-} - ------------------------------------------------------------------------------ --- | --- Module : GHC.IOPort --- Copyright : (c) Tamar Christina 2019 --- License : see libraries/base/LICENSE --- --- Maintainer : cvs-ghc@haskell.org --- Stability : internal --- Portability : non-portable (GHC Extensions) --- --- The IOPort type. This is a facility used by the Windows IO subsystem. --- We have strict rules with an I/O Port: --- * writing more than once is an error --- * reading more than once is an error --- --- It gives us the ability to have one thread to block, wait for a result from --- another thread and then being woken up. *Nothing* more. --- --- This type is very much GHC internal. It might be changed or removed without --- notice in future releases. --- ------------------------------------------------------------------------------ - -module GHC.IOPort ( - -- * IOPorts - IOPort(..) - , newIOPort - , newEmptyIOPort - , readIOPort - , writeIOPort - , doubleReadException - ) where - -import GHC.Base -import GHC.Exception -import Text.Show - -data IOPortException = IOPortException deriving Show - -instance Exception IOPortException where - displayException IOPortException = "IOPortException" - - -doubleReadException :: SomeException -doubleReadException = toException IOPortException - -data IOPort a = IOPort (IOPort# RealWorld a) -{- ^ -An 'IOPort' is a synchronising variable, used -for communication between concurrent threads, where one of the threads is -controlled by an external state. e.g. by an I/O action that is serviced by the -runtime. It can be thought of as a box, which may be empty or full. - -It is mostly similar to the behavior of 'Control.Concurrent.MVar.MVar' -except 'writeIOPort' doesn't block if the variable is full and the GC -won't forcibly release the lock if it thinks -there's a deadlock. - -The properties of IOPorts are: -* Writing to an empty IOPort will not block. -* Writing to an full IOPort will not block. It might throw an exception. -* Reading from an IOPort for the second time might throw an exception. -* Reading from a full IOPort will not block, return the value and empty the port. -* Reading from an empty IOPort will block until a write. -* Reusing an IOPort (that is, reading or writing twice) is not supported - and might throw an exception. Even if reads and writes are - interleaved. - -This type is very much GHC internal. It might be changed or removed without -notice in future releases. - --} - --- | @since 4.1.0.0 -instance Eq (IOPort a) where - (IOPort ioport1#) == (IOPort ioport2#) = - isTrue# (sameIOPort# ioport1# ioport2#) - - - --- |Create an 'IOPort' which is initially empty. -newEmptyIOPort :: IO (IOPort a) -newEmptyIOPort = IO $ \ s# -> - case newIOPort# s# of - (# s2#, svar# #) -> (# s2#, IOPort svar# #) - --- |Create an 'IOPort' which contains the supplied value. -newIOPort :: a -> IO (IOPort a) -newIOPort value = - newEmptyIOPort >>= \ ioport -> - writeIOPort ioport value >> - return ioport - --- |Atomically read the the contents of the 'IOPort'. If the 'IOPort' is --- currently empty, 'readIOPort' will wait until it is full. After a --- 'readIOPort', the 'IOPort' is left empty. --- --- There is one important property of 'readIOPort': --- --- * Only a single threads can be blocked on an 'IOPort'. --- -readIOPort :: IOPort a -> IO a -readIOPort (IOPort ioport#) = IO $ \ s# -> readIOPort# ioport# s# - --- |Put a value into an 'IOPort'. If the 'IOPort' is currently full, --- 'writeIOPort' will throw an exception. --- --- There is one important property of 'writeIOPort': --- --- * Only a single thread can be blocked on an 'IOPort'. --- -writeIOPort :: IOPort a -> a -> IO Bool -writeIOPort (IOPort ioport#) x = IO $ \ s# -> - case writeIOPort# ioport# x s# of - (# s, 0# #) -> (# s, False #) - (# s, _ #) -> (# s, True #) - diff --git a/libraries/base/base.cabal b/libraries/base/base.cabal index 58e11e30f7..6bc64c06e2 100644 --- a/libraries/base/base.cabal +++ b/libraries/base/base.cabal @@ -318,8 +318,6 @@ Library Type.Reflection Type.Reflection.Unsafe Unsafe.Coerce - -- TODO: remove - GHC.IOPort reexported-modules: GHC.Num.Integer @@ -348,7 +346,6 @@ Library GHC.Event.IntVar GHC.Event.PSQ GHC.Event.Unique - -- GHC.IOPort -- TODO: hide again after debug GHC.Unicode.Internal.Bits GHC.Unicode.Internal.Char.UnicodeData.GeneralCategory GHC.Unicode.Internal.Char.UnicodeData.SimpleLowerCaseMapping diff --git a/libraries/ghc-heap/GHC/Exts/Heap/Closures.hs b/libraries/ghc-heap/GHC/Exts/Heap/Closures.hs index 13d1ff71c2..2ff2dcb1de 100644 --- a/libraries/ghc-heap/GHC/Exts/Heap/Closures.hs +++ b/libraries/ghc-heap/GHC/Exts/Heap/Closures.hs @@ -237,14 +237,6 @@ data GenClosure b , value :: !b -- ^ Pointer to closure } - -- | An @IOPort#@, with a queue of thread state objects blocking on them - | IOPortClosure - { info :: !StgInfoTable - , queueHead :: !b -- ^ Pointer to head of queue - , queueTail :: !b -- ^ Pointer to tail of queue - , value :: !b -- ^ Pointer to closure - } - -- | A @MutVar#@ | MutVarClosure { info :: !StgInfoTable @@ -418,7 +410,6 @@ allClosures (MutArrClosure {..}) = mccPayload allClosures (SmallMutArrClosure {..}) = mccPayload allClosures (MutVarClosure {..}) = [var] allClosures (MVarClosure {..}) = [queueHead,queueTail,value] -allClosures (IOPortClosure {..}) = [queueHead,queueTail,value] allClosures (FunClosure {..}) = ptrArgs allClosures (BlockingQueueClosure {..}) = [link, blackHole, owner, queue] allClosures (WeakClosure {..}) = [cfinalizers, key, value, finalizer] ++ Data.Foldable.toList weakLink diff --git a/libraries/ghc-prim/GHC/Prim/PtrEq.hs b/libraries/ghc-prim/GHC/Prim/PtrEq.hs index 34285a879a..029bc13de1 100644 --- a/libraries/ghc-prim/GHC/Prim/PtrEq.hs +++ b/libraries/ghc-prim/GHC/Prim/PtrEq.hs @@ -29,7 +29,6 @@ module GHC.Prim.PtrEq sameMutVar#, sameTVar#, sameMVar#, - sameIOPort#, eqStableName# ) where @@ -109,10 +108,6 @@ sameTVar# = reallyUnsafePtrEquality# sameMVar# :: MVar# s a -> MVar# s a -> Int# sameMVar# = reallyUnsafePtrEquality# --- | Compare the underlying pointers of two 'IOPort#'s. -sameIOPort# :: IOPort# s a -> IOPort# s a -> Int# -sameIOPort# = reallyUnsafePtrEquality# - -- Note [Comparing stable names] -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- A StableName# is actually a pointer to a stable name object (SNO) diff --git a/libraries/ghc-prim/changelog.md b/libraries/ghc-prim/changelog.md index 6f665f3fcb..dc0f06b867 100644 --- a/libraries/ghc-prim/changelog.md +++ b/libraries/ghc-prim/changelog.md @@ -31,7 +31,7 @@ - `Array#`, `SmallArray#`, `Weak#`, `StablePtr#`, `StableName#`, - `MutableArray#`, `SmallMutableArray#`, `MutVar#`, - `TVar#`, `MVar#`, `IOPort#`. + `TVar#`, `MVar#` For example, `Array#` used to have kind: @@ -88,8 +88,6 @@ - `STM` operations `atomically#`, `retry#`, `catchRetry#` and `catchSTM#`. - - `newIOPort#`, `readIOPort#`, `writeIOPort#`, - - `mkWeak#`, `mkWeakNoFinalizer#`, `addCFinalizerToWeak#`, `deRefWeak#`, `finalizeWeak#`, - `makeStablePtr#`, `deRefStablePtr#`, `eqStablePtr#`, `makeStableName#`, `stableNameToInt#`, @@ -188,7 +186,6 @@ - `sameMutableArray#`, `sameSmallMutableArray#`, `sameMutableByteArray#` and `sameMutableArrayArray#`, - `sameMutVar#`, `sameTVar#` and`sameMVar#`, - - `sameIOPort#`, - `eqStableName#`. - The following functions have been added to `GHC.Exts`: diff --git a/rts/Prelude.h b/rts/Prelude.h index 2a935f9f90..82552acd4f 100644 --- a/rts/Prelude.h +++ b/rts/Prelude.h @@ -50,7 +50,6 @@ PRELUDE_CLOSURE(base_GHCziIOziException_blockedIndefinitelyOnSTM_closure); PRELUDE_CLOSURE(base_GHCziIOziException_cannotCompactFunction_closure); PRELUDE_CLOSURE(base_GHCziIOziException_cannotCompactPinned_closure); PRELUDE_CLOSURE(base_GHCziIOziException_cannotCompactMutable_closure); -PRELUDE_CLOSURE(base_GHCziIOPort_doubleReadException_closure); PRELUDE_CLOSURE(base_ControlziExceptionziBase_nonTermination_closure); PRELUDE_CLOSURE(base_ControlziExceptionziBase_nestedAtomically_closure); PRELUDE_CLOSURE(base_GHCziEventziThread_blockedOnBadFD_closure); @@ -117,7 +116,6 @@ PRELUDE_INFO(base_GHCziStable_StablePtr_con_info); #define cannotCompactMutable_closure DLL_IMPORT_DATA_REF(base_GHCziIOziException_cannotCompactMutable_closure) #define nonTermination_closure DLL_IMPORT_DATA_REF(base_ControlziExceptionziBase_nonTermination_closure) #define nestedAtomically_closure DLL_IMPORT_DATA_REF(base_ControlziExceptionziBase_nestedAtomically_closure) -#define doubleReadException DLL_IMPORT_DATA_REF(base_GHCziIOPort_doubleReadException_closure) #define absentSumFieldError_closure DLL_IMPORT_DATA_REF(ghczmprim_GHCziPrimziPanic_absentSumFieldError_closure) #define raiseUnderflowException_closure DLL_IMPORT_DATA_REF(ghczmprim_GHCziPrimziException_raiseUnderflow_closure) #define raiseOverflowException_closure DLL_IMPORT_DATA_REF(ghczmprim_GHCziPrimziException_raiseOverflow_closure) diff --git a/rts/PrimOps.cmm b/rts/PrimOps.cmm index 7b760e5702..1ba076bb80 100644 --- a/rts/PrimOps.cmm +++ b/rts/PrimOps.cmm @@ -32,7 +32,6 @@ import pthread_mutex_unlock; import CLOSURE base_ControlziExceptionziBase_nestedAtomically_closure; import CLOSURE base_GHCziIOziException_heapOverflow_closure; import CLOSURE base_GHCziIOziException_blockedIndefinitelyOnMVar_closure; -import CLOSURE base_GHCziIOPort_doubleReadException_closure; import AcquireSRWLockExclusive; import ReleaseSRWLockExclusive; import CLOSURE ghczmprim_GHCziTypes_False_closure; @@ -2070,239 +2069,6 @@ stg_tryReadMVarzh ( P_ mvar, /* :: MVar a */ ) } /* ----------------------------------------------------------------------------- - * IOPort primitives - * - * readIOPort & writeIOPort work as follows. Firstly, an important invariant: - * - * Only one read and one write is allowed for an IOPort. - * Reading or writing to the same port twice will throw an exception. - * - * readIOPort: - * IOPort empty : then add ourselves to the blocking queue - * IOPort full : remove the value from the IOPort, and - * blocking queue empty : return - * blocking queue non-empty : perform the only blocked - * writeIOPort from the queue, and - * wake up the thread - * (IOPort is now empty) - * - * writeIOPort is just the dual of the above algorithm. - * - * How do we "perform a writeIOPort"? Well, By storing the value and prt on the - * stack, same way we do with MVars. Semantically the operations mutate the - * stack the same way so we will re-use the logic and datastructures for MVars - * for IOPort. See stg_block_putmvar and stg_block_takemvar in HeapStackCheck.c - * for the stack layout, and the PerformPut and PerformTake macros below. We - * also re-use the closure types MVAR_CLEAN/_DIRTY for IOPort. - * - * The remaining caveats of MVar thus also apply for an IOPort. The main - * crucial difference between an MVar and IOPort is that the scheduler will not - * be allowed to interrupt a blocked IOPort just because it thinks there's a - * deadlock. This is especially crucial for the non-threaded runtime. - * - * To avoid double reads/writes we set only the head to a MVarTSOQueue when - * a reader queues up on a port. - * We set the tail to the port itself upon reading. We can do this - * since there can only be one reader/writer for the port. In contrast to MVars - * which do need to keep a list of blocked threads. - * - * This means IOPorts have these valid states and transitions: - * - ┌─────────┐ - │ Empty │ head == tail == value == END_TSO_QUEUE - ├─────────┤ - │ │ - write │ │ read - v v - value != END_TSO_QUEUE ┌─────────┐ ┌─────────┐ value == END_TSO_QUEUE - head == END_TSO_QUEUE │ full │ │ reading │ head == queue with single reader - tail == END_TSO_QUEUE └─────────┘ └─────────┘ tail == END_TSO_QUEUE - │ │ - read │ │ write - │ │ - v v - ┌──────────┐ value != END_TSO_QUEUE - │ Used │ head == END_TSO_QUEUE - └──────────┘ tail == ioport - - * - * -------------------------------------------------------------------------- */ - - -stg_readIOPortzh ( P_ ioport /* :: IOPort a */ ) -{ - W_ val, info, tso, q; - - LOCK_CLOSURE(ioport, info); - - /* If the Port is empty, put ourselves on the blocked readers - * list and wait until we're woken up. - */ - if (StgMVar_value(ioport) == stg_END_TSO_QUEUE_closure) { - - // There is or was already another reader, throw exception. - if (StgMVar_head(ioport) != stg_END_TSO_QUEUE_closure || - StgMVar_tail(ioport) != stg_END_TSO_QUEUE_closure) { - unlockClosure(ioport, info); - jump stg_raiseIOzh(base_GHCziIOPort_doubleReadException_closure); - } - - if (info == stg_MVAR_CLEAN_info) { - ccall dirty_MVAR(BaseReg "ptr", ioport "ptr", StgMVar_value(ioport)); - } - - ALLOC_PRIM_WITH_CUSTOM_FAILURE - (SIZEOF_StgMVarTSOQueue, - unlockClosure(ioport, stg_MVAR_DIRTY_info); - GC_PRIM_P(stg_readIOPortzh, ioport)); - - q = Hp - SIZEOF_StgMVarTSOQueue + WDS(1); - - // link = stg_END_TSO_QUEUE_closure since we check that - // there is no other reader above. - StgMVarTSOQueue_link(q) = stg_END_TSO_QUEUE_closure; - StgMVarTSOQueue_tso(q) = CurrentTSO; - - SET_HDR(q, stg_MVAR_TSO_QUEUE_info, CCS_SYSTEM); - // See Note [Heap memory barriers] - prim_write_barrier; - - StgMVar_head(ioport) = q; - StgTSO__link(CurrentTSO) = q; - StgTSO_block_info(CurrentTSO) = ioport; - StgTSO_why_blocked(CurrentTSO) = BlockedOnMVar::I16; - - //Unlocks the closure as well - jump stg_block_readmvar(ioport); - - } - - //This way we can check of there has been a read already. - //Upon reading we set tail to indicate the port is now closed. - if (StgMVar_tail(ioport) == stg_END_TSO_QUEUE_closure) { - StgMVar_tail(ioport) = ioport; - StgMVar_head(ioport) = stg_END_TSO_QUEUE_closure; - } else { - //Or another thread has read already: Throw an exception. - unlockClosure(ioport, info); - jump stg_raiseIOzh(base_GHCziIOPort_doubleReadException_closure); - } - - val = StgMVar_value(ioport); - - unlockClosure(ioport, info); - return (val); -} - -stg_writeIOPortzh ( P_ ioport, /* :: IOPort a */ - P_ val, /* :: a */ ) -{ - W_ info, tso, q; - - LOCK_CLOSURE(ioport, info); - - /* If there is already a value in the port, then raise an exception - as it's the second write. - Correct usages of IOPort should never have a second - write. */ - if (StgMVar_value(ioport) != stg_END_TSO_QUEUE_closure) { - unlockClosure(ioport, info); - jump stg_raiseIOzh(base_GHCziIOPort_doubleReadException_closure); - return (0); - } - - // We are going to mutate the closure, make sure its current pointers - // are marked. - if (info == stg_MVAR_CLEAN_info) { - ccall update_MVAR(BaseReg "ptr", ioport "ptr", StgMVar_value(ioport) "ptr"); - } - - q = StgMVar_head(ioport); -loop: - if (q == stg_END_TSO_QUEUE_closure) { - /* No takes, the IOPort is now full. */ - if (info == stg_MVAR_CLEAN_info) { - ccall dirty_MVAR(BaseReg "ptr", ioport "ptr"); - } - StgMVar_value(ioport) = val; - - unlockClosure(ioport, stg_MVAR_DIRTY_info); - return (1); - } - //Possibly IND added by removeFromMVarBlockedQueue - if (StgHeader_info(q) == stg_IND_info || - StgHeader_info(q) == stg_MSG_NULL_info) { - q = StgInd_indirectee(q); - goto loop; - } - - // There is a readIOPort waiting: wake it up - tso = StgMVarTSOQueue_tso(q); - - // Assert no read has happened yet. - ASSERT(StgMVar_tail(ioport) == stg_END_TSO_QUEUE_closure); - // And there is only one reader queued up. - ASSERT(StgMVarTSOQueue_link(q) == stg_END_TSO_QUEUE_closure); - - // We perform the read here, so set tail/head accordingly. - StgMVar_head(ioport) = stg_END_TSO_QUEUE_closure; - StgMVar_tail(ioport) = ioport; - - // In contrast to MVars we do not need to move on to the - // next element in the waiting list here, as there can only ever - // be one thread blocked on a port. - - ASSERT(StgTSO_block_info(tso) == ioport); - // save why_blocked here, because waking up the thread destroys - // this information - W_ why_blocked; - why_blocked = TO_W_(StgTSO_why_blocked(tso)); - - // actually perform the takeMVar - W_ stack; - stack = StgTSO_stackobj(tso); - if (IS_STACK_CLEAN(stack)) { - ccall dirty_STACK(MyCapability() "ptr", stack "ptr"); - } - PerformTake(stack, val); - - // indicate that the operation has now completed. - StgTSO__link(tso) = stg_END_TSO_QUEUE_closure; - - ccall tryWakeupThread(MyCapability() "ptr", tso); - - // For MVars we loop here, waking up all readers. - // IOPorts however can only have on reader. So we are done - // at this point. - - //Either there was no reader queued, or he must have been - //blocked on BlockedOnMVar - ASSERT(why_blocked == BlockedOnMVar); - - unlockClosure(ioport, info); - return (1); -} -/* ----------------------------------------------------------------------------- - IOPort primitives - -------------------------------------------------------------------------- */ - -stg_newIOPortzh () -{ - W_ ioport; - - ALLOC_PRIM_ (SIZEOF_StgMVar, stg_newIOPortzh); - - ioport = Hp - SIZEOF_StgMVar + WDS(1); - SET_HDR(ioport, stg_MVAR_DIRTY_info,CCCS); - // MVARs start dirty: generation 0 has no mutable list - StgMVar_head(ioport) = stg_END_TSO_QUEUE_closure; - StgMVar_tail(ioport) = stg_END_TSO_QUEUE_closure; - StgMVar_value(ioport) = stg_END_TSO_QUEUE_closure; - - return (ioport); -} - -/* ----------------------------------------------------------------------------- Stable name primitives ------------------------------------------------------------------------- */ diff --git a/rts/RtsSymbols.c b/rts/RtsSymbols.c index 9731f4febf..55a4c03a47 100644 --- a/rts/RtsSymbols.c +++ b/rts/RtsSymbols.c @@ -757,9 +757,6 @@ extern char **environ; SymI_HasDataProto(stg_newMVarzh) \ SymI_HasDataProto(stg_newMutVarzh) \ SymI_HasDataProto(stg_newTVarzh) \ - SymI_HasDataProto(stg_readIOPortzh) \ - SymI_HasDataProto(stg_writeIOPortzh) \ - SymI_HasDataProto(stg_newIOPortzh) \ SymI_HasDataProto(stg_noDuplicatezh) \ SymI_HasDataProto(stg_atomicModifyMutVar2zh) \ SymI_HasDataProto(stg_atomicModifyMutVarzuzh) \ diff --git a/rts/include/stg/MiscClosures.h b/rts/include/stg/MiscClosures.h index c1f9d3e94d..1be3aae075 100644 --- a/rts/include/stg/MiscClosures.h +++ b/rts/include/stg/MiscClosures.h @@ -412,10 +412,6 @@ RTS_FUN_DECL(stg_block_stmwait); RTS_FUN_DECL(stg_block_throwto); RTS_RET(stg_block_throwto); -RTS_FUN_DECL(stg_readIOPortzh); -RTS_FUN_DECL(stg_writeIOPortzh); -RTS_FUN_DECL(stg_newIOPortzh); - /* Entry/exit points from StgStartup.cmm */ RTS_RET(stg_stop_thread); diff --git a/rts/package.conf.in b/rts/package.conf.in index 248b6b9c57..6772bc2fc0 100644 --- a/rts/package.conf.in +++ b/rts/package.conf.in @@ -98,7 +98,6 @@ ld-options: , "-Wl,-u,_base_GHCziIOziException_cannotCompactFunction_closure" , "-Wl,-u,_base_GHCziIOziException_cannotCompactPinned_closure" , "-Wl,-u,_base_GHCziIOziException_cannotCompactMutable_closure" - , "-Wl,-u,_base_GHCziIOPort_doubleReadException_closure" , "-Wl,-u,_base_ControlziExceptionziBase_nonTermination_closure" , "-Wl,-u,_base_ControlziExceptionziBase_nestedAtomically_closure" , "-Wl,-u,_base_GHCziEventziThread_blockedOnBadFD_closure" @@ -211,7 +210,6 @@ ld-options: , "-Wl,-u,base_GHCziIOziException_cannotCompactFunction_closure" , "-Wl,-u,base_GHCziIOziException_cannotCompactPinned_closure" , "-Wl,-u,base_GHCziIOziException_cannotCompactMutable_closure" - , "-Wl,-u,base_GHCziIOPort_doubleReadException_closure" , "-Wl,-u,base_ControlziExceptionziBase_nonTermination_closure" , "-Wl,-u,base_ControlziExceptionziBase_nestedAtomically_closure" , "-Wl,-u,base_GHCziEventziThread_blockedOnBadFD_closure" diff --git a/rts/rts.cabal.in b/rts/rts.cabal.in index 6324829903..f9249da56d 100644 --- a/rts/rts.cabal.in +++ b/rts/rts.cabal.in @@ -266,7 +266,6 @@ library "-Wl,-u,_base_GHCziIOziException_cannotCompactFunction_closure" "-Wl,-u,_base_GHCziIOziException_cannotCompactPinned_closure" "-Wl,-u,_base_GHCziIOziException_cannotCompactMutable_closure" - "-Wl,-u,_base_GHCziIOPort_doubleReadException_closure" "-Wl,-u,_base_ControlziExceptionziBase_nonTermination_closure" "-Wl,-u,_base_ControlziExceptionziBase_nestedAtomically_closure" "-Wl,-u,_base_GHCziEventziThread_blockedOnBadFD_closure" @@ -349,7 +348,6 @@ library "-Wl,-u,base_GHCziIOziException_cannotCompactFunction_closure" "-Wl,-u,base_GHCziIOziException_cannotCompactPinned_closure" "-Wl,-u,base_GHCziIOziException_cannotCompactMutable_closure" - "-Wl,-u,base_GHCziIOPort_doubleReadException_closure" "-Wl,-u,base_ControlziExceptionziBase_nonTermination_closure" "-Wl,-u,base_ControlziExceptionziBase_nestedAtomically_closure" "-Wl,-u,base_GHCziEventziThread_blockedOnBadFD_closure" diff --git a/rts/win32/AsyncWinIO.c b/rts/win32/AsyncWinIO.c index 2c15dbad5a..e8fd8f0b39 100644 --- a/rts/win32/AsyncWinIO.c +++ b/rts/win32/AsyncWinIO.c @@ -147,7 +147,7 @@ * Create a thread to execute "runner" We never truly shut down the IO Manager. While this means we - might block forever on the IOPort if the IO Manager is no longer + might block forever on the MVar if the IO Manager is no longer needed we consider this cheap compared to the complexity of properly handling pausing and resuming of the manager. diff --git a/rts/win32/libHSbase.def b/rts/win32/libHSbase.def index e91d11a688..2634e7c899 100644 --- a/rts/win32/libHSbase.def +++ b/rts/win32/libHSbase.def @@ -43,7 +43,6 @@ EXPORTS base_GHCziIOziException_cannotCompactFunction_closure base_GHCziIOziException_cannotCompactPinned_closure base_GHCziIOziException_cannotCompactMutable_closure - base_GHCziIOPort_doubleReadException_closure base_ControlziExceptionziBase_nonTermination_closure base_ControlziExceptionziBase_nestedAtomically_closure base_GHCziEventziThread_blockedOnBadFD_closure diff --git a/testsuite/tests/primops/should_run/UnliftedIOPort.hs b/testsuite/tests/primops/should_run/UnliftedIOPort.hs index 7bdf0dff7a..e69de29bb2 100644 --- a/testsuite/tests/primops/should_run/UnliftedIOPort.hs +++ b/testsuite/tests/primops/should_run/UnliftedIOPort.hs @@ -1,31 +0,0 @@ -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE BlockArguments #-} -{-# LANGUAGE MagicHash #-} -{-# LANGUAGE StandaloneKindSignatures #-} -{-# LANGUAGE UnboxedTuples #-} -{-# LANGUAGE UnliftedDatatypes #-} - -module Main where - -import Data.Kind -import GHC.Exts -import GHC.IO - -type U :: Type -data U = U Int# - -main :: IO () -main = do - res <- IO \ s0 -> - case newIOPort# s0 of - (# s1, port #) -> - case writeIOPort# port (U 17#) s1 of - (# s2, i #) -> - case catch# (writeIOPort# port (U 19#)) (\ _ s -> (# s, 3# #)) s2 of - (# s3, j #) -> - case readIOPort# port s3 of - (# s4, U r1 #) -> - case catch# (readIOPort# port) (\ _ s -> (# s, U 4# #)) s4 of - (# s5, U r2 #) -> - (# s5, [ I# i, I# j, I# r1, I# r2 ] #) - print res diff --git a/testsuite/tests/primops/should_run/all.T b/testsuite/tests/primops/should_run/all.T index 65c24b1d00..157d41ee74 100644 --- a/testsuite/tests/primops/should_run/all.T +++ b/testsuite/tests/primops/should_run/all.T @@ -45,7 +45,6 @@ test('LevPolyPtrEquality2', normal, compile_and_run, ['']) test('UnliftedArray1', normal, compile_and_run, ['']) test('UnliftedArray2', normal, compile_and_run, ['']) test('UnliftedArrayCAS', normal, compile_and_run, ['']) -test('UnliftedIOPort', normal, compile_and_run, ['']) test('UnliftedMutVar1', normal, compile_and_run, ['']) test('UnliftedMutVar2', normal, compile_and_run, ['']) test('UnliftedMutVar3', normal, compile_and_run, ['']) diff --git a/utils/genprimopcode/Main.hs b/utils/genprimopcode/Main.hs index 6984f4a296..23742880f6 100644 --- a/utils/genprimopcode/Main.hs +++ b/utils/genprimopcode/Main.hs @@ -802,8 +802,6 @@ ppType (TyApp (TyCon "StableName#") [x]) = "mkStableNamePrimTy " ++ ppType x ppType (TyApp (TyCon "MVar#") [x,y]) = "mkMVarPrimTy " ++ ppType x ++ " " ++ ppType y -ppType (TyApp (TyCon "IOPort#") [x,y]) = "mkIOPortPrimTy " ++ ppType x - ++ " " ++ ppType y ppType (TyApp (TyCon "TVar#") [x,y]) = "mkTVarPrimTy " ++ ppType x ++ " " ++ ppType y ppType (TyApp (VecTyCon _ pptc) []) = pptc |