Skip to content

Commit 7268559

Browse files
committed
Improve documentation
1 parent 6c231ff commit 7268559

File tree

2 files changed

+65
-103
lines changed

2 files changed

+65
-103
lines changed

src/main/asciidoc/index.adoc

Lines changed: 34 additions & 47 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,67 @@ dependencies {
2739
}
2840
----
2941

30-
== Getting started
42+
[#_virtual_thread_verticle]
43+
== Virtual thread verticle
44+
45+
A virtual thread verticle is a worker verticle that needs a *single* instance to run the application.When the verticle *awaits* for a future result, this verticle can still process events like an event-loop verticle.
3146

3247
[source,java]
3348
----
3449
{@link examples.VirtualThreadExamples#gettingStarted}
3550
----
3651

37-
== What this is about
38-
39-
Async/Await for Vert.x
52+
=== Blocking in a virtual thread verticle
4053

41-
== What this is not about
42-
43-
Blocking on other JDK blocking constructs such as latches, locks, sleep, etc...
44-
45-
NOTE: it remains possible to block on these constructs using `Async.await`
46-
47-
== What you get
48-
49-
By default, Vert.x dispatches events on an event-loop thread.
50-
51-
[source,java]
52-
----
53-
{@link examples.VirtualThreadExamples#whatYouGet1}
54-
----
54+
You can block using the {@link io.vertx.virtualthreads.await.Async#await} method to suspend the current virtual thread until the awaited result is available.
5555

56-
Using virtual threads with Vert.x requires to run application tasks on a virtual threads
56+
You can await on a Vert.x `Future`
5757

5858
[source,java]
5959
----
60-
{@link examples.VirtualThreadExamples#whatYouGet2}
60+
{@link examples.VirtualThreadExamples#awaitingFutures1}
6161
----
6262

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.
63+
or on a JDK `CompletionStage`
6464

6565
[source,java]
6666
----
67-
{@link examples.VirtualThreadExamples#whatYouGet3}
67+
{@link examples.VirtualThreadExamples#awaitingFutures2}
6868
----
6969

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
70+
You can acquire the ownership of a `java.util.concurrent.locks`
7171

7272
[source,java]
7373
----
74-
{@link examples.VirtualThreadExamples#whatYouGet4}
74+
{@link examples.VirtualThreadExamples#awaitingLocks1}
7575
----
7676

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.
78-
79-
== Verticles
77+
=== Field visibility
8078

81-
Virtual thread verticles are actually worker verticles, however a single worker instance is enough to execute it.
79+
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.
8280

8381
[source,java]
8482
----
85-
{@link examples.VirtualThreadExamples#deployVerticle}
83+
{@link examples.VirtualThreadExamples#fieldVisibility1}
8684
----
8785

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

9488
[source,java]
9589
----
96-
{@link examples.VirtualThreadExamples#awaitingFutures1}
90+
{@link examples.VirtualThreadExamples#fieldVisibility2}
9791
----
9892

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

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

106-
=== Locks
97+
When your application blocks without `await`, the Vert.x scheduler cannot schedule events on the verticle, it behaves like a regular worker verticle, yet using a virtual thread.
10798

108-
You can lock a `java.util.concurrent.locks`
99+
This use case is not precluded, however the verticle should be deployed with several instances to deliver the aimed concurrency.
109100

110-
[source,java]
111-
----
112-
{@link examples.VirtualThreadExamples#awaitingLocks1}
113-
----
114101

115-
== Thread local support
102+
=== Thread local support
116103

117104
Thread locals are only reliable within the execution of a context task.
118105

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)