iofd

package module
v0.3.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 6, 2026 License: MIT Imports: 6 Imported by: 1

README

iofd

Go Reference Go Report Card Codecov

Universal file descriptor abstractions for Unix systems in Go.

Language: English | 简体中文 | Español | 日本語 | Français

Overview

iofd provides minimal file descriptor abstractions and specialized Linux handles for the Go ecosystem. It serves as the canonical handle abstraction for high-performance I/O systems.

Key Features
  • Zero Overhead: All kernel interactions via zcall assembly, bypassing Go's syscall hooks
  • Zero-Allocation Hot Paths: Fixed-size EventFD, TimerFD, and SignalFD success paths keep syscall arguments stack-backed
  • Specialized Handles: Linux-specific EventFD, TimerFD, PidFD, MemFD, SignalFD
  • Cross-Platform Core: Base FD operations work on Linux, Darwin, and FreeBSD
  • Explicit Ownership: FD close idempotence applies to one descriptor cell; close after users are drained and use Dup for an independent close owner

Installation

go get code.hybscloud.com/iofd

Quick Start

EventFD Signaling
efd, _ := iofd.NewEventFD(0)
defer efd.Close()

efd.Signal(1)
val, _ := efd.Wait() // val == 1
TimerFD
tfd, _ := iofd.NewTimerFD()
defer tfd.Close()

// One-shot timer at 100ms
tfd.ArmDuration(100*time.Millisecond, 0)
// ... poll/epoll/io_uring wait ...
count, _ := tfd.Expirations() // count == 1
Error Handling
_, err := efd.Wait()
if errors.Is(err, iox.ErrWouldBlock) {
    // Non-blocking, no data available - retry later
} else if errors.Is(err, iofd.ErrClosed) {
    // FD was closed
} else if err != nil {
    // Other error
}

API

Core Types
Type Description
FD File descriptor cell with atomic same-cell lifecycle operations
EventFD Linux eventfd for inter-thread signaling
TimerFD Linux timerfd for high-resolution timers
PidFD Linux pidfd for race-free process management
MemFD Linux memfd for anonymous memory-backed files
MappedRegion Memory-mapped region for zero-copy access
SignalFD Linux signalfd for synchronous signal handling
Interfaces
Interface Methods Description
PollFd Fd() int Pollable file descriptor
PollCloser Fd(), Close() Closeable pollable descriptor
Handle Fd(), Close(), Read(), Write() Full I/O handle
Signaler Signal(), Wait() Signaling mechanism
Timer Arm(), Disarm() Timer handle
FD Operations
// Create FD from raw descriptor
fd := iofd.NewFD(rawFd)
// NewFD takes close ownership. Do not close copied FD values;
// close only after users are drained. Use fd.Dup() for an
// independent descriptor owner.

// Atomic operations
fd.Raw()           // Get raw int32 value
fd.Valid()         // Check if valid (non-negative)
fd.Close()         // Same-cell close after drain

// I/O operations
fd.Read(buf)       // Read bytes
fd.Write(buf)      // Write bytes

// Descriptor flags
fd.SetNonblock(true)   // Set O_NONBLOCK
fd.SetCloexec(true)    // Set FD_CLOEXEC
fd.Dup()               // Duplicate with CLOEXEC
Constructor Flags
Constructor Default flags
NewEventFD, NewEventFDSemaphore `EFD_NONBLOCK
NewTimerFD, NewTimerFDRealtime, NewTimerFDBoottime `TFD_NONBLOCK
NewSignalFD `SFD_NONBLOCK
NewPidFD PIDFD_NONBLOCK; close-on-exec is set by the kernel
NewPidFDBlocking Blocking pidfd; close-on-exec is still set by the kernel
NewMemFD, NewMemFDSealed, NewMemFDHugeTLB MFD_CLOEXEC plus memfd-specific flags; no creation-time nonblocking flag exists
MemFD Memory Mapping
// Create memfd and set size
mfd, _ := iofd.NewMemFD("buffer")
mfd.Truncate(4096)

// Map for zero-copy access
region, _ := mfd.Mmap(4096, iofd.PROT_READ|iofd.PROT_WRITE)
data := region.Bytes()  // []byte backed by shared memory
copy(data, []byte("hello"))

// Cleanup
region.Unmap()
mfd.Close()

Architecture

┌─────────────────────────────────────────────────────────┐
│                   Application Layer                      │
├─────────────────────────────────────────────────────────┤
│  EventFD │ TimerFD │ MemFD │ PidFD │ SignalFD │   FD   │
├─────────────────────────────────────────────────────────┤
│                        iofd                              │
├─────────────────────────────────────────────────────────┤
│                       zcall                              │
│                (zero-overhead syscalls)                  │
├─────────────────────────────────────────────────────────┤
│                   Linux Kernel                           │
└─────────────────────────────────────────────────────────┘

Platform Support

Platform FD Core EventFD TimerFD PidFD MemFD SignalFD
Linux/amd64
Linux/arm64
Darwin/arm64
FreeBSD/amd64

Note: Specialized handles (EventFD, TimerFD, etc.) are Linux-specific kernel primitives. On Darwin and FreeBSD, only the core FD type is available.

Safety Considerations

  • Atomic Operations: Raw, Valid, and same-cell Close use atomic access; callers still drain users before Close()
  • Ownership: Close() is idempotent for the same FD cell; copied open FD values are not independent owners
  • Close Ordering: Call Close() only after in-flight operations and borrowed raw descriptor users are drained
  • Valid Check: Use Valid() before operations on potentially closed descriptors
  • Duplication: Use Dup() or PidFD.GetFD() when another closeable descriptor is required
  • MappedRegion Lifetime: Bytes() slice is only valid while the region is mapped

License

MIT — see LICENSE.

©2025 Hayabusa Cloud Co., Ltd.

Documentation

Overview

Package iofd provides minimal file descriptor abstractions and specialized Linux handles for the Go ecosystem. It serves as the canonical handle abstraction for high-performance I/O systems.

Design Principles

Zero-Overhead: All kernel interactions use code.hybscloud.com/zcall exclusively, bypassing Go's standard library syscall hooks. This eliminates runtime scheduler overhead in hot paths.

Allocation Discipline: Fixed-size EventFD, TimerFD, and SignalFD success paths keep syscall argument storage stack-backed and do not allocate heap memory. Caller-provided Into methods keep result storage caller-owned.

Atomic Lifecycle: FD uses atomic operations through one addressable descriptor cell. Close() is idempotent for that same cell. Copying an open FD does not duplicate kernel ownership; use Dup() for an independent close-capable descriptor.

Non-Blocking Constructors: EventFD, TimerFD, SignalFD, and NewPidFD create non-blocking descriptors by default. MemFD is created close-on-exec but has no creation-time nonblocking flag, and NewPidFDBlocking intentionally creates a blocking pidfd. Non-blocking operations return iox.ErrWouldBlock when they would block.

Supported Architectures

The core FD type works on all Unix platforms. Specialized handles (EventFD, TimerFD, PidFD, MemFD, SignalFD) require Linux.

Platform         FD Core   EventFD   TimerFD   PidFD   MemFD   SignalFD
Linux/amd64        ✓         ✓         ✓        ✓       ✓        ✓
Linux/arm64        ✓         ✓         ✓        ✓       ✓        ✓
Darwin/arm64       ✓         -         -        -       -        -
FreeBSD/amd64      ✓         -         -        -       -        -

Usage

Basic EventFD usage:

efd, err := iofd.NewEventFD(0)
if err != nil {
	return err
}
defer efd.Close()

// Signal from one goroutine
efd.Signal(1)

// Wait in another
val, err := efd.Wait()

Timer example:

tfd, err := iofd.NewTimerFD()
if err != nil {
	return err
}
defer tfd.Close()

// Arm for 100ms one-shot
tfd.ArmDuration(100*time.Millisecond, 0)

// Poll or use with epoll/io_uring, then read expirations
count, err := tfd.Expirations()

Safety Considerations

Atomic Operations: FD access uses atomic load/store through one addressable FD cell. Atomicity protects same-cell state transitions; it does not make descriptor-number reuse safe if Close races with in-flight operations.

Valid Check: Always check Valid() or handle ErrClosed before performing operations on potentially closed descriptors.

Close Idempotency: Close() can be called multiple times safely on the same FD cell. Do not close copied FD values; they are not independent owners. After Close(), Raw() returns -1 and operations return ErrClosed.

Close Ordering: Call Close only after all in-flight operations and borrowed raw descriptor users are drained. The package does not add hidden synchronization around kernel descriptor reuse.

MappedRegion Lifetime: When using MemFD.Mmap(), the returned MappedRegion's Bytes() slice is only valid while the region is mapped. Call Unmap() when done.

Dependencies

This package depends on:

  • code.hybscloud.com/zcall — Zero-overhead syscalls
  • code.hybscloud.com/iox — Semantic errors (ErrWouldBlock)

Package iofd provides minimal file descriptor abstractions and specialized Linux handles for the Go ecosystem. It serves as the common denominator for kernel resource lifecycle management.

All kernel interactions use code.hybscloud.com/zcall exclusively, bypassing Go's standard library syscall hooks for zero-overhead operation.

Index

Constants

View Source
const (
	O_NONBLOCK = 0x800
	O_CLOEXEC  = 0x80000
)

File status flags for fcntl F_GETFL/F_SETFL. These are consistent across all Linux architectures.

View Source
const (
	F_DUPFD         = 0
	F_GETFD         = 1
	F_SETFD         = 2
	F_GETFL         = 3
	F_SETFL         = 4
	F_DUPFD_CLOEXEC = 1030
)

fcntl commands. These are consistent across all Linux architectures.

View Source
const (
	EFD_SEMAPHORE = 0x1
	EFD_CLOEXEC   = 0x80000
	EFD_NONBLOCK  = 0x800
)

eventfd flags

View Source
const (
	MFD_CLOEXEC       = 0x1
	MFD_ALLOW_SEALING = 0x2
	MFD_HUGETLB       = 0x4
	MFD_NOEXEC_SEAL   = 0x8
	MFD_EXEC          = 0x10
)

memfd flags

View Source
const (
	F_SEAL_SEAL         = 0x1  // Prevent further seals
	F_SEAL_SHRINK       = 0x2  // Prevent shrinking
	F_SEAL_GROW         = 0x4  // Prevent growing
	F_SEAL_WRITE        = 0x8  // Prevent writes
	F_SEAL_FUTURE_WRITE = 0x10 // Prevent future writes (allows current mappings)
)

Seal types for memfd

View Source
const (
	F_ADD_SEALS = 1033
	F_GET_SEALS = 1034
)

fcntl commands for sealing

View Source
const (
	PROT_NONE  = 0x0
	PROT_READ  = 0x1
	PROT_WRITE = 0x2
	PROT_EXEC  = 0x4
)

Memory protection flags for Mmap.

View Source
const (
	MAP_SHARED  = 0x1
	MAP_PRIVATE = 0x2
)

Memory mapping flags.

View Source
const (
	SIGHUP    = 1
	SIGINT    = 2
	SIGQUIT   = 3
	SIGILL    = 4
	SIGTRAP   = 5
	SIGABRT   = 6
	SIGBUS    = 7
	SIGFPE    = 8
	SIGKILL   = 9
	SIGUSR1   = 10
	SIGSEGV   = 11
	SIGUSR2   = 12
	SIGPIPE   = 13
	SIGALRM   = 14
	SIGTERM   = 15
	SIGSTKFLT = 16
	SIGCHLD   = 17
	SIGCONT   = 18
	SIGSTOP   = 19
	SIGTSTP   = 20
	SIGTTIN   = 21
	SIGTTOU   = 22
	SIGURG    = 23
	SIGXCPU   = 24
	SIGXFSZ   = 25
	SIGVTALRM = 26
	SIGPROF   = 27
	SIGWINCH  = 28
	SIGIO     = 29
	SIGPWR    = 30
	SIGSYS    = 31
)

Signal constants matching Linux signal numbers.

View Source
const (
	SFD_CLOEXEC  = 0x80000
	SFD_NONBLOCK = 0x800
)

signalfd flags

View Source
const (
	CLOCK_REALTIME  = 0
	CLOCK_MONOTONIC = 1
	CLOCK_BOOTTIME  = 7
)

Clock IDs

View Source
const (
	TFD_CLOEXEC       = 0x80000
	TFD_NONBLOCK      = 0x800
	TFD_TIMER_ABSTIME = 0x1
)

timerfd flags

View Source
const (
	FD_CLOEXEC = 1
)

File descriptor flags for fcntl F_GETFD/F_SETFD. These are consistent across all Linux architectures.

View Source
const (
	PIDFD_NONBLOCK = 0x800
)

pidfd flags

View Source
const (
	SYS_FCNTL = 72
)

Syscall numbers for Linux amd64.

Variables

View Source
var (
	// ErrClosed indicates the file descriptor has been closed.
	ErrClosed = errors.New("fd: file descriptor closed")

	// ErrInvalidParam indicates an invalid parameter was passed.
	ErrInvalidParam = errors.New("fd: invalid parameter")

	// ErrInterrupted indicates the operation was interrupted by a signal.
	ErrInterrupted = errors.New("fd: interrupted")

	// ErrNoMemory indicates insufficient memory for the operation.
	ErrNoMemory = errors.New("fd: no memory")

	// ErrPermission indicates permission denied.
	ErrPermission = errors.New("fd: permission denied")
)

Error definitions for iofd operations. These errors provide semantic meaning for common file descriptor failures.

Functions

This section is empty.

Types

type EventFD

type EventFD struct {
	// contains filtered or unexported fields
}

EventFD represents a Linux eventfd file descriptor. It provides an efficient inter-thread/kernel signaling mechanism.

An eventfd maintains an unsigned 64-bit counter. Writing adds to the counter, reading returns and resets it (or decrements by 1 in semaphore mode).

EventFD is created with EFD_NONBLOCK and EFD_CLOEXEC by default. Do not copy EventFD after first use.

func NewEventFD

func NewEventFD(initval uint) (*EventFD, error)

NewEventFD creates a new eventfd with the given initial value. The eventfd is created with EFD_NONBLOCK | EFD_CLOEXEC flags.

Returns iox.ErrWouldBlock semantics are used for non-blocking operations.

func NewEventFDSemaphore

func NewEventFDSemaphore(initval uint) (*EventFD, error)

NewEventFDSemaphore creates a new eventfd in semaphore mode. The eventfd is created with EFD_SEMAPHORE, EFD_NONBLOCK, and EFD_CLOEXEC. In semaphore mode, reads decrement the counter by 1 instead of resetting it.

func (*EventFD) Close

func (e *EventFD) Close() error

Close closes the eventfd. Implements PollCloser interface.

func (*EventFD) Fd

func (e *EventFD) Fd() int

Fd returns the underlying file descriptor. Implements PollFd interface.

func (*EventFD) Raw added in v0.2.0

func (e *EventFD) Raw() int32

Raw returns the raw file descriptor for use in tight loops. The caller must ensure the EventFD remains valid while using the raw fd. The returned descriptor number is borrowed and must not be closed directly.

func (*EventFD) Read

func (e *EventFD) Read(p []byte) (int, error)

Read reads the eventfd counter into p. p must be at least 8 bytes. Only the first 8 bytes are used. This is a lower-level interface; prefer Wait() for typical usage. Returns iox.ErrWouldBlock if the counter is zero.

func (*EventFD) Signal

func (e *EventFD) Signal(val uint64) error

Signal increments the eventfd counter by val. Returns iox.ErrWouldBlock if the counter would overflow (non-blocking mode).

The maximum value is 0xFFFFFFFFFFFFFFFE (2^64 - 2). Writing would block/fail if adding val would exceed this limit. On the success path, Signal does not allocate heap memory.

func (*EventFD) Valid added in v0.2.0

func (e *EventFD) Valid() bool

Valid reports whether the eventfd is still valid.

func (*EventFD) Wait

func (e *EventFD) Wait() (uint64, error)

Wait reads and returns the eventfd counter value. In normal mode, this resets the counter to zero. In semaphore mode, this decrements the counter by 1.

Returns iox.ErrWouldBlock if the counter is zero (non-blocking mode). On the success path, Wait does not allocate heap memory.

func (*EventFD) WaitInto added in v0.3.1

func (e *EventFD) WaitInto(val *uint64) error

WaitInto reads the eventfd counter value into a caller-provided pointer. Returns iox.ErrWouldBlock if the counter is zero (non-blocking mode). This stores the result in caller-owned memory for hot paths.

Postcondition: On success, *val contains the counter value.

func (*EventFD) Write

func (e *EventFD) Write(p []byte) (int, error)

Write writes a value to the eventfd from p. p must be at least 8 bytes containing a native-endian uint64. This is a lower-level interface; prefer Signal() for typical usage. Returns iox.ErrWouldBlock if the counter would overflow.

type FD

type FD int32

FD represents one close-capable file descriptor cell. It stores the raw descriptor number as an int32 and uses atomic operations for Raw, Valid, and same-cell Close access.

An FD value is small and copyable as a Go value, but copying an open FD does not duplicate kernel ownership. A copied FD contains the same descriptor number in a different Go cell; closing both cells can close an unrelated descriptor if the number is reused. Use Dup to create an independent close-capable descriptor.

FD does not serialize Close against in-flight operations or borrowed raw descriptor users. Callers must stop and drain descriptor users before closing.

Invariants:

  • A valid FD holds a non-negative value.
  • After Close(), the FD value becomes -1.
  • FD access is atomic through one addressable cell.
  • Close is idempotent for that same cell.
const InvalidFD FD = -1

InvalidFD represents an invalid file descriptor.

func NewFD

func NewFD(fd int) FD

NewFD creates an FD from a raw file descriptor value. The caller is responsible for ensuring fd is valid and for transferring close ownership to the returned FD. If the raw descriptor is only borrowed, do not call Close on the returned value.

func (*FD) Close

func (fd *FD) Close() error

Close closes the file descriptor owned by this FD cell. It is safe to call Close multiple times on the same FD cell; subsequent calls are no-ops.

Close idempotence does not extend across copied FD values. Copying an open FD copies the descriptor number, not ownership. Use Dup to create a second descriptor that may be closed independently.

Call Close only after all in-flight operations and borrowed raw descriptor users are drained. Returns nil if already closed.

Postcondition: fd.Raw() == -1

func (*FD) Dup

func (fd *FD) Dup() (FD, error)

Dup duplicates the file descriptor and returns a new close-capable FD. The returned FD refers to the same open file description through a distinct descriptor-table entry and has FD_CLOEXEC set by default.

func (*FD) Fd

func (fd *FD) Fd() int

Fd returns the file descriptor number as an int for interface compatibility. Implements PollFd interface.

The returned descriptor number is borrowed and is valid only while the FD remains open.

func (*FD) Raw

func (fd *FD) Raw() int32

Raw returns the underlying file descriptor number as an int32. Returns -1 if the FD is invalid or closed.

Raw is a borrowed observation only. It does not transfer ownership and the returned descriptor number must not be closed independently. Callers must keep the FD open while using the returned number.

func (*FD) Read

func (fd *FD) Read(p []byte) (int, error)

Read reads up to len(p) bytes from the file descriptor. Returns iox.ErrWouldBlock if the fd is non-blocking and no data is available.

On EOF (read returns 0 bytes with no error), this returns (0, nil) rather than (0, io.EOF). This is intentional for low-level I/O:

  • For SOCK_STREAM: (0, nil) indicates peer closed the connection (EOF)
  • For SOCK_DGRAM/SOCK_SEQPACKET: (0, nil) indicates an empty message was received, which is NOT EOF - the peer may send more messages

Higher-level stream abstractions should translate (0, nil) to io.EOF if needed.

func (*FD) SetCloexec

func (fd *FD) SetCloexec(cloexec bool) error

SetCloexec sets or clears the FD_CLOEXEC flag on the file descriptor.

func (*FD) SetNonblock

func (fd *FD) SetNonblock(nonblock bool) error

SetNonblock sets or clears the O_NONBLOCK flag on the file descriptor.

func (*FD) Valid

func (fd *FD) Valid() bool

Valid reports whether the file descriptor is valid (non-negative).

func (*FD) Write

func (fd *FD) Write(p []byte) (int, error)

Write writes len(p) bytes to the file descriptor. Returns iox.ErrWouldBlock if the fd is non-blocking and cannot accept data.

type Handle

type Handle interface {
	PollCloser
	ReadWriter
}

Handle represents a generic kernel handle with full I/O capabilities.

type MappedRegion added in v0.3.0

type MappedRegion struct {
	// contains filtered or unexported fields
}

MappedRegion represents a memory-mapped region of a MemFD. The region must be unmapped with Unmap when no longer needed.

func (*MappedRegion) Bytes added in v0.3.0

func (r *MappedRegion) Bytes() []byte

Bytes returns the mapped region as a byte slice. The returned slice is only valid while the region is mapped. After Unmap returns, all previously returned slices are invalid.

Concurrency: The caller must synchronize access to the returned slice. Concurrent reads are safe; concurrent writes require external locking.

func (*MappedRegion) Len added in v0.3.0

func (r *MappedRegion) Len() int

Len returns the length of the mapped region in bytes. On 32-bit systems, regions larger than 2GB are not supported.

func (*MappedRegion) Ptr added in v0.3.0

func (r *MappedRegion) Ptr() unsafe.Pointer

Ptr returns the base pointer of the mapped region.

func (*MappedRegion) Unmap added in v0.3.0

func (r *MappedRegion) Unmap() error

Unmap unmaps the memory region. After this call, the region must not be accessed.

type MemFD

type MemFD struct {
	// contains filtered or unexported fields
}

MemFD represents a Linux memfd file descriptor. It provides an anonymous memory-backed file that can be used for:

  • Inter-process communication via file descriptor passing
  • Memory mapping without filesystem overhead
  • Sealing to prevent modifications

MemFD is created with MFD_CLOEXEC by default. memfd_create has no creation-time nonblocking flag. Do not copy MemFD after first use.

Invariants:

  • The file exists only in memory; it has no filesystem presence.
  • Size starts at 0; use Truncate to set the desired size before use.
  • Content is zeroed on allocation.

func NewMemFD

func NewMemFD(name string) (*MemFD, error)

NewMemFD creates a new memfd with the given name. The name is used for debugging (visible in /proc/[pid]/fd/). The memfd is created with MFD_CLOEXEC. It is not created nonblocking because Linux memfd_create does not provide a nonblocking creation flag.

The file is initially empty; call Truncate to set its size.

func NewMemFDHugeTLB

func NewMemFDHugeTLB(name string) (*MemFD, error)

NewMemFDHugeTLB creates a new memfd backed by huge pages. The memfd is created with MFD_CLOEXEC and MFD_HUGETLB. This can improve performance for large memory mappings. The size must be a multiple of the huge page size.

func NewMemFDSealed

func NewMemFDSealed(name string) (*MemFD, error)

NewMemFDSealed creates a new memfd that allows sealing operations. The memfd is created with MFD_CLOEXEC and MFD_ALLOW_SEALING. Use this when you need to apply seals to prevent modifications.

func (*MemFD) Close

func (m *MemFD) Close() error

Close closes the memfd. The memory is freed when all references (including mmaps) are released. Implements PollCloser interface.

func (*MemFD) Fd

func (m *MemFD) Fd() int

Fd returns the underlying file descriptor. Implements PollFd interface.

func (*MemFD) Mmap added in v0.3.0

func (m *MemFD) Mmap(length int, prot int) (*MappedRegion, error)

Mmap maps the memfd into memory for zero-copy access. The mapping starts at offset 0 and spans the specified length.

prot specifies the memory protection: PROT_READ, PROT_WRITE, or both. The memfd must be sized appropriately before mapping (see Truncate).

The returned MappedRegion must be unmapped with Unmap when done. The MemFD can be closed after mapping; the region remains valid.

func (*MemFD) MmapAt added in v0.3.0

func (m *MemFD) MmapAt(length int, prot int, offset int64) (*MappedRegion, error)

MmapAt maps the memfd into memory starting at the given offset. offset must be page-aligned (typically 4096 bytes).

func (*MemFD) Name

func (m *MemFD) Name() string

Name returns the name given at creation.

func (*MemFD) Pread added in v0.2.0

func (m *MemFD) Pread(p []byte, offset int64) (int, error)

Pread reads from the memfd at the specified offset without changing the file position.

func (*MemFD) Pwrite added in v0.2.0

func (m *MemFD) Pwrite(p []byte, offset int64) (int, error)

Pwrite writes to the memfd at the specified offset without changing the file position.

func (*MemFD) Raw added in v0.2.0

func (m *MemFD) Raw() int32

Raw returns the raw file descriptor for use in tight loops. The caller must ensure the MemFD remains valid while using the raw fd. The returned descriptor number is borrowed and must not be closed directly. Callers must synchronize Close with users of the borrowed descriptor number.

func (*MemFD) Read

func (m *MemFD) Read(p []byte) (int, error)

Read reads from the memfd at the current file offset.

func (*MemFD) Seal

func (m *MemFD) Seal(seals uint) error

Seal applies seals to prevent certain operations. This is only available if the memfd was created with MFD_ALLOW_SEALING.

Once a seal is applied, it cannot be removed.

func (*MemFD) Seals

func (m *MemFD) Seals() (uint, error)

Seals returns the current seal flags.

func (*MemFD) Size

func (m *MemFD) Size() (int64, error)

Size returns the current size of the memfd.

func (*MemFD) Truncate

func (m *MemFD) Truncate(size int64) error

Truncate sets the size of the memfd. If the new size is larger, the extended area is zero-filled. If smaller, data beyond the new size is discarded.

func (*MemFD) Valid

func (m *MemFD) Valid() bool

Valid reports whether the memfd is still valid.

func (*MemFD) Write

func (m *MemFD) Write(p []byte) (int, error)

Write writes to the memfd at the current file offset.

type PidFD

type PidFD struct {
	// contains filtered or unexported fields
}

PidFD represents a Linux pidfd file descriptor. It provides a stable handle to a process that avoids PID reuse races.

A pidfd becomes readable when the process terminates, making it suitable for polling via epoll and io_uring. pidfd_open sets close-on-exec on returned pidfds. Do not copy PidFD after first use.

Invariants:

  • The pidfd refers to a specific process instance, not just a PID.
  • After the process exits, the pidfd remains valid for signal/wait operations.

func NewPidFD

func NewPidFD(pid int) (*PidFD, error)

NewPidFD creates a new pidfd for the specified process ID. The pidfd is created with PIDFD_NONBLOCK. pidfd_open sets close-on-exec.

Returns an error if the process does not exist or if pidfd is not supported.

func NewPidFDBlocking

func NewPidFDBlocking(pid int) (*PidFD, error)

NewPidFDBlocking creates a new pidfd for the specified process ID without the PIDFD_NONBLOCK flag. pidfd_open still sets close-on-exec.

func (*PidFD) Close

func (p *PidFD) Close() error

Close closes the pidfd. Implements PollCloser interface.

func (*PidFD) Fd

func (p *PidFD) Fd() int

Fd returns the underlying file descriptor. Implements PollFd interface.

func (*PidFD) GetFD

func (p *PidFD) GetFD(targetFD int) (FD, error)

GetFD duplicates a file descriptor from the target process. targetFD is the file descriptor number in the target process.

This operation requires appropriate privileges (CAP_SYS_PTRACE or being in the same user namespace with PTRACE_MODE_ATTACH_REALCREDS).

Returns a new close-capable FD in the current process that refers to the same open file description as targetFD in the target process. The returned FD has close-on-exec set by the kernel.

func (*PidFD) PID

func (p *PidFD) PID() int

PID returns the process ID this pidfd refers to. Note: The PID value may be reused by a new process after the original exits, but the pidfd still refers to the original process instance.

func (*PidFD) Raw added in v0.2.0

func (p *PidFD) Raw() int32

Raw returns the raw file descriptor for use in tight loops. The caller must ensure the PidFD remains valid while using the raw fd. The returned descriptor number is borrowed and must not be closed directly. Callers must synchronize Close with users of the borrowed descriptor number.

func (*PidFD) SendSignal

func (p *PidFD) SendSignal(sig int) error

SendSignal sends a signal to the process. This is race-free with respect to PID reuse.

sig is the signal number to send (e.g., SIGTERM, SIGKILL). Returns nil on success.

func (*PidFD) Valid

func (p *PidFD) Valid() bool

Valid reports whether the pidfd is still valid.

type PollCloser

type PollCloser interface {
	PollFd
	// Close releases the underlying file descriptor.
	// Call Close only after in-flight users of the descriptor are drained.
	// After Close returns, Fd() behavior is undefined.
	Close() error
}

PollCloser extends PollFd with the ability to close the resource.

type PollFd

type PollFd interface {
	// Fd returns the underlying file descriptor as an integer.
	// The returned value is borrowed and valid only while the resource is open.
	// It must not be closed independently.
	// Callers must keep the resource open while using the returned value.
	Fd() int
}

PollFd represents a pollable file descriptor. Any resource that can be monitored for I/O readiness implements this interface.

type ReadWriter

type ReadWriter interface {
	Reader
	Writer
}

ReadWriter combines Reader and Writer interfaces.

type Reader

type Reader interface {
	// Read reads up to len(p) bytes into p.
	// Returns the number of bytes read and any error encountered.
	// Returns iox.ErrWouldBlock if the resource is not ready.
	Read(p []byte) (n int, err error)
}

Reader is an interface for reading from a file descriptor.

type SigSet

type SigSet uint64

SigSet represents a signal set for signalfd operations. This package represents it as a 64-bit mask where bit N represents signal N+1.

Limitation: This implementation supports signals 1-64 only. Real-time signals beyond SIGRTMIN+32 (signal 64) are not supported. For most applications, standard signals (1-31) are sufficient.

func (*SigSet) Add

func (s *SigSet) Add(sig int)

Add adds a signal to the set. If sig is outside the valid range (1-64), the call is a no-op.

func (*SigSet) Del

func (s *SigSet) Del(sig int)

Del removes a signal from the set. If sig is outside the valid range (1-64), the call is a no-op.

func (SigSet) Empty

func (s SigSet) Empty() bool

Empty reports whether the set contains no signals.

func (SigSet) Has

func (s SigSet) Has(sig int) bool

Has reports whether the signal is in the set. Returns false if sig is outside the valid range (1-64).

type SignalFD

type SignalFD struct {
	// contains filtered or unexported fields
}

SignalFD represents a Linux signalfd file descriptor. It provides a file descriptor for accepting signals synchronously, enabling signal handling via poll/epoll/io_uring.

SignalFD is created with SFD_NONBLOCK and SFD_CLOEXEC by default. Do not copy SignalFD after first use.

Invariants:

  • The caller must block the signals with sigprocmask before using signalfd.
  • Each Read returns exactly one SignalInfo structure (128 bytes).

func NewSignalFD

func NewSignalFD(mask SigSet) (*SignalFD, error)

NewSignalFD creates a new signalfd monitoring the given signal set. The signalfd is created with SFD_NONBLOCK and SFD_CLOEXEC flags.

The caller should block the signals in the set using sigprocmask before creating the signalfd to prevent default signal handling.

func (*SignalFD) Close

func (s *SignalFD) Close() error

Close closes the signalfd. Implements PollCloser interface.

func (*SignalFD) Fd

func (s *SignalFD) Fd() int

Fd returns the underlying file descriptor. Implements PollFd interface.

func (*SignalFD) Mask

func (s *SignalFD) Mask() SigSet

Mask returns the current signal mask. Concurrency: callers must synchronize with concurrent SetMask calls.

func (*SignalFD) Raw added in v0.2.0

func (s *SignalFD) Raw() int32

Raw returns the raw file descriptor for use in tight loops. The caller must ensure the SignalFD remains valid while using the raw fd. The returned descriptor number is borrowed and must not be closed directly.

func (*SignalFD) Read

func (s *SignalFD) Read(p []byte) (int, error)

Read reads signal information into the provided buffer. Implements io.Reader interface. p must be at least 128 bytes. Returns iox.ErrWouldBlock if no signal is pending.

func (*SignalFD) ReadInto

func (s *SignalFD) ReadInto(info *SignalInfo) error

ReadInto reads signal information into a caller-provided SignalInfo. Returns iox.ErrWouldBlock if no signal is pending. This stores the result in caller-owned memory for hot paths.

Postcondition: On success, info contains the next pending signal.

func (*SignalFD) SetMask

func (s *SignalFD) SetMask(mask SigSet) error

SetMask updates the signal mask monitored by this signalfd. Concurrency: SetMask/Mask are unsynchronized; callers must serialize concurrent access. On the success path, SetMask does not allocate heap memory.

func (*SignalFD) Valid added in v0.2.0

func (s *SignalFD) Valid() bool

Valid reports whether the signalfd is still valid.

type SignalInfo

type SignalInfo struct {
	Signo   uint32 // Signal number
	Errno   int32  // Error number (unused)
	Code    int32  // Signal code
	PID     uint32 // PID of sender
	UID     uint32 // UID of sender
	FD      int32  // File descriptor (SIGIO)
	TID     uint32 // Kernel timer ID (POSIX timers)
	Band    uint32 // Band event (SIGIO)
	Overrun uint32 // Overrun count (POSIX timers)
	Trapno  uint32 // Trap number
	Status  int32  // Exit status or signal (SIGCHLD)
	Int     int32  // Integer sent by sigqueue
	Ptr     uint64 // Pointer sent by sigqueue
	Utime   uint64 // User CPU time (SIGCHLD)
	Stime   uint64 // System CPU time (SIGCHLD)
	Addr    uint64 // Fault address (SIGILL, SIGFPE, SIGSEGV, SIGBUS)
	AddrLsb uint16 // LSB of address (SIGBUS)

	Syscall  int32  // Syscall number (SIGSYS)
	CallAddr uint64 // Syscall instruction address (SIGSYS)
	Arch     uint32 // Architecture (SIGSYS)
	// contains filtered or unexported fields
}

SignalInfo contains information about a received signal. This structure matches struct signalfd_siginfo from the Linux kernel.

type Signaler

type Signaler interface {
	PollCloser
	// Signal increments the eventfd counter by the given value.
	// Returns iox.ErrWouldBlock if the counter would overflow.
	Signal(val uint64) error
	// Wait reads and resets the eventfd counter.
	// Returns iox.ErrWouldBlock if the counter is zero.
	Wait() (uint64, error)
}

Signaler is an interface for signaling mechanisms like eventfd.

type Timer

type Timer interface {
	PollCloser
	// Arm sets the timer to expire after the given duration.
	// If interval is non-zero, the timer repeats with that interval.
	Arm(initial, interval int64) error
	// Disarm stops the timer.
	Disarm() error
}

Timer is an interface for timer handles like timerfd.

type TimerFD

type TimerFD struct {
	// contains filtered or unexported fields
}

TimerFD represents a Linux timerfd file descriptor. It provides a high-resolution timer that can be monitored via poll/epoll/io_uring.

TimerFD is created with TFD_NONBLOCK and TFD_CLOEXEC by default. Do not copy TimerFD after first use.

func NewTimerFD

func NewTimerFD() (*TimerFD, error)

NewTimerFD creates a new timerfd using CLOCK_MONOTONIC. The timerfd is created with TFD_NONBLOCK and TFD_CLOEXEC. The timer is initially disarmed.

func NewTimerFDBoottime

func NewTimerFDBoottime() (*TimerFD, error)

NewTimerFDBoottime creates a new timerfd using CLOCK_BOOTTIME. The timerfd is created with TFD_NONBLOCK and TFD_CLOEXEC. This clock includes time spent in suspend.

func NewTimerFDRealtime

func NewTimerFDRealtime() (*TimerFD, error)

NewTimerFDRealtime creates a new timerfd using CLOCK_REALTIME. The timerfd is created with TFD_NONBLOCK and TFD_CLOEXEC. Use this for wall-clock time that adjusts with system time changes.

func (*TimerFD) Arm

func (t *TimerFD) Arm(initial, interval int64) error

Arm sets the timer to expire after initial nanoseconds. If interval is non-zero, the timer repeats with that interval (in nanoseconds).

Parameters:

  • initial: time until first expiration in nanoseconds (0 disarms)
  • interval: interval for periodic timer in nanoseconds (0 for one-shot)

On the success path, Arm does not allocate heap memory.

func (*TimerFD) ArmAt

func (t *TimerFD) ArmAt(deadline, interval int64) error

ArmAt sets the timer to expire at an absolute time. If interval is non-zero, the timer repeats with that interval (in nanoseconds).

Parameters:

  • deadline: absolute time for first expiration (Unix nanoseconds)
  • interval: interval for periodic timer in nanoseconds (0 for one-shot)

On the success path, ArmAt does not allocate heap memory.

func (*TimerFD) ArmDuration

func (t *TimerFD) ArmDuration(initial, interval time.Duration) error

ArmDuration is a convenience method that arms the timer using time.Duration.

func (*TimerFD) Close

func (t *TimerFD) Close() error

Close closes the timerfd. Implements PollCloser interface.

func (*TimerFD) Disarm

func (t *TimerFD) Disarm() error

Disarm stops the timer.

func (*TimerFD) Expirations added in v0.2.0

func (t *TimerFD) Expirations() (uint64, error)

Expirations reads the number of expirations since the last read. Returns iox.ErrWouldBlock if no expirations have occurred (non-blocking mode).

The returned value is the number of times the timer has expired since the last successful read. For periodic timers, this may be > 1 if multiple intervals elapsed before reading. On the success path, Expirations does not allocate heap memory.

func (*TimerFD) ExpirationsInto added in v0.3.2

func (t *TimerFD) ExpirationsInto(count *uint64) error

ExpirationsInto reads expiration count into a caller-provided pointer. Returns iox.ErrWouldBlock if no expirations have occurred (non-blocking mode). This stores the result in caller-owned memory for hot paths.

Postcondition: On success, *count contains the expiration count.

func (*TimerFD) Fd

func (t *TimerFD) Fd() int

Fd returns the underlying file descriptor. Implements PollFd interface.

func (*TimerFD) GetTime

func (t *TimerFD) GetTime() (remaining, interval int64, err error)

GetTime returns the current timer setting. Returns (remaining time until expiration, interval) in nanoseconds. On the success path, GetTime does not allocate heap memory.

func (*TimerFD) GetTimeDuration added in v0.3.1

func (t *TimerFD) GetTimeDuration() (remaining, interval time.Duration, err error)

GetTimeDuration is a convenience method that returns timer settings as time.Duration. Symmetric to ArmDuration().

func (*TimerFD) Raw added in v0.2.0

func (t *TimerFD) Raw() int32

Raw returns the raw file descriptor for use in tight loops. The caller must ensure the TimerFD remains valid while using the raw fd. The returned descriptor number is borrowed and must not be closed directly.

func (*TimerFD) Read

func (t *TimerFD) Read(p []byte) (int, error)

Read reads expiration count into the provided buffer. Implements io.Reader interface. p must be at least 8 bytes. Returns iox.ErrWouldBlock if no expirations have occurred.

func (*TimerFD) Valid added in v0.2.0

func (t *TimerFD) Valid() bool

Valid reports whether the timerfd is still valid.

type Writer

type Writer interface {
	// Write writes len(p) bytes from p.
	// Returns the number of bytes written and any error encountered.
	// Returns iox.ErrWouldBlock if the resource is not ready.
	Write(p []byte) (n int, err error)
}

Writer is an interface for writing to a file descriptor.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL