Backend API. Server-side code: this runs in a Codename One backend, not in the app on the device.

public final class VirtualThread

  1. Object
  2. VirtualThread

A thread of control that is not an OS thread.

One of these per connection is what lets a server keep a context per client without keeping an OS THREAD per client. The difference is not stylistic: a handoff between OS threads measured 21181ns on the machine this was built on, and switching a virtual thread measured 2.6ns.

A virtual thread runs until it finishes or until it asks for bytes that have not arrived, at which point it parks and the host thread goes and runs another one. Parking happens inside the ordinary blocking calls, so the code a virtual thread runs is written in the plain blocking style and does not know it is not a thread – which is the reason to have them rather than callbacks.

Fields

public static final int FINISHED = 0resume: the connection is done and the handle should be freed.
public static final int PARKED_IO = 1resume: waiting for bytes; its descriptor goes back to the poller.
public static final int RUNNABLE = 2resume: it gave up its turn but is ready to run again NOW.
public static final int WAITING = 3resume: parked on an OUTBOUND descriptor – a database socket, a TLS peer, an HTTP call – rather than on its own.

Methods

public static long create(int fd, int stackBytes)A virtual thread that will serve fd when first resumed.
public static long createTask(long token, int stackBytes)A virtual thread that runs a background task: when first resumed it calls Tasks.runVirtual(token).
public static int resume(long handle)Run it until it parks, yields or finishes.
public static int waitCount(long handle)How many descriptors a virtual thread that answered WAITING waits on; 0 for none, which is a wait on the timeout alone.
public static int waitDescriptor(long handle, int index)The index-th descriptor a WAITING virtual thread waits on.
public static int waitEvents(long handle, int index)What it waits for on that descriptor: Reactor.READ, Reactor.WRITE or both.
public static long waitTimeout(long handle)How long it is willing to wait, in milliseconds from now, or -1 for no bound.
public static int descriptorOf(long handle)The descriptor this virtual thread serves, or -1.
public static void free(long handle)Release it.
public static void yieldNow()Step aside so the host thread can run another virtual thread, without waiting for anything.
public static long current()The handle of the virtual thread the caller runs on, or 0 on a host or platform thread.
public static boolean isVirtual()Whether the caller is running on a virtual thread that may park – one that holds no monitor.
public static boolean supported()Whether this build has virtual threads at all, which is not the same question as isVirtual().
public static void report()Print created/finished/freed counts to stderr, for diagnosis.

Inherited methods

Field details

FINISHED

public static final int FINISHED = 0
resume: the connection is done and the handle should be freed.

PARKED_IO

public static final int PARKED_IO = 1
resume: waiting for bytes; its descriptor goes back to the poller.

RUNNABLE

public static final int RUNNABLE = 2

resume: it gave up its turn but is ready to run again NOW.

It is waiting on something that is not its socket – the collector’s allocation backpressure, or its own fairness yield. Putting it on the poller instead would wait for a client that is waiting for the response this virtual thread owes it, and the connection would hang for ever.

WAITING

public static final int WAITING = 3
resume: parked on an OUTBOUND descriptor – a database socket, a TLS peer, an HTTP call – rather than on its own. The host registers the descriptors waitCount names with its poller and resumes it when one is ready or waitTimeout runs out, running other virtual threads meanwhile.

Method details

create

public static long create(int fd, int stackBytes)

A virtual thread that will serve fd when first resumed.

The stack is the C stack only. Java locals and the operand stack live in the virtual thread’s own VM state, which is mapped lazily, so what this size buys is call DEPTH rather than data: it holds the C activation records of the Java methods the connection is nested inside.

Returns

a handle, or 0 if the stack could not be allocated

createTask

public static long createTask(long token, int stackBytes)
A virtual thread that runs a background task: when first resumed it calls Tasks.runVirtual(token). It has no descriptor – descriptorOf answers -1 – so its host runs it from the ring and never parks it on the poller.

Returns

a handle, or 0 if the stack could not be allocated

resume

public static int resume(long handle)
Run it until it parks, yields or finishes. One of the four constants.

waitCount

public static int waitCount(long handle)
How many descriptors a virtual thread that answered WAITING waits on; 0 for none, which is a wait on the timeout alone.

waitDescriptor

public static int waitDescriptor(long handle, int index)
The index-th descriptor a WAITING virtual thread waits on.

waitEvents

public static int waitEvents(long handle, int index)
What it waits for on that descriptor: Reactor.READ, Reactor.WRITE or both.

waitTimeout

public static long waitTimeout(long handle)
How long it is willing to wait, in milliseconds from now, or -1 for no bound.

descriptorOf

public static int descriptorOf(long handle)
The descriptor this virtual thread serves, or -1.

free

public static void free(long handle)
Release it. Only valid once resume has returned FINISHED.

yieldNow

public static void yieldNow()

Step aside so the host thread can run another virtual thread, without waiting for anything.

Parking happens by itself when bytes have not arrived. This is for the other case: a virtual thread that COULD keep going but has had its turn. A no-op when the caller is not a virtual thread.

current

public static long current()
The handle of the virtual thread the caller runs on, or 0 on a host or platform thread. What a waiter hands its host so it can nap rather than be resumed again at once.

isVirtual

public static boolean isVirtual()
Whether the caller is running on a virtual thread that may park – one that holds no monitor. A virtual thread inside synchronized is PINNED to its host and answers false: it waits the way a platform thread does, because switching out would let another virtual thread on the same host into the same critical section, monitor ownership being the host’s.

supported

public static boolean supported()
Whether this build has virtual threads at all, which is not the same question as isVirtual(). The context switch is compiled in only on non-Windows aarch64/x86_64; elsewhere create() can only ever return 0. The server asks this to pick its default poll mode.

report

public static void report()
Print created/finished/freed counts to stderr, for diagnosis.