Add failing ORC thread-safety regressions and run all suites with nimble test
This commit is contained in:
1 parent
15af03e5c5
commit
c1c8a3c072
8 files changed
+342
-2
No files matched your search
@@ -0,0 +1,103 @@
|
||||
## Run each scenario in its own ThreadSanitizer-instrumented process.
|
||||
## Success means no race was reported; a crash is not required to expose UB.
|
||||
import std/[atomics, locks, os]
|
||||
import ../src/namespaced_logging
|
||||
|
||||
const iterations = 1_000
|
||||
var start: Atomic[bool]
|
||||
var cloneLock: Lock
|
||||
|
||||
proc awaitStart() =
|
||||
while not start.load():
|
||||
discard
|
||||
|
||||
proc serviceWorker(service: LogService) {.thread.} =
|
||||
awaitStart()
|
||||
for i in 0 ..< iterations:
|
||||
let local = service.threadLocalRef()
|
||||
let logger = local.getLogger("worker")
|
||||
doAssert logger.isEnabled(lvlInfo)
|
||||
|
||||
proc independentWorker() {.thread.} =
|
||||
awaitStart()
|
||||
var service = initLogService()
|
||||
service.setThreshold("worker", lvlInfo)
|
||||
for i in 0 ..< iterations:
|
||||
let local = service.threadLocalRef()
|
||||
let logger = local.getLogger("worker")
|
||||
doAssert logger.isEnabled(lvlInfo)
|
||||
|
||||
type
|
||||
CustomState = ref object
|
||||
value: int
|
||||
StatefulAppender = CustomLogAppender[CustomState]
|
||||
|
||||
proc ignoreMessage(state: CustomState, msg: LogMessage) {.gcsafe, noconv.} =
|
||||
discard
|
||||
|
||||
proc stateWorker(source: ptr StatefulAppender) {.thread.} =
|
||||
awaitStart()
|
||||
for i in 0 ..< iterations:
|
||||
var copied: StatefulAppender
|
||||
withLock cloneLock:
|
||||
copied = StatefulAppender(clone(source[]))
|
||||
doAssert copied.state.value == 42
|
||||
|
||||
proc makeFormatter(prefix: string): LogMessageFormatter =
|
||||
result = proc(msg: LogMessage): string {.gcsafe.} = prefix & msg.msg
|
||||
|
||||
proc formatterWorker(source: ptr ConsoleLogAppender) {.thread.} =
|
||||
awaitStart()
|
||||
# This formatter only needs msg. Avoid clock/timezone initialization so
|
||||
# sanitizer diagnostics identify closure ownership rather than libc state.
|
||||
let msg = LogMessage(msg: "message")
|
||||
for i in 0 ..< iterations:
|
||||
var copied: ConsoleLogAppender
|
||||
withLock cloneLock:
|
||||
copied = ConsoleLogAppender(clone(source[]))
|
||||
doAssert copied.formatter(msg) == "prefix: message"
|
||||
|
||||
proc main() =
|
||||
doAssert paramCount() == 1
|
||||
initLock(cloneLock)
|
||||
defer: deinitLock(cloneLock)
|
||||
case paramStr(1)
|
||||
of "independent":
|
||||
var workers: array[4, Thread[void]]
|
||||
for i in 0 ..< workers.len:
|
||||
createThread(workers[i], independentWorker)
|
||||
start.store(true)
|
||||
joinThreads(workers)
|
||||
of "service":
|
||||
var service = initLogService()
|
||||
service.setThreshold("worker", lvlInfo)
|
||||
service.addAppender(initConsoleLogAppender())
|
||||
var workers: array[4, Thread[LogService]]
|
||||
for i in 0 ..< workers.len:
|
||||
createThread(workers[i], serviceWorker, service)
|
||||
# No parent-side copying/configuration changes overlap the workers.
|
||||
# The parent retains service and all thread argument storage until join.
|
||||
start.store(true)
|
||||
joinThreads(workers)
|
||||
of "custom-state":
|
||||
var source = initCustomLogAppender(CustomState(value: 42), ignoreMessage)
|
||||
var workers: array[4, Thread[ptr StatefulAppender]]
|
||||
for i in 0 ..< workers.len:
|
||||
createThread(workers[i], stateWorker, addr source)
|
||||
start.store(true)
|
||||
joinThreads(workers)
|
||||
of "formatter":
|
||||
var source = initConsoleLogAppender()
|
||||
source.formatter = makeFormatter("prefix: ")
|
||||
var workers: array[4, Thread[ptr ConsoleLogAppender]]
|
||||
for i in 0 ..< workers.len:
|
||||
createThread(workers[i], formatterWorker, addr source)
|
||||
start.store(true)
|
||||
joinThreads(workers)
|
||||
else:
|
||||
raise newException(ValueError, "unknown thread safety scenario")
|
||||
|
||||
# The pointer-based scenarios borrow immutable source appenders whose owner
|
||||
# outlives all workers. This isolates clone/destruction races from LogService
|
||||
# copying and from ordinary unsynchronized application-state mutation.
|
||||
main()
|
||||
Reference in new issue
Block a user