Skip to content
opencode-agent[bot] edited this page May 10, 2026 · 1 revision

TSI (Thread Switch Indicator)

Per-processor field controlling atomic thread switching state in JNode's hybrid scheduler.

Overview

TSI (Thread Switch Indicator) is a volatile Word field stored as the first field in the VmProcessor class, making it accessible at a fixed zero offset for atomic operations from assembly code. It serves as the central coordination mechanism for JNode's hybrid preemptive/cooperative thread scheduler, enabling safe thread switches while preventing race conditions between interrupt handlers and the scheduler.

The TSI field is manipulated from both Java code (VmProcessor.java) and assembly code (vm-ints.asm, unsafe.asm) using atomic operations to ensure thread safety. When a thread reaches a yieldpoint (compiler-injected checkpoint), the assembly handler examines the TSI to determine whether a thread switch should occur, and uses atomic operations to coordinate the switch state.

Key Components

Class / File Role
core/src/core/org/jnode/vm/scheduler/VmProcessor.java Core TSI implementation with atomic state constants and manipulation methods
core/src/native/x86/vm-ints.asm Yieldpoint handler, TSI state transitions during thread switch
core/src/native/x86/unsafe.asm TSI checking for switch requests
builder/src/test/org/jnode/jnasm/jnode32.asm TSI constant definitions (VmProcessor_TSI_*)

How It Works

TSI Bit Flags

The TSI is a 4-bit state machine:

Bit 0: TSI_SWITCH_NEEDED    (0x0001) - Thread requests a context switch
Bit 1: TSI_SYSTEM_READY    (0x0002) - System is ready for switching (idle thread started)
Bit 2: TSI_SWITCH_ACTIVE    (0x0004) - Thread switch is in progress
Bit 3: TSI_BLOCK_SWITCH     (0x0008) - Thread switching is blocked (GC, etc.)

The combined value TSI_SWITCH_REQUESTED (0x0003 = NEEDED | SYSTEM_READY) indicates a valid state for switching.

Atomic Operations

TSI is accessed atomically via the getTSIAddress() method which returns the address of the first field:

// From VmProcessor.java:322
getTSIAddress().atomicOr(Word.fromIntSignExtend(TSI_BLOCK_SWITCH));

// From VmProcessor.java:343
getTSIAddress().atomicAnd(Word.fromIntSignExtend(~TSI_BLOCK_SWITCH));

The assembly code uses direct memory access via the processor pointer (FS register in 32-bit, R12 in 64-bit):

; From vm-ints.asm:126-127
or THREADSWITCHINDICATOR,VmProcessor_TSI_SWITCH_ACTIVE
and THREADSWITCHINDICATOR,~VmProcessor_TSI_SWITCH_NEEDED

State Transitions

  1. Normal operation: TSI starts at 0, idle thread calls systemReadyForThreadSwitch() to set SYSTEM_READY
  2. Thread yields: Java code sets SWITCH_NEEDED, checks for SWITCH_REQUESTED (0x3), calls Unsafe.yieldPoint()
  3. Yieldpoint handling: Assembly sets SWITCH_ACTIVE, clears SWITCH_NEEDED, calls VmProcessor.reschedule()
  4. Switch completion: Scheduler picks new thread, returns from yieldpoint handler

Integration with Yieldpoints

TSI is tightly coupled with the yieldpoint mechanism. When JIT-compiled code reaches a yieldpoint:

  1. CPU executes INT 0x30 (yieldpoint trap)
  2. Assembly handler checks TSI bits
  3. If SWITCH_NEEDED && SYSTEM_READY && !BLOCK_SWITCH: proceed with switch
  4. The TSI_SWITCH_ACTIVE flag prevents nested switches

Gotchas & Non-Obvious Behavior

  • TSI must be first field: The VmProcessor class explicitly orders TSI as the first field ("KEEP THIS THE FIRST FIELD!!!") so assembly can compute its address as processor base + 0
  • Atomic operations required: Non-atomic TSI modifications can cause race conditions between Java code and interrupt handlers
  • BLOCK_SWITCH during GC: The GC acquires the scheduler lock and sets BLOCK_SWITCH to prevent yieldpoints from triggering during critical sections
  • SWITCH_ACTIVE prevents recursion: Once set, SWITCH_ACTIVE prevents another thread switch until cleared, avoiding nested reschedule() calls
  • Yield validation: The yield() method validates that TSI becomes exactly SWITCH_REQUESTED (0x3); any other value triggers a fatal error

Related Pages

Clone this wiki locally