{-# LANGUAGE BangPatterns #-}

{- |
Module      : OpenTelemetry.Context.ThreadLocal
Copyright   : (c) Ian Duncan, 2021-2026
License     : BSD-3
Description : Thread-local context storage with token-based attach\/detach
Stability   : experimental

= Overview

Manages the implicit 'Context' that flows through your application. The
@inSpan@ functions automatically push\/pop context here, so you rarely
need to use this module directly.

= Common operations

@
import OpenTelemetry.Context.ThreadLocal
import OpenTelemetry.Context (insertBaggage, lookupSpan)

-- Read the current context (contains active span + baggage):
ctx <- getContext

-- Modify the current context (e.g. attach baggage):
adjustContext (insertBaggage myBaggage)

-- Attach context extracted from incoming headers:
tok <- attachContext extractedCtx
-- ... later ...
detachContext tok
@

= Token semantics

Per the OpenTelemetry specification, 'attachContext' returns an opaque 'Token'
that must be passed to 'detachContext' to restore the previous context. Tokens
enforce __LIFO ordering__: if you attach contexts A then B, you must detach B
before A. Detaching out of order logs a diagnostic error.

= When to use this module

* Reading the current span: @lookupSpan \<$\> getContext@
* Setting\/reading baggage on the current thread
* Manually attaching context after propagator extraction
* Implementing custom instrumentation middleware
-}
module OpenTelemetry.Context.ThreadLocal (
  -- * Thread-local context

  -- ** Opaque token (spec-mandated attach\/detach handle)
  Token,
  getContext,
  lookupContext,
  attachContext,
  detachContext,
  adjustContext,
  getAndAdjustContext,

  -- * Active baggage (implicit context)
  getActiveBaggage,
  setActiveBaggage,
  clearActiveBaggage,

  -- * Fused ref-based operations (zero CAS on hot path)

  --
  -- These operations use a per-thread IORef for direct reads and writes,
  -- eliminating all CAS contention from the span lifecycle hot path.
  ContextEntry (ceContext),
  emptyEntry,
  ensureContextRef,
  ensureContextRefFast,
  lookupContextRefFast,
  readContextRef,
  writeContextRef,

  -- ** Generalized thread-local context functions

  -- You should not use these without using some sort of specific cross-thread coordination mechanism,
  -- as there is no guarantee of what work the remote thread has done yet.
  lookupContextOnThread,
  attachContextOnThread,
  detachContextFromThread,
  adjustContextOnThread,

  -- ** Debugging tools
  threadContextMap,
) where

import Control.Concurrent
import Control.Concurrent.Thread.Storage
import Control.Monad (when)
import Control.Monad.IO.Class
import Data.IORef
import Data.Maybe (fromMaybe)
import Data.Word (Word64)
import OpenTelemetry.Baggage (Baggage)
import OpenTelemetry.Context (Context, empty, insertBaggage, lookupBaggage, removeBaggage)
import OpenTelemetry.Internal.Logging (otelLogError)
import System.IO.Unsafe
import Prelude hiding (lookup)


{- | Per-thread context entry: the context plus the active token ID.
A token ID of 0 means no token is active (initial state).
-}
data ContextEntry = ContextEntry
  { ContextEntry -> Context
ceContext :: !Context
  , ContextEntry -> Word64
ceTokenId :: {-# UNPACK #-} !Word64
  }


emptyEntry :: ContextEntry
emptyEntry :: ContextEntry
emptyEntry = Context -> Word64 -> ContextEntry
ContextEntry Context
empty Word64
0
{-# INLINE emptyEntry #-}


{- | Opaque token returned by 'attachContext'.

Pass this to 'detachContext' to restore the context that was active before
the attach. The implementation validates LIFO ordering: detaching out of
order (i.e. with a token that doesn't match the most recent attach) logs
a diagnostic error per the OpenTelemetry specification.

Tokens are lightweight (three words) and do not hold resources that need
explicit cleanup. If a token is simply dropped without calling
'detachContext', nothing leaks — but you lose the LIFO validation for
that attach point.

@since 0.5.0.0
-}
data Token = Token
  { Token -> Word64
_tokenId :: {-# UNPACK #-} !Word64
  , Token -> Context
_tokenPreviousContext :: !Context
  , Token -> Word64
_tokenPreviousTokenId :: {-# UNPACK #-} !Word64
  }


type ThreadContextMap = ThreadStorageMap ContextEntry


tokenCounter :: IORef Word64
tokenCounter :: IORef Word64
tokenCounter = IO (IORef Word64) -> IORef Word64
forall a. IO a -> a
unsafePerformIO (Word64 -> IO (IORef Word64)
forall a. a -> IO (IORef a)
newIORef Word64
0)
{-# NOINLINE tokenCounter #-}


nextTokenId :: IO Word64
nextTokenId :: IO Word64
nextTokenId = IORef Word64 -> (Word64 -> (Word64, Word64)) -> IO Word64
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef Word64
tokenCounter (\Word64
n -> let !n' :: Word64
n' = Word64
n Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Word64
1 in (Word64
n', Word64
n'))
{-# INLINE nextTokenId #-}


{- | This is a global variable that is used to store the thread-local context map.
 It is not intended to be used directly for production purposes, but is exposed for debugging purposes.
-}
threadContextMap :: ThreadContextMap
threadContextMap :: ThreadContextMap
threadContextMap = IO ThreadContextMap -> ThreadContextMap
forall a. IO a -> a
unsafePerformIO IO ThreadContextMap
forall (m :: * -> *) a. MonadIO m => m (ThreadStorageMap a)
newThreadStorageMap
{-# NOINLINE threadContextMap #-}


{- | Retrieve a stored 'Context' for the current thread, or an empty context if none exists.

 Warning: this can easily cause disconnected traces if libraries don't explicitly set the
 context on forked threads.

 @since 0.0.1.0
-}
getContext :: (MonadIO m) => m Context
getContext :: forall (m :: * -> *). MonadIO m => m Context
getContext = do
  me <- ThreadContextMap -> m (Maybe ContextEntry)
forall (m :: * -> *) a.
MonadIO m =>
ThreadStorageMap a -> m (Maybe a)
lookup ThreadContextMap
threadContextMap
  pure $! case me of
    Maybe ContextEntry
Nothing -> Context
empty
    Just ContextEntry
entry -> ContextEntry -> Context
ceContext ContextEntry
entry
{-# INLINE getContext #-}


{- | Retrieve a stored 'Context' for the current thread, if it exists.

 @since 0.0.1.0
-}
lookupContext :: (MonadIO m) => m (Maybe Context)
lookupContext :: forall (m :: * -> *). MonadIO m => m (Maybe Context)
lookupContext = (Maybe ContextEntry -> Maybe Context)
-> m (Maybe ContextEntry) -> m (Maybe Context)
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((ContextEntry -> Context) -> Maybe ContextEntry -> Maybe Context
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ContextEntry -> Context
ceContext) (ThreadContextMap -> m (Maybe ContextEntry)
forall (m :: * -> *) a.
MonadIO m =>
ThreadStorageMap a -> m (Maybe a)
lookup ThreadContextMap
threadContextMap)
{-# INLINE lookupContext #-}


{- | Retrieve a stored 'Context' for the provided 'ThreadId', if it exists.

 @since 0.0.1.0
-}
lookupContextOnThread :: (MonadIO m) => ThreadId -> m (Maybe Context)
lookupContextOnThread :: forall (m :: * -> *). MonadIO m => ThreadId -> m (Maybe Context)
lookupContextOnThread ThreadId
tid = (Maybe ContextEntry -> Maybe Context)
-> m (Maybe ContextEntry) -> m (Maybe Context)
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((ContextEntry -> Context) -> Maybe ContextEntry -> Maybe Context
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ContextEntry -> Context
ceContext) (ThreadContextMap -> ThreadId -> m (Maybe ContextEntry)
forall (m :: * -> *) a.
MonadIO m =>
ThreadStorageMap a -> ThreadId -> m (Maybe a)
lookupOnThread ThreadContextMap
threadContextMap ThreadId
tid)
{-# INLINE lookupContextOnThread #-}


{- | Attach a 'Context' to the current thread, returning an opaque 'Token'.

Pass the token to 'detachContext' to restore the previous context. Tokens
enforce LIFO ordering per the OpenTelemetry specification.

@since 0.5.0.0
-}
attachContext :: (MonadIO m) => Context -> m Token
attachContext :: forall (m :: * -> *). MonadIO m => Context -> m Token
attachContext Context
newCtx = IO Token -> m Token
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Token -> m Token) -> IO Token -> m Token
forall a b. (a -> b) -> a -> b
$ do
  tokId <- IO Word64
nextTokenId
  update threadContextMap $ \Maybe ContextEntry
mentry ->
    let !old :: ContextEntry
old = ContextEntry -> Maybe ContextEntry -> ContextEntry
forall a. a -> Maybe a -> a
fromMaybe ContextEntry
emptyEntry Maybe ContextEntry
mentry
        !tok :: Token
tok = Word64 -> Context -> Word64 -> Token
Token Word64
tokId (ContextEntry -> Context
ceContext ContextEntry
old) (ContextEntry -> Word64
ceTokenId ContextEntry
old)
        !new :: ContextEntry
new = Context -> Word64 -> ContextEntry
ContextEntry Context
newCtx Word64
tokId
    in (ContextEntry -> Maybe ContextEntry
forall a. a -> Maybe a
Just ContextEntry
new, Token
tok)
{-# INLINE attachContext #-}


{- | Attach a 'Context' to the provided 'ThreadId', returning an opaque 'Token'.

@since 0.5.0.0
-}
attachContextOnThread :: (MonadIO m) => ThreadId -> Context -> m Token
attachContextOnThread :: forall (m :: * -> *). MonadIO m => ThreadId -> Context -> m Token
attachContextOnThread ThreadId
tid Context
newCtx = IO Token -> m Token
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Token -> m Token) -> IO Token -> m Token
forall a b. (a -> b) -> a -> b
$ do
  tokId <- IO Word64
nextTokenId
  updateOnThread threadContextMap tid $ \Maybe ContextEntry
mentry ->
    let !old :: ContextEntry
old = ContextEntry -> Maybe ContextEntry -> ContextEntry
forall a. a -> Maybe a -> a
fromMaybe ContextEntry
emptyEntry Maybe ContextEntry
mentry
        !tok :: Token
tok = Word64 -> Context -> Word64 -> Token
Token Word64
tokId (ContextEntry -> Context
ceContext ContextEntry
old) (ContextEntry -> Word64
ceTokenId ContextEntry
old)
        !new :: ContextEntry
new = Context -> Word64 -> ContextEntry
ContextEntry Context
newCtx Word64
tokId
    in (ContextEntry -> Maybe ContextEntry
forall a. a -> Maybe a
Just ContextEntry
new, Token
tok)
{-# INLINE attachContextOnThread #-}


{- | Restore the context that was active before the corresponding
'attachContext' call, using the provided 'Token'.

If the token does not match the most recently attached context on this
thread (LIFO violation), the implementation logs a diagnostic error per
the OpenTelemetry specification. The previous context is still restored
regardless.

@since 0.5.0.0
-}
detachContext :: (MonadIO m) => Token -> m ()
detachContext :: forall (m :: * -> *). MonadIO m => Token -> m ()
detachContext (Token Word64
expectedId Context
prevCtx Word64
prevTokenId) = IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ do
  mismatch <- ThreadContextMap
-> (Maybe ContextEntry -> (Maybe ContextEntry, Bool)) -> IO Bool
forall (m :: * -> *) a b.
MonadIO m =>
ThreadStorageMap a -> (Maybe a -> (Maybe a, b)) -> m b
update ThreadContextMap
threadContextMap ((Maybe ContextEntry -> (Maybe ContextEntry, Bool)) -> IO Bool)
-> (Maybe ContextEntry -> (Maybe ContextEntry, Bool)) -> IO Bool
forall a b. (a -> b) -> a -> b
$ \Maybe ContextEntry
mentry ->
    let !current :: ContextEntry
current = ContextEntry -> Maybe ContextEntry -> ContextEntry
forall a. a -> Maybe a -> a
fromMaybe ContextEntry
emptyEntry Maybe ContextEntry
mentry
        !restored :: ContextEntry
restored = Context -> Word64 -> ContextEntry
ContextEntry Context
prevCtx Word64
prevTokenId
    in (ContextEntry -> Maybe ContextEntry
forall a. a -> Maybe a
Just ContextEntry
restored, ContextEntry -> Word64
ceTokenId ContextEntry
current Word64 -> Word64 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word64
expectedId)
  when mismatch $
    otelLogError
      "Context detach token mismatch: LIFO ordering violated. \
      \This likely indicates a context leak — an attachContext call \
      \without a corresponding detachContext in the correct order."
{-# INLINE detachContext #-}


{- | Restore the context on the provided 'ThreadId' using the given 'Token'.

@since 0.5.0.0
-}
detachContextFromThread :: (MonadIO m) => ThreadId -> Token -> m ()
detachContextFromThread :: forall (m :: * -> *). MonadIO m => ThreadId -> Token -> m ()
detachContextFromThread ThreadId
tid (Token Word64
expectedId Context
prevCtx Word64
prevTokenId) = IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ do
  mismatch <- ThreadContextMap
-> ThreadId
-> (Maybe ContextEntry -> (Maybe ContextEntry, Bool))
-> IO Bool
forall (m :: * -> *) a b.
MonadIO m =>
ThreadStorageMap a -> ThreadId -> (Maybe a -> (Maybe a, b)) -> m b
updateOnThread ThreadContextMap
threadContextMap ThreadId
tid ((Maybe ContextEntry -> (Maybe ContextEntry, Bool)) -> IO Bool)
-> (Maybe ContextEntry -> (Maybe ContextEntry, Bool)) -> IO Bool
forall a b. (a -> b) -> a -> b
$ \Maybe ContextEntry
mentry ->
    let !current :: ContextEntry
current = ContextEntry -> Maybe ContextEntry -> ContextEntry
forall a. a -> Maybe a -> a
fromMaybe ContextEntry
emptyEntry Maybe ContextEntry
mentry
        !restored :: ContextEntry
restored = Context -> Word64 -> ContextEntry
ContextEntry Context
prevCtx Word64
prevTokenId
    in (ContextEntry -> Maybe ContextEntry
forall a. a -> Maybe a
Just ContextEntry
restored, ContextEntry -> Word64
ceTokenId ContextEntry
current Word64 -> Word64 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word64
expectedId)
  when mismatch $
    otelLogError
      "Context detach token mismatch on remote thread: LIFO ordering violated."
{-# INLINE detachContextFromThread #-}


{- | Alter the context on the current thread using the provided function.

If there is not a context associated with the current thread, the function will
be applied to an empty context and the result will be stored.

This does not affect the active token — it modifies the context in place.

 @since 0.0.1.0
-}
adjustContext :: (MonadIO m) => (Context -> Context) -> m ()
adjustContext :: forall (m :: * -> *). MonadIO m => (Context -> Context) -> m ()
adjustContext Context -> Context
f = ThreadContextMap
-> (Maybe ContextEntry -> (Maybe ContextEntry, ())) -> m ()
forall (m :: * -> *) a b.
MonadIO m =>
ThreadStorageMap a -> (Maybe a -> (Maybe a, b)) -> m b
update ThreadContextMap
threadContextMap ((Maybe ContextEntry -> (Maybe ContextEntry, ())) -> m ())
-> (Maybe ContextEntry -> (Maybe ContextEntry, ())) -> m ()
forall a b. (a -> b) -> a -> b
$ \Maybe ContextEntry
mentry ->
  let !old :: ContextEntry
old = ContextEntry -> Maybe ContextEntry -> ContextEntry
forall a. a -> Maybe a -> a
fromMaybe ContextEntry
emptyEntry Maybe ContextEntry
mentry
      !ctx' :: Context
ctx' = Context -> Context
f (ContextEntry -> Context
ceContext ContextEntry
old)
  in (ContextEntry -> Maybe ContextEntry
forall a. a -> Maybe a
Just (ContextEntry
old {ceContext = ctx'}), ())
{-# INLINE adjustContext #-}


{- | Atomically read the current context and replace it via @f@ in a single
operation, returning the old context.

@since 0.4.0.0
-}
getAndAdjustContext :: (MonadIO m) => (Context -> Context) -> m Context
getAndAdjustContext :: forall (m :: * -> *).
MonadIO m =>
(Context -> Context) -> m Context
getAndAdjustContext Context -> Context
f = ThreadContextMap
-> (Maybe ContextEntry -> (Maybe ContextEntry, Context))
-> m Context
forall (m :: * -> *) a b.
MonadIO m =>
ThreadStorageMap a -> (Maybe a -> (Maybe a, b)) -> m b
update ThreadContextMap
threadContextMap ((Maybe ContextEntry -> (Maybe ContextEntry, Context))
 -> m Context)
-> (Maybe ContextEntry -> (Maybe ContextEntry, Context))
-> m Context
forall a b. (a -> b) -> a -> b
$ \Maybe ContextEntry
mentry ->
  let !old :: ContextEntry
old = ContextEntry -> Maybe ContextEntry -> ContextEntry
forall a. a -> Maybe a -> a
fromMaybe ContextEntry
emptyEntry Maybe ContextEntry
mentry
      !ctx :: Context
ctx = ContextEntry -> Context
ceContext ContextEntry
old
      !new :: Context
new = Context -> Context
f Context
ctx
  in (ContextEntry -> Maybe ContextEntry
forall a. a -> Maybe a
Just (ContextEntry
old {ceContext = new}), Context
ctx)
{-# INLINE getAndAdjustContext #-}


{- | Get or create the per-thread context IORef. On the first call per thread,
this inserts a new entry (flat-table CAS, once per thread lifetime). On all
subsequent calls the IORef is found via a single flat-table probe (no CAS).

Use 'readIORef' and 'writeIORef' on the returned 'IORef' for zero-overhead
context access on the hot path.

@since 0.5.0.0
-}
ensureContextRef :: ThreadId -> Int -> IO (IORef ContextEntry)
ensureContextRef :: ThreadId -> Int -> IO (IORef ContextEntry)
ensureContextRef ThreadId
tid Int
tw = ThreadContextMap
-> ThreadId -> Int -> ContextEntry -> IO (IORef ContextEntry)
forall a.
ThreadStorageMap a -> ThreadId -> Int -> a -> IO (IORef a)
ensureRef ThreadContextMap
threadContextMap ThreadId
tid Int
tw ContextEntry
emptyEntry
{-# INLINE ensureContextRef #-}


{- | Fused CMM fast path: reads @CurrentTSO.id@ and probes the flat table
entirely in CMM, returning the per-thread context IORef. No 'ThreadId'
allocation, no FFI call, no 'Maybe' wrapper on the steady-state path.

Returns @(threadId, contextRef)@. The thread ID is returned for reuse
as the @thread.id@ span attribute.

First call per thread: falls back to 'myThreadId' + insert (one CAS).

@since 0.5.0.0
-}
ensureContextRefFast :: IO (Int, IORef ContextEntry)
ensureContextRefFast :: IO (Int, IORef ContextEntry)
ensureContextRefFast = ThreadContextMap -> ContextEntry -> IO (Int, IORef ContextEntry)
forall a. ThreadStorageMap a -> a -> IO (Int, IORef a)
ensureRefFast ThreadContextMap
threadContextMap ContextEntry
emptyEntry
{-# INLINE ensureContextRefFast #-}


{- | Fused CMM probe: reads @CurrentTSO.id@ and probes the flat table
entirely in CMM. Returns @(threadId, Maybe (IORef ContextEntry))@.
The thread ID is returned for reuse on the slow path.

@since 0.5.0.0
-}
lookupContextRefFast :: IO (Int, Maybe (IORef ContextEntry))
lookupContextRefFast :: IO (Int, Maybe (IORef ContextEntry))
lookupContextRefFast = ThreadContextMap -> IO (Int, Maybe (IORef ContextEntry))
forall a. ThreadStorageMap a -> IO (Int, Maybe (IORef a))
lookupRefFast ThreadContextMap
threadContextMap
{-# INLINE lookupContextRefFast #-}


{- | Read just the 'Context' from a per-thread IORef.

For the hot path where you need both the entry and the context, prefer
reading the 'IORef' directly and using 'ceContext'.

@since 0.5.0.0
-}
readContextRef :: IORef ContextEntry -> IO Context
readContextRef :: IORef ContextEntry -> IO Context
readContextRef IORef ContextEntry
ref = ContextEntry -> Context
ceContext (ContextEntry -> Context) -> IO ContextEntry -> IO Context
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IORef ContextEntry -> IO ContextEntry
forall a. IORef a -> IO a
readIORef IORef ContextEntry
ref
{-# INLINE readContextRef #-}


{- | Write a 'Context' to a per-thread IORef, preserving the active token ID.
Only the owning thread should call this.

For the hot path (e.g. 'inSpanInternal'), prefer writing the full
'ContextEntry' via 'writeIORef' to avoid the read-modify-write.

@since 0.5.0.0
-}
writeContextRef :: IORef ContextEntry -> Context -> IO ()
writeContextRef :: IORef ContextEntry -> Context -> IO ()
writeContextRef IORef ContextEntry
ref Context
ctx = IORef ContextEntry -> (ContextEntry -> ContextEntry) -> IO ()
forall a. IORef a -> (a -> a) -> IO ()
modifyIORef' IORef ContextEntry
ref (\ContextEntry
e -> ContextEntry
e {ceContext = ctx})
{-# INLINE writeContextRef #-}


{- | Alter the context on the provided thread.

If there is not a context associated with the provided thread, the function will
be applied to an empty context and the result will be stored.

 @since 0.0.1.0
-}
adjustContextOnThread :: (MonadIO m) => ThreadId -> (Context -> Context) -> m ()
adjustContextOnThread :: forall (m :: * -> *).
MonadIO m =>
ThreadId -> (Context -> Context) -> m ()
adjustContextOnThread ThreadId
tid Context -> Context
f = ThreadContextMap
-> ThreadId
-> (Maybe ContextEntry -> (Maybe ContextEntry, ()))
-> m ()
forall (m :: * -> *) a b.
MonadIO m =>
ThreadStorageMap a -> ThreadId -> (Maybe a -> (Maybe a, b)) -> m b
updateOnThread ThreadContextMap
threadContextMap ThreadId
tid ((Maybe ContextEntry -> (Maybe ContextEntry, ())) -> m ())
-> (Maybe ContextEntry -> (Maybe ContextEntry, ())) -> m ()
forall a b. (a -> b) -> a -> b
$ \Maybe ContextEntry
mentry ->
  let !old :: ContextEntry
old = ContextEntry -> Maybe ContextEntry -> ContextEntry
forall a. a -> Maybe a -> a
fromMaybe ContextEntry
emptyEntry Maybe ContextEntry
mentry
      !ctx' :: Context
ctx' = Context -> Context
f (ContextEntry -> Context
ceContext ContextEntry
old)
  in (ContextEntry -> Maybe ContextEntry
forall a. a -> Maybe a
Just (ContextEntry
old {ceContext = ctx'}), ())
{-# INLINE adjustContextOnThread #-}


{- | Get the currently active 'Baggage' from the implicit thread-local context.

@since 0.4.0.0
-}
getActiveBaggage :: (MonadIO m) => m (Maybe Baggage)
getActiveBaggage :: forall (m :: * -> *). MonadIO m => m (Maybe Baggage)
getActiveBaggage = Context -> Maybe Baggage
lookupBaggage (Context -> Maybe Baggage) -> m Context -> m (Maybe Baggage)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m Context
forall (m :: * -> *). MonadIO m => m Context
getContext
{-# INLINE getActiveBaggage #-}


{- | Set the currently active 'Baggage' in the implicit thread-local context.

@since 0.4.0.0
-}
setActiveBaggage :: (MonadIO m) => Baggage -> m ()
setActiveBaggage :: forall (m :: * -> *). MonadIO m => Baggage -> m ()
setActiveBaggage Baggage
b = (Context -> Context) -> m ()
forall (m :: * -> *). MonadIO m => (Context -> Context) -> m ()
adjustContext (Baggage -> Context -> Context
insertBaggage Baggage
b)
{-# INLINE setActiveBaggage #-}


{- | Clear the active 'Baggage' from the implicit thread-local context.

@since 0.4.0.0
-}
clearActiveBaggage :: (MonadIO m) => m ()
clearActiveBaggage :: forall (m :: * -> *). MonadIO m => m ()
clearActiveBaggage = (Context -> Context) -> m ()
forall (m :: * -> *). MonadIO m => (Context -> Context) -> m ()
adjustContext Context -> Context
removeBaggage
{-# INLINE clearActiveBaggage #-}