Separate shared service ownership from thread-local logging caches
This commit is contained in:
1 parent
c1c8a3c072
commit
761bd66793
6 files changed
+386
-157
No files matched your search
@@ -6,6 +6,7 @@ import ../src/namespaced_logging
|
||||
const iterations = 1_000
|
||||
var start: Atomic[bool]
|
||||
var cloneLock: Lock
|
||||
var ready: Atomic[int]
|
||||
|
||||
proc awaitStart() =
|
||||
while not start.load():
|
||||
@@ -27,6 +28,50 @@ proc independentWorker() {.thread.} =
|
||||
let logger = local.getLogger("worker")
|
||||
doAssert logger.isEnabled(lvlInfo)
|
||||
|
||||
type
|
||||
AppenderLifetimes = object
|
||||
created, destroyed: Atomic[int]
|
||||
TrackedAppenderObj = object of LogAppender
|
||||
lifetimes: ptr AppenderLifetimes
|
||||
TrackedAppender = ref TrackedAppenderObj
|
||||
|
||||
proc `=destroy`(app: TrackedAppenderObj) =
|
||||
if app.lifetimes != nil:
|
||||
app.lifetimes.destroyed.atomicInc()
|
||||
`=destroy`(app.namespace)
|
||||
|
||||
proc trackedAppender(lifetimes: ptr AppenderLifetimes): TrackedAppender =
|
||||
lifetimes.created.atomicInc()
|
||||
TrackedAppender(lifetimes: lifetimes)
|
||||
|
||||
method clone(app: TrackedAppender): LogAppender {.gcsafe.} =
|
||||
trackedAppender(app.lifetimes)
|
||||
|
||||
proc lifetimeWorker(source: ptr LogService) {.thread.} =
|
||||
# Borrow the original only until this thread owns a local service. The
|
||||
# readiness barrier lets the parent release it before we continue.
|
||||
let local = source[].threadLocalRef()
|
||||
ready.atomicInc()
|
||||
awaitStart()
|
||||
for i in 0 ..< iterations:
|
||||
var handle = local.service
|
||||
var copied = handle
|
||||
handle = LogService()
|
||||
var moved = move(copied)
|
||||
moved.setThreshold("worker", lvlInfo)
|
||||
let nested = moved.threadLocalRef()
|
||||
doAssert nested.getLogger("worker").isEnabled(lvlInfo)
|
||||
# Local caches and the last shared owner are destroyed on worker threads.
|
||||
|
||||
proc reconfigureWorker(service: LogService) {.thread.} =
|
||||
var handle = service
|
||||
let local = service.threadLocalRef()
|
||||
awaitStart()
|
||||
for i in 0 ..< iterations:
|
||||
handle.setThreshold("worker", lvlInfo)
|
||||
local.ensureFreshness()
|
||||
doAssert local.getLogger("worker").isEnabled(lvlInfo)
|
||||
|
||||
type
|
||||
CustomState = ref object
|
||||
value: int
|
||||
@@ -86,6 +131,32 @@ proc main() =
|
||||
createThread(workers[i], stateWorker, addr source)
|
||||
start.store(true)
|
||||
joinThreads(workers)
|
||||
of "service-lifetime":
|
||||
var lifetimes: AppenderLifetimes
|
||||
var service = initLogService()
|
||||
block:
|
||||
let original = trackedAppender(addr lifetimes)
|
||||
service.addAppender(original)
|
||||
var workers: array[4, Thread[ptr LogService]]
|
||||
for i in 0 ..< workers.len:
|
||||
createThread(workers[i], lifetimeWorker, addr service)
|
||||
while ready.load() != workers.len:
|
||||
discard
|
||||
# Thread arguments contain only a borrowed pointer, so there are no hidden
|
||||
# owning handles in Thread.data keeping the control block alive here.
|
||||
service = LogService()
|
||||
start.store(true)
|
||||
joinThreads(workers)
|
||||
doAssert lifetimes.created.load() > workers.len
|
||||
doAssert lifetimes.destroyed.load() == lifetimes.created.load()
|
||||
of "service-reconfigure":
|
||||
var service = initLogService()
|
||||
service.addAppender(initConsoleLogAppender())
|
||||
var workers: array[4, Thread[LogService]]
|
||||
for i in 0 ..< workers.len:
|
||||
createThread(workers[i], reconfigureWorker, service)
|
||||
start.store(true)
|
||||
joinThreads(workers)
|
||||
of "formatter":
|
||||
var source = initConsoleLogAppender()
|
||||
source.formatter = makeFormatter("prefix: ")
|
||||
|
||||
Reference in new issue
Block a user