Skip to content

Commit 000ffec

Browse files
authored
Document ContexVar and Co
1 parent 0c7a4fc commit 000ffec

File tree

1 file changed

+47
-1
lines changed

1 file changed

+47
-1
lines changed

README.md

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,53 @@ But before discussing it, it's necessary to explain a difference in each pair of
532532
2. Methods with `RetryRunnable` and `RetryCallable` are contextual and may dynamically alter their behavior for the given iteration depending on the context passed. The `RetryContext` provides provides all necessary iteration-specific information.
533533

534534

535-
## 9. Contextual Promises
535+
## 9. Context variables & contextual Promises
536+
537+
Ahh, those dreaded `TreadLocal`-s we all hate, love to hate, but, neveretheless, use everywhere. It's quite common to store some contextual data (like authenticated user and current locale) inside `ThreadLocal` variables. Sometimes it's a custom code, sometimes the code from third-party library we can't alter.
538+
539+
Typically, we spawn asynchronous code from some thread with well-known characteristics, like HTTP request thread. Here we can easly access contextual information from thread-local variables. However, using thread-local variables from asynchronous code block is hard while it's impossible to predict what thread from the pool will execute the code. It's necessary to capture the context of the one thread and propagate it to threads executing asynchronous code.
540+
541+
To solve this issue, there Tascalate Concurrent provides `ContextVar` class, that serves as a proxy over thread-local variable for multi-threaded code. Typical usage scenario is the following:
542+
543+
1. Define `ContextualPromiseFactory` holding `ContextVar`-s from existing thread-local variables.
544+
2. Capture a context of the thread that spawns asynchronous operations.
545+
3. Convert a `Promise` to context-aware promise.
546+
```java
547+
class MyService {
548+
// Tread-local variables; they are configured by some custom code (irrelevant for Tascalate Concurrent)
549+
static final ThreadLocal<Principal> CURRENT_PRINCIPAL = new ThreadLocal<Principal>();
550+
static final ThreadLocal<Locale> CURRENT_LOCALE = new ThreadLocal<Locale>();
551+
}
552+
...
553+
// [1] -- Define `ContextualPromiseFactory`, it's immutable, thread-safe and may be shared in any manner
554+
ContextualPromiseFactory cpf = ContextVar.relay(MyService.CURRENT_PRINCIPAL, MyService.CURRENT_LOCALE);
555+
...
556+
// [2] -- In the invoker thread (like HTTP request thread),
557+
// where MyService.CURRENT_PRINCIPAL, MyService.CURRENT_LOCALE are available:
558+
Function<Promise<String>, Promise<String>> newContextualPromise = cpf.captureContext();
559+
...
560+
// [3] -- Convert a Promise to context-aware promise with 'newContextualPromise' factory:
561+
Promise<Void> httpRequest =
562+
CompletableTask.supplyAsync(() -> getDownloadUrlTemplate(), myExecutor)
563+
.as(newContextualPromise) // <--- HERE the conversion is set
564+
.thenApply(url ->
565+
url + "?user=" + MyService.CURRENT_PRINCIPAL.name() + "&locale" + MyService.CURRENT_LOCALE.toString()
566+
)
567+
.thenAccept(url -> {
568+
log.info("Get data for user " + MyService.CURRENT_PRINCIPAL.name());
569+
executeHttpRequest(url);
570+
});
571+
```
572+
In example above, after the call to `Promise.as(newContextualPromise)` all of the derrived promises may access contextual information captured in originated thread (i.e. code blocks in thenApply / thenAccept).
573+
574+
Worth to mention, that copying contextual variables has certain performance penalty. To stop it at the certain level, just use `Promise.raw()` to undecorate derrived promises (as with any other decorator):
575+
```java
576+
Promise<Void> afterHttpRequest =
577+
httpRequest.raw()
578+
.thenRun(() -> {
579+
// MyService.CURRENT_PRINCIPAL, MyService.CURRENT_LOCALE are not available here
580+
});
581+
```
536582

537583
## 10. Decorators for CompletionStage / CompletableFuture / Promise
538584

0 commit comments

Comments
 (0)