Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't reset telemetry when forking a thread #201

Merged
merged 5 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core-program/core-program.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ cabal-version: 1.12
-- see: https://github.com/sol/hpack

name: core-program
version: 0.6.9.4
version: 0.7.0.0
synopsis: Opinionated Haskell Interoperability
description: A library to help build command-line programs, both tools and
longer-running daemons.
Expand Down
23 changes: 19 additions & 4 deletions core-program/lib/Core/Program/Execute.hs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ import Data.ByteString.Char8 qualified as C (singleton)
import Data.List qualified as List (intersperse)
import GHC.Conc (getNumProcessors, numCapabilities, setNumCapabilities)
import GHC.IO.Encoding (setLocaleEncoding, utf8)
import GHC.Stack (HasCallStack)
import System.Directory
( findExecutable
)
Expand Down Expand Up @@ -1120,7 +1121,7 @@ type annotation somewhere.

@since 0.5.1
-}
queryOptionValue' :: Externalize ξ => LongName -> Program τ (Maybe ξ)
queryOptionValue' :: (Externalize ξ) => LongName -> Program τ (Maybe ξ)
queryOptionValue' name = do
context <- ask
let params = commandLineFrom context
Expand Down Expand Up @@ -1189,7 +1190,7 @@ If the attempt to parse the supplied value fails an exception will be thrown.

@since 0.6.2
-}
queryEnvironmentValue' :: Externalize ξ => LongName -> Program τ (Maybe ξ)
queryEnvironmentValue' :: (Externalize ξ) => LongName -> Program τ (Maybe ξ)
queryEnvironmentValue' name = do
context <- ask
let params = commandLineFrom context
Expand Down Expand Up @@ -1228,9 +1229,23 @@ queryCommandName = do
Just (LongName name) -> pure (intoRope name)
Nothing -> error "Attempted lookup of command but not a Complex Config"

{- |
Exception thrown by 'invalid'. It's not meant to be caught and so is not
exposed publicly.

@since 0.6.10
-}
data InvalidState = InvalidState
deriving (Show)

instance Exception InvalidState

{- |
Illegal internal state resulting from what should be unreachable code or
otherwise a programmer error.
-}
invalid :: Program τ α
invalid = error "Invalid State"
invalid :: (HasCallStack) => Program τ α
invalid = do
critical "Invalid state reached"
write "error: invalid state"
terminate 99
65 changes: 52 additions & 13 deletions core-program/lib/Core/Program/Threads.hs
Original file line number Diff line number Diff line change
Expand Up @@ -126,24 +126,72 @@ Fork a thread. The child thread will run in the same 'Context' as the calling
'Program', including sharing the user-defined application state value.

If you want to find out what the result of a thread was use 'waitThread' on
the 'Thread' object returned from this function. If you don't need the
result, use 'forkThread_' instead.
the 'Thread' object returned from this function. For example:

@
t1 <- 'forkThread' $ do
'Core.Program.Logging.info' \"Doing interesting stuff concurrently\"
'pure' True

...

result <- 'waitThread' t1

if result
then -- expected
else -- not good
@

If you don't need the result, you can use 'forkThread_' instead.

Threads that are launched off as children are on their own! If the code in the
child thread throws an exception that is /not/ caught within that thread, the
exception will kill the thread. Threads dying without telling anyone is a bit
of an anti-pattern, so this library logs a warning-level log message if this
happens.

(this wraps __base__'s 'Control.Concurrent.forkIO')
(this function wraps __base__'s 'Control.Concurrent.forkIO')

/Concerning telemetry/

Note that threads inherit the telemetry state from their parent. If you are
using the tracing features from __core-telemetry__ any telemetry
registered in that side task will be included in the enclosing span active in the thread
istathar marked this conversation as resolved.
Show resolved Hide resolved
that spawned the thread:

@
t2 <- 'forkThread' $ do
'Core.Program.Logging.info' \"Performing quick side task\"
'Core.Telemetry.Observability.telemetry'
[ ''Core.Telemetry.Observability.metric' \"counter\" 42
]
...

@

In this case the @\"counter\"@ field in the parent span will get the value
@42@. This is appropriate for the common case where you are doing small side
tasks concurrently to accelerate a larger computation.

But at other times you are launching off a fully independent control flow and
want it to have its own telemetry. In those cases, you'll want to start a new
span (or even a new trace) immediately after forking the thread:

@
'forkThread_' $ do
'Core.Telemetry.Observability.encloseSpan' \"subTask\" $ do
...
@

any telemetry from this worker thread will be appropriately nested in a new
child span called @\"subTask\"@.

@since 0.2.7
-}
forkThread :: Program τ α -> Program τ (Thread α)
forkThread program = do
context <- ask
let i = startTimeFrom context
let v = currentDatumFrom context
let scope = currentScopeFrom context

liftIO $ do
Expand All @@ -154,18 +202,9 @@ forkThread program = do
start <- readMVar i
i' <- newMVar start

-- we also need to fork the current Datum, in the same way that we do
-- when we create a nested span. We do this simply by creating a new
-- MVar so that when the new thread updates the attached metadata
-- it'll be evolving a different object.

datum <- readMVar v
v' <- newMVar datum

let context' =
context
{ startTimeFrom = i'
, currentDatumFrom = v'
}

-- fork, and run nested program
Expand Down
2 changes: 1 addition & 1 deletion core-program/package.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: core-program
version: 0.6.9.4
istathar marked this conversation as resolved.
Show resolved Hide resolved
version: 0.7.0.0
synopsis: Opinionated Haskell Interoperability
description: |
A library to help build command-line programs, both tools and
Expand Down
1 change: 1 addition & 0 deletions unbeliever.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ executable snippet
, core-data
, core-program
, core-text
buildable: False
default-language: Haskell2010

executable webserver
Expand Down
Loading