using concurrent** (Service) -** Use to create [ThreadStashes]`ThreadStash` whose contents may be automatically deleted.** ** This is important in the context of a web application where resources need to be cleaned up at the end of a web ** request / thread. ** ** @since 1.3.0constmixin ThreadStashManager {** Creates a `ThreadStash` with the given prefix.abstract ThreadStash createStash(Str owner)** Returns all (fully qualified) keys in the current thread associated / used with this manager. abstract Str[] keys()** Add a handler to be called on thread clean up. Handlers need to be added for each thread.abstract Void addCleanUpHandler(|->| handler)** Removes all values in the current thread associated / used with this manager.abstract Void cleanUpThread()}internalconstclass ThreadStashManagerImpl : ThreadStashManager {privateconst Str prefixprivate Int? counter { get { Actor.locals["${ThreadStashManager#.name}.counter"]} set { Actor.locals["${ThreadStashManager#.name}.counter"] = it}}private |->|[] cleanupHandlers { get { Actor.locals.getOrAdd("${prefix}.cleanupHandlers"){ |->|[,]}} set {}}new make(Str prefix := "ThreadStash"){this.prefix = createPrefix(prefix)}override ThreadStash createStash(Str owner){ ThreadStash(prefix + "." + owner)}override Str[] keys(){ Actor.locals.keys .findAll {it.startsWith(prefix)} .sort}override Void addCleanUpHandler(|->| handler){ cleanupHandlers.add(handler)}override Void cleanUpThread(){ cleanupHandlers.each |handler| { handler.call } keys.each { Actor.locals.remove(it)}}// ---- Helper Methods ------------------------------------------------------------------------private Str createPrefix(Str name){ count := counter ?: 1 padded := count.toStr.padl(2, '0') prefix := "${name}.${padded}" counter = count + 1return prefix}}