{-# LANGUAGE CPP #-}

{-# OPTIONS_GHC -fno-cse #-}
-- -fno-cse is needed for GLOBAL_VAR's to behave properly

-- | Do not use global variables!
--
-- Global variables are a hack. Do not use them if you can help it.
module GHC.Utils.GlobalVars
   ( v_unsafeHasPprDebug
   , v_unsafeHasNoDebugOutput
   , v_unsafeHasNoStateHack
   , unsafeHasPprDebug
   , unsafeHasNoDebugOutput
   , unsafeHasNoStateHack

   , global
   , consIORef
   , globalM
   , sharedGlobal
   , sharedGlobalM
   )
where

import GHC.Prelude.Basic

import GHC.Conc.Sync ( sharedCAF )

import System.IO.Unsafe
import Data.IORef
import Foreign (Ptr)

#define GLOBAL_VAR(name,value,ty)  \
{-# NOINLINE name #-};             \
name :: IORef (ty);                \
name = global (value);

#define GLOBAL_VAR_M(name,value,ty) \
{-# NOINLINE name #-};              \
name :: IORef (ty);                 \
name = globalM (value);


#define SHARED_GLOBAL_VAR(name,accessor,saccessor,value,ty) \
{-# NOINLINE name #-};                                      \
name :: IORef (ty);                                         \
name = sharedGlobal (value) (accessor);                     \
foreign import ccall unsafe saccessor                       \
  accessor :: Ptr (IORef a) -> IO (Ptr (IORef a));

#define SHARED_GLOBAL_VAR_M(name,accessor,saccessor,value,ty)  \
{-# NOINLINE name #-};                                         \
name :: IORef (ty);                                            \
name = sharedGlobalM (value) (accessor);                       \
foreign import ccall unsafe saccessor                          \
  accessor :: Ptr (IORef a) -> IO (Ptr (IORef a));



#if !MIN_VERSION_GLASGOW_HASKELL(9,3,0,0)

GLOBAL_VAR(v_unsafeHasPprDebug,      False, Bool)
GLOBAL_VAR(v_unsafeHasNoDebugOutput, False, Bool)
GLOBAL_VAR(v_unsafeHasNoStateHack,   False, Bool)

#else
SHARED_GLOBAL_VAR( v_unsafeHasPprDebug
                 , getOrSetLibHSghcGlobalHasPprDebug
                 , "getOrSetLibHSghcGlobalHasPprDebug"
                 , False
                 , Bool )
SHARED_GLOBAL_VAR( v_unsafeHasNoDebugOutput
                 , getOrSetLibHSghcGlobalHasNoDebugOutput
                 , "getOrSetLibHSghcGlobalHasNoDebugOutput"
                 , False
                 , Bool )
SHARED_GLOBAL_VAR( v_unsafeHasNoStateHack
                 , getOrSetLibHSghcGlobalHasNoStateHack
                 , "getOrSetLibHSghcGlobalHasNoStateHack"
                 , False
                 , Bool )
#endif

unsafeHasPprDebug :: Bool
unsafeHasPprDebug :: Bool
unsafeHasPprDebug = forall a. IO a -> a
unsafePerformIO forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> IO a
readIORef IORef Bool
v_unsafeHasPprDebug

unsafeHasNoDebugOutput :: Bool
unsafeHasNoDebugOutput :: Bool
unsafeHasNoDebugOutput = forall a. IO a -> a
unsafePerformIO forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> IO a
readIORef IORef Bool
v_unsafeHasNoDebugOutput

unsafeHasNoStateHack :: Bool
unsafeHasNoStateHack :: Bool
unsafeHasNoStateHack = forall a. IO a -> a
unsafePerformIO forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> IO a
readIORef IORef Bool
v_unsafeHasNoStateHack

{-
************************************************************************
*                                                                      *
                        Globals and the RTS
*                                                                      *
************************************************************************

When a plugin is loaded, it currently gets linked against a *newly
loaded* copy of the GHC package. This would not be a problem, except
that the new copy has its own mutable state that is not shared with
that state that has already been initialized by the original GHC
package.

(Note that if the GHC executable was dynamically linked this
wouldn't be a problem, because we could share the GHC library it
links to; this is only a problem if DYNAMIC_GHC_PROGRAMS=NO.)

The solution is to make use of @sharedCAF@ through @sharedGlobal@
for globals that are shared between multiple copies of ghc packages.
-}

-- Global variables:

global :: a -> IORef a
global :: forall a. a -> IORef a
global a
a = forall a. IO a -> a
unsafePerformIO (forall a. a -> IO (IORef a)
newIORef a
a)

consIORef :: IORef [a] -> a -> IO ()
consIORef :: forall a. IORef [a] -> a -> IO ()
consIORef IORef [a]
var a
x =
  forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef [a]
var (\[a]
xs -> (a
xforall a. a -> [a] -> [a]
:[a]
xs,()))

globalM :: IO a -> IORef a
globalM :: forall a. IO a -> IORef a
globalM IO a
ma = forall a. IO a -> a
unsafePerformIO (IO a
ma forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall a. a -> IO (IORef a)
newIORef)

-- Shared global variables:

sharedGlobal :: a -> (Ptr (IORef a) -> IO (Ptr (IORef a))) -> IORef a
sharedGlobal :: forall a. a -> (Ptr (IORef a) -> IO (Ptr (IORef a))) -> IORef a
sharedGlobal a
a Ptr (IORef a) -> IO (Ptr (IORef a))
get_or_set = forall a. IO a -> a
unsafePerformIO forall a b. (a -> b) -> a -> b
$
  forall a. a -> IO (IORef a)
newIORef a
a forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall a b c. (a -> b -> c) -> b -> a -> c
flip forall a. a -> (Ptr a -> IO (Ptr a)) -> IO a
sharedCAF Ptr (IORef a) -> IO (Ptr (IORef a))
get_or_set

sharedGlobalM :: IO a -> (Ptr (IORef a) -> IO (Ptr (IORef a))) -> IORef a
sharedGlobalM :: forall a. IO a -> (Ptr (IORef a) -> IO (Ptr (IORef a))) -> IORef a
sharedGlobalM IO a
ma Ptr (IORef a) -> IO (Ptr (IORef a))
get_or_set = forall a. IO a -> a
unsafePerformIO forall a b. (a -> b) -> a -> b
$
  IO a
ma forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall a. a -> IO (IORef a)
newIORef forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall a b c. (a -> b -> c) -> b -> a -> c
flip forall a. a -> (Ptr a -> IO (Ptr a)) -> IO a
sharedCAF Ptr (IORef a) -> IO (Ptr (IORef a))
get_or_set