tslogger- Thread-safe logging.

Safe HaskellNone




Thread-safe Logging with bonus controlled-schedule debugging capabilities.

This module supports logging to memory, serializing messages and deferring the work of actually printing them. Another thread can flush the logged messages at its leisure.

The second capability of this infrastructure is to use the debugging print messages as points at which to gate the execution of the program. That is, each logStrLn_ call becomes a place where the program blocks and checks in with a central coordinator, which only allows one thread to unblock at a time. Thus, if there are sufficient debug logging messages in the program, this can enable a form of deterministic replay (and quickcheck-style testing of different interleavings).


Global variables

dbgLvl :: Int Source

Debugging flag shared by several modules. This is activated by setting the environment variable DEBUG=1..5.

By convention DEBUG=100 turns on full sequentialization of the program and control over the interleavings in concurrent code, enabling systematic debugging of concurrency problems.

defaultMemDbgRange :: (Int, Int) Source

This codifies the convention of keeping fine-grained pre-each-memory-modification messages at higher debug levels. These are used for fuzz testing concurrent interleavings. Setting dbgRange in the DbgCfg to this value should give you only the messages necessary for stress testing schedules.

New logger interface

newLogger Source


:: (Int, Int)

What inclusive range of messages do we accept? Defaults to `(0,dbgLvl)`.

-> [OutDest]

Where do we write debugging messages?

-> WaitMode

Do we wait for workers before proceeding sequentially but randomly (fuzz testing event interleavings)?

-> IO Logger 

Create a new logger, which includes forking a coordinator thread. Takes as argument the number of worker threads participating in the computation.

logOn :: Logger -> LogMsg -> IO () Source

Write a log message from the current thread, IF the level of the message falls into the range accepted by the given Logger, otherwise, the message is ignored.

data Logger Source

A Logger coordinates a set of threads that print debug logging messages.

This are abstract objects supporting only the operations provided by this module and the non-hidden fields of the Logger.

data WaitMode Source

Several different ways we know to wait for quiescence in the concurrent mutator before proceeding.



UNFINISHED: Dynamically track tasks/workers. The num workers starts at 1 and then is modified with incrTasks and decrTasks.


A fixed set of threads must check-in each round before proceeding.


numThreads :: Int

How many threads total must check in?

downThreads :: IO Int

Poll how many threads WON'T participate this round. After all *productive* threads have checked in this number must grow to eventually include all other threads.


In this mode, logging calls are non-blocking and return immediately, rather than waiting on a central coordinator. This is what we want if we're simply printing debugging output, not controlling the schedule for stress testing.


data LogMsg Source

We allow logging in O(1) time in String or ByteString format. In practice the distinction is not that important, because only *thunks* should be logged; the thread printing the logs should deal with forcing those thunks.




lvl :: Int
body :: String

This sort of message is chatter and NOT meant to participate in the scheduler-testing framework. | ByteStrMsg { lvl::Int, }


lvl :: Int
obod :: String

data OutDest Source

A destination for log messages



Output via GHC's traceEvent runtime events.

OutputTo Handle

Printed human-readable output to a handle.


Accumulate output in memory and flush when appropriate.

General utilities

forkWithExceptions :: (IO () -> IO ThreadId) -> String -> IO () -> IO ThreadId Source

Exceptions that walk up the fork-tree of threads.

WARNING: By holding onto the ThreadId we keep the parent thread from being garbage collected (at least as of GHC 7.6). This means that even if it was complete, it will still be hanging around to accept the exception below.

data Backoff Source

The state for an exponential backoff.


newBackoff Source


:: Int

Maximum delay, nanoseconds

-> IO Backoff 

Create an object used for exponentential backoff; see backoff.

backoff :: Backoff -> IO Backoff Source

Perform the backoff, possibly delaying the thread.

data DbgCfg Source

DebugConfig: what level of debugging support is activated?




dbgRange :: Maybe (Int, Int)

Inclusive range of debug messages to accept (i.e. filter on priority level). If Nothing, use the default level, which is (0,N) where N is controlled by the DEBUG environment variable. The convention is to use Just (0,0) to disable logging.

dbgDests :: [OutDest]

Destinations for debug log messages.

dbgScheduling :: Bool

In additional to logging debug messages, control thread interleaving at these points when this is True.