Skip to content

FileHandle Implementation

opencode-agent[bot] edited this page Nov 1, 2026 · 3 revisions

FileHandle-Implementation

File handle tracking for open files with position tracking and access mode management.

Overview

JNode implements file handles as lightweight wrappers around filesystem entries that track the current file position and access mode. File handles are created when a file is opened and managed through a central FileHandleManager that tracks all open file references. The implementation bridges the standard Java I/O APIs with JNode's virtual filesystem layer.

Key Components

Class/File Location Purpose
FileHandleImpl fs/src/fs/org/jnode/fs/service/def/FileHandleImpl.java Concrete file handle implementation
FileHandleManager fs/src/fs/org/jnode/fs/service/def/FileHandleManager.java Central registry for open file handles
FileHandleManager.FileData fs/src/fs/org/jnode/fs/service/def/FileHandleManager.java Per-file handle tracking, write exclusion, stale-handle reclamation
VMFileHandle core/src/classlib/java/io/VMFileHandle.java Interface for file handle operations
VMOpenMode core/src/classlib/java/io/VMOpenMode.java Enum for file open modes
FileSystemAPIImpl fs/src/fs/org/jnode/fs/service/def/FileSystemAPIImpl.java Uses FileHandleManager for open()

How It Works

File Handle Lifecycle

When a file is opened through the VFS:

  1. FileSystemAPIImpl.open(path, mode) delegates to FileHandleManager.open(file, mode)
  2. FileHandleManager retrieves or creates a FileData entry for the underlying FSFile
  3. FileData.open(mode) creates a new FileHandleImpl instance
  4. The handle tracks position (fileOffset), mode (VMOpenMode), the creating thread (owner), canWrite, and references the underlying file
sequenceDiagram
    participant App as Application
    participant API as FileSystemAPIImpl
    participant FHM as FileHandleManager
    participant FD as FileData
    participant FHI as FileHandleImpl

    App->>API: open(path, READ)
    API->>FHM: open(fsFile, READ)
    FHM->>FD: open(mode)
    FD->>FHI: new FileHandleImpl(file, mode, fhm)
    FHM-->>API: handle
    API-->>App: VMFileHandle
Loading

Position Tracking

FileHandleImpl maintains a fileOffset field that tracks the current read/write position:

  • getPosition() returns the current offset
  • setPosition(long) updates the offset, allowing seeking beyond EOF (extends file)
  • read(ByteBuffer) reads from fileOffset and advances position
  • write(ByteBuffer) writes at fileOffset and advances position

Open Mode Handling

The constructor processes the VMOpenMode:

  • READ: Read-only handle
  • WRITE: Write-only, truncates file to length 0
  • READ_WRITE: Read and write access
  • WRITE mode causes file.setLength(0) to truncate the file

Write Exclusion

FileHandleManager.FileData enforces exclusive write access:

  • Only one handle can be open for writing per file (hasWriters flag)
  • Duplicate handles with write access are rejected with "File is already open for writing"
  • Read handles can coexist with write handles (read shares)

Handle Duplication

The dup() method creates a new handle for the same file:

  • Delegates to FileHandleManager.dup(handle, newMode)
  • Creates a new FileHandleImpl with same underlying FSFile
  • Allows mode change during duplication

Handle Closure

When a handle is closed:

  1. Underlying file is flushed (file.flush())
  2. closed flag is set
  3. FileHandleManager removes handle from FileData tracking
  4. FileData is removed from manager when last handle closes

Stale Handle Reclamation

A handle that its owner thread abandons without closing used to pin the FSFile and its write lock forever. FileHandleImpl records owner = Thread.currentThread() at construction, and every FileHandleManager.open() sweeps existing FileData entries first:

openFile.closeStaleHandles();
if (!openFile.hasHandles()) {
    files.remove();
}

closeStaleHandles() removes each handle whose !isOwnerAlive(), calls closeFromManager() to flush and mark it closed, then clears hasWriters if that handle was writable. closeFromManager() sets closed in a finally block, so a failed flush still releases the handle. The sweep is opportunistic — it runs on open, not on a timer — and it is the reason a dead command thread cannot permanently block the next writer of the same file.

FileHandleManagerTest (in the fs suite) covers this path. See FSEntry-Cache for the related invalid-cache-hit regression tests.

Gotchas

  • Position seeking beyond EOF: setPosition() extends the file size if seeking past EOF. This behavior exists for compatibility with Classpath's RandomAccessFile.
  • Write exclusion: Only one write handle per file. Attempting to open a second write handle throws an exception.
  • Single-byte read/write inefficiency: read() and write(int) single-byte methods allocate temporary ByteBuffer objects on every call.
  • MappedByteBuffer unimplemented: mapImpl() returns null — memory-mapped I/O is not implemented.
  • Lock unimplemented: lock() and unlock() methods are stubs returning true/no-op.
  • No synchronized position update: Position updates in read/write are not atomic with the actual I/O operation.
  • Ownership is thread-based, not process-based. owner is the thread that constructed the handle. A long-lived handle passed to another thread is still reclaimed when the original thread dies, so do not share an open handle across thread lifetimes without an explicit close.
  • Stale cleanup runs only on open(). Idle systems do not reap abandoned handles until the next open sweeps the map; there is no background reaper.
  • closeFromManager() flushes but does not call fhm.close(). It runs inside the manager's own sweep and sets closed directly, avoiding re-entry into the map it is iterating.
  • Write availability is reset when the write handle dies. The sweep clears hasWriters for a reclaimed writable handle; without that, a dead writer would block all later writes.

Related Pages

Clone this wiki locally