-
Notifications
You must be signed in to change notification settings - Fork 0
Yieldpoint
A compiler-injected checkpoint in compiled code that triggers
INT 0x30, invoking the context-switch handler inVmProcessor.reschedule(). Yieldpoints enable cooperative scheduling; methods marked@Uninterruptiblehave yieldpoints suppressed.
Yieldpoints are the cooperative mechanism by which JNode's thread scheduler gains control during normal code execution. Unlike preemptive scheduling (which uses timer interrupts), yieldpoints are inserted by the JIT compiler at strategic points in compiled bytecode. When a yieldpoint fires, it triggers INT 0x30, which transfers control to VmProcessor.reschedule() to potentially switch to another thread.
This design achieves several goals:
- Low overhead: The common path (no switch needed) is just a CMP + conditional jump, optimized for branch prediction
-
Deterministic behavior:
@Uninterruptiblemethods never yield, ensuring atomic operations - Integration with TSI: The Thread Switch Indicator flags control when switches can occur
| File/Class | Role |
|---|---|
VmProcessor (line 419) |
Contains reschedule() — the @Uninterruptible method called when yieldpoint fires |
X86CompilerConstants.YIELDPOINT_INTNO = 0x30 |
The interrupt vector used for yieldpoints (line 78) |
X86CompilerHelper.writeYieldPoint() |
Generates the CMP + INT sequence in compiled code (line 319) |
vm-ints.asm (line 23) |
Low-level stub_yieldPointHandler that saves state and calls Java |
VmProcessor.TSI_* flags |
Control when switches are allowed (line 186-207) |
MethodPragmaFlags.isUninterruptible() |
Controls whether yieldpoints are suppressed in a method |
The L1/L1a JIT compilers call X86CompilerHelper.writeYieldPoint() to insert a yieldpoint check at method entry and in loop headers. The generated assembly:
; Check if TSI_SWITCH_REQUESTED flag is set
cmp dword [fs:offset], TSISWITCH_REQUESTED ; 32-bit
; or
cmp r12, offset, TSISWITCH_REQUESTED ; 64-bit (r12 = PROCESSOR64)
je no_yp ; Jump if flag NOT set (optimized for common case)
int 0x30 ; Trigger yieldpoint interrupt
no_yp: ; Continue normal executionThe code is optimized for the case where no switch is needed (predict NOT taken).
When INT 0x30 executes, the assembly handler in vm-ints.asm (stub_yieldPointHandler, line 23) saves context and calls VmProcessor.reschedule():
@Uninterruptible
final void reschedule() {
// Get current thread
final VmThread current = currentThread;
// Add current to ready queue if still running
if (current.isRunning()) {
scheduler.addToReadyQueue(current, false, getIdString());
}
// Wake up any sleeping threads
VmThread newThread = scheduler.popFirstSleepingThread();
if (newThread == null) {
newThread = scheduler.popFirstReadyThread();
}
// Switch to new thread
newThread.wakeUpByScheduler();
this.nextThread = newThread;
}The Thread Switch Indicator (VmProcessor.threadSwitchIndicator) is a 4-bit flags word:
| Flag | Value | Purpose |
|---|---|---|
TSI_SWITCH_NEEDED |
0x0001 | Bit set by timer interrupt to request switch |
TSI_SYSTEM_READY |
0x0002 | Set after VM initialization completes |
TSI_SWITCH_ACTIVE |
0x0004 | Set during context switch to prevent re-entry |
TSI_BLOCK_SWITCH |
0x0008 | Set to temporarily block switches (e.g., in native code) |
The combined TSI_SWITCH_REQUESTED = TSI_SWITCH_NEEDED | TSI_SYSTEM_READY is the value the compiler checks.
Methods annotated with @Uninterruptible have yieldpoints suppressed entirely. The compiler checks method.isUninterruptible() before emitting yieldpoint code (line 320 in X86CompilerHelper). This ensures atomic execution of critical sections like GC marking, lock acquisition, and interrupt handling.
-
Yieldpoint storms: If many threads are runnable and the scheduler round-robins quickly, frequent yieldpoints can cause performance degradation. The scheduler includes a "same thread priority count" check to detect possible infinite loops (line 473 in
VmProcessor). -
Blocking vs. yielding: A thread calling
Object.wait()blocks (removes from ready queue) but does NOT use yieldpoints — it usesObject.wait()semantics. Yieldpoints only trigger on active execution. -
Timeslice vs. yieldpoint: There are two interrupt sources:
INT 0x30(yieldpoint) andINT 0x33(timeslice). Timeslice uses the samereschedule()path but is triggered by the timer interrupt, not by compiled code. -
No re-entrancy:
TSI_SWITCH_ACTIVEprevents nested switches; if a yieldpoint fires whilereschedule()is executing, it is silently ignored. -
64-bit register: In 64-bit mode, the processor pointer is in
R12(defined asPROCESSOR64inX86CompilerConstants), not the typical C calling convention.
- Core-Thread-Scheduling — Parent concept covering the hybrid preemptive/cooperative model
- TSI — Thread Switch Indicator: atomic flags that gate yieldpoint triggers, assembly-level flag manipulation
- JIT-Compilers — Where yieldpoints are inserted into compiled code
-
Assembly-Files — Where
vm-ints.asmandkernel.asmare defined - Thread-Scheduling — Overview of thread scheduling mechanisms
- Interrupt-Handling — How interrupts (including yieldpoints) are routed