Skip to content

Commit 3931e10

Browse files
committed
Improve documentation
1 parent 6c231ff commit 3931e10

File tree

2 files changed

+72
-100
lines changed

2 files changed

+72
-100
lines changed

src/main/asciidoc/index.adoc

Lines changed: 41 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ Use virtual threads to write Vert.x code that looks like it is synchronous.
44

55
You still write the traditional Vert.x code processing events, but you have the opportunity to write synchronous code for complex workflows and use thread locals in such workflows.
66

7+
== Introduction
8+
9+
One of the key advantages of Vert.x over many legacy application platforms is that it is almost entirely non-blocking (of kernel threads) - this allows it to handle a lot of concurrency (e.g. handle many connections, or messages) using a very small number of kernel threads, which allows it to scale very well.
10+
11+
The non-blocking nature of Vert.x leads to asynchronous APIs. Asynchronous APIs can take various forms including callback style, promises or Rx-style. Vert.x uses futurese in most places (although, it also supports Rx).
12+
13+
In some cases, programming using asynchronous APIs can be more challenging than using a direct synchronous style, in particular if you have several operations that you want to do in sequence. Also, error propagation is often more complex when using asynchronous APIs.
14+
15+
Vertx virtual threads allows you to work with asynchronous APIs, but using a direct synchronous style that you're already familiar with.
16+
17+
It does this by using Java 21 virtual threads. Virtual threads are very lightweight threads that do not correspond to underlying kernel threads. When they are blocked they do not block a kernel thread.
18+
719
== Usage
820

921
To use the virtual threads with Vert.x add the following dependency to the _dependencies_ section of your build descriptor:
@@ -27,92 +39,77 @@ dependencies {
2739
}
2840
----
2941

30-
== Getting started
42+
== Using virtual threads
43+
44+
You can deploy virtual thread verticles.
45+
46+
A virtual thread verticle is a worker verticle that requires only a *single* instance of the verticle to run the application.
47+
48+
When the verticle *awaits* a result, the verticle can still process events like an event-loop verticle.
3149

3250
[source,java]
3351
----
3452
{@link examples.VirtualThreadExamples#gettingStarted}
3553
----
3654

37-
== What this is about
55+
=== Blocking within a virtual thread verticle
3856

39-
Async/Await for Vert.x
57+
You can use {@link io.vertx.virtualthreads.await.Async#await} to suspend the current virtual thread until the awaited result is available.
4058

41-
== What this is not about
59+
The virtual thread is effectively blocked, but the application can still process events.
4260

43-
Blocking on other JDK blocking constructs such as latches, locks, sleep, etc...
61+
When a virtual thread awaits for a result and the verticle needs to process a task, a new virtual thread is created to handle this task.
4462

45-
NOTE: it remains possible to block on these constructs using `Async.await`
63+
When the result is available, the virtual thread execution is resumed and scheduled after the current task is suspended or finished.
4664

47-
== What you get
65+
Like any verticle at most one task is executed at the same time.
4866

49-
By default, Vert.x dispatches events on an event-loop thread.
67+
You can await on a Vert.x `Future`
5068

5169
[source,java]
5270
----
53-
{@link examples.VirtualThreadExamples#whatYouGet1}
54-
----
55-
56-
Using virtual threads with Vert.x requires to run application tasks on a virtual threads
57-
58-
[source,java]
59-
----
60-
{@link examples.VirtualThreadExamples#whatYouGet2}
71+
{@link examples.VirtualThreadExamples#awaitingFutures1}
6172
----
6273

63-
This project implements virtual threads with Vert.x with a race free model. Events are dispatched to a virtual thread, when this virtual thread awaits an asynchronous result, the pending events are not dispatched until the virtual thread is resumed.
74+
or on a JDK `CompletionStage`
6475

6576
[source,java]
6677
----
67-
{@link examples.VirtualThreadExamples#whatYouGet3}
78+
{@link examples.VirtualThreadExamples#awaitingFutures2}
6879
----
6980

70-
When a virtual thread awaits a future, a new virtual thread can be started to handle new events and avoid blocking the appliction or potential self deadlocks, e.g. in the following example, awaiting the response does not prevent the timer to fire
81+
You can acquire the ownership of a `java.util.concurrent.locks`
7182

7283
[source,java]
7384
----
74-
{@link examples.VirtualThreadExamples#whatYouGet4}
85+
{@link examples.VirtualThreadExamples#awaitingLocks1}
7586
----
7687

77-
If you block a virtual thread without `{@link io.vertx.virtualthreads.await.Async`, your application then will behave like a regular worker and events will not be processed until the current task ends.
88+
=== Field visibility
7889

79-
== Verticles
80-
81-
Virtual thread verticles are actually worker verticles, however a single worker instance is enough to execute it.
90+
A virtual thread verticle can interact safely with fields before an `await` call. However, if you are reading a field before an `await` call and reusing the value after the call, you should keep in mind that this value might have changed.
8291

8392
[source,java]
8493
----
85-
{@link examples.VirtualThreadExamples#deployVerticle}
94+
{@link examples.VirtualThreadExamples#fieldVisibility1}
8695
----
8796

88-
== Supported primitives
89-
90-
=== Futures
91-
92-
You can await a Vert.x `Future`
97+
You should read/write fields before calling `await` to avoid this.
9398

9499
[source,java]
95100
----
96-
{@link examples.VirtualThreadExamples#awaitingFutures1}
101+
{@link examples.VirtualThreadExamples#fieldVisibility2}
97102
----
98103

99-
or a JDK `CompletionStage`
104+
NOTE: this is the same behavior with an event-loop verticle
100105

101-
[source,java]
102-
----
103-
{@link examples.VirtualThreadExamples#awaitingFutures2}
104-
----
106+
=== Blocking without await
105107

106-
=== Locks
108+
When your application blocks without using `await`, e.g. using `ReentrantLock#lock`, the Vert.x scheduler is not aware of it and cannot schedule events on the verticle: it behaves like a regular worker verticle, yet using virtual threads.
107109

108-
You can lock a `java.util.concurrent.locks`
109-
110-
[source,java]
111-
----
112-
{@link examples.VirtualThreadExamples#awaitingLocks1}
113-
----
110+
This use case is not encouraged but it is not forbidden, however the verticle should be deployed with several instances to deliver the aimed concurrency, like a regular worker verticle.
114111

115-
== Thread local support
112+
=== Thread local support
116113

117114
Thread locals are only reliable within the execution of a context task.
118115

src/main/java/examples/VirtualThreadExamples.java

Lines changed: 31 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -23,71 +23,47 @@
2323
public class VirtualThreadExamples {
2424

2525
public void gettingStarted(Vertx vertx) {
26-
Async async = new Async(vertx);
27-
async.run(v -> {
28-
// Run on a Vert.x a virtual thread
29-
HttpClient client = vertx.createHttpClient();
30-
HttpClientRequest req = Async.await(client.request(HttpMethod.GET, 8080, "localhost", "/"));
31-
HttpClientResponse resp = Async.await(req.send());
32-
int status = resp.statusCode();
33-
Buffer body = Async.await(resp.body());
34-
});
35-
}
3626

37-
public void whatYouGet1(HttpClientRequest request) {
38-
request
39-
.send()
40-
.onSuccess(response -> {
41-
// Set the buffer for handlers
42-
response.handler(buffers -> {
27+
AbstractVerticle verticle = new AbstractVerticle() {
28+
@Override
29+
public void start() {
30+
HttpClient client = vertx.createHttpClient();
31+
HttpClientRequest req = Async.await(client.request(
32+
HttpMethod.GET,
33+
8080,
34+
"localhost",
35+
"/"));
36+
HttpClientResponse resp = Async.await(req.send());
37+
int status = resp.statusCode();
38+
Buffer body = Async.await(resp.body());
39+
}
40+
};
4341

44-
});
45-
});
42+
// Run the verticle a on virtual thread
43+
vertx.deployVerticle(verticle, new DeploymentOptions()
44+
.setWorker(true)
45+
.setInstances(1)
46+
.setWorkerOptions(new VirtualThreadOptions()));
4647
}
4748

48-
public void whatYouGet2(HttpClientRequest request) {
49-
Thread.startVirtualThread(() -> {
50-
CompletableFuture<HttpClientResponse> fut = new CompletableFuture<>();
51-
request.send().onComplete(ar -> {
52-
if (ar.succeeded()) {
53-
fut.complete(ar.result());
54-
} else {
55-
fut.completeExceptionally(ar.cause());
56-
}
57-
});;
58-
try {
59-
HttpClientResponse response = fut.get();
60-
// As we get the response the virtual thread, there is a window of time where the event-loop thread has already sent buffers and we lost these events
61-
response.handler(buffer -> {
49+
private int counter;
6250

63-
});
64-
} catch (Exception e) {
65-
// Ooops
66-
}
67-
});
51+
public void fieldVisibility1() {
52+
int value = counter;
53+
value += Async.await(getRemoteValue());
54+
// the counter value might have changed
55+
counter = value;
6856
}
6957

70-
public void whatYouGet3(HttpClientRequest request) {
71-
Future<HttpClientResponse> fut = request.send();
72-
HttpClientResponse response = Async.await(fut);
73-
// Buffer events might be in the queue and if they are, they will be dispatched next
74-
response.handler(buffer -> {
75-
76-
});
58+
public void fieldVisibility2() {
59+
counter += Async.await(getRemoteValue());
7760
}
7861

79-
public void whatYouGet4(Vertx vertx, HttpClientRequest request) {
80-
Promise<HttpClientResponse> promise = Promise.promise();
81-
vertx.setTimer(100, id -> promise.tryFail("Too late"));
82-
request.send().onComplete(promise);
83-
try {
84-
HttpClientResponse response = Async.await(promise.future());
85-
} catch (Exception timeout) {
86-
// Too late
87-
}
62+
private Future<Buffer> callRemoteService() {
63+
return null;
8864
}
8965

90-
private Future<Buffer> callRemoteService() {
66+
private Future<Integer> getRemoteValue() {
9167
return null;
9268
}
9369

@@ -115,7 +91,6 @@ public void start() {
11591
.setWorkerOptions(new VirtualThreadOptions()));
11692
}
11793

118-
11994
public void awaitingFutures1(HttpClientResponse response) {
12095
Buffer body = Async.await(response.body());
12196
}
@@ -133,7 +108,7 @@ public void awaitingLocks1(Lock theLock) {
133108
}
134109
}
135110

136-
public void threadLocalSupport1(Lock theLock, String userId, HttpClient client) {
111+
public void threadLocalSupport1(String userId, HttpClient client) {
137112
ThreadLocal<String> local = new ThreadLocal();
138113
local.set(userId);
139114
HttpClientRequest req = Async.await(client.request(HttpMethod.GET, 8080, "localhost", "/"));

0 commit comments

Comments
 (0)