Skip to content

feat!: errno always-capture — CallFunction returns (syscall.Errno, error) (v0.6.0)#61

Merged
kolkov merged 3 commits into
mainfrom
feat/errno-capture
Jul 12, 2026
Merged

feat!: errno always-capture — CallFunction returns (syscall.Errno, error) (v0.6.0)#61
kolkov merged 3 commits into
mainfrom
feat/errno-capture

Conversation

@kolkov

@kolkov kolkov commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • BREAKING: CallFunction and CallFunctionContext now return (syscall.Errno, error) — errno is always captured
  • Assembly-level errno capture inside trampoline (thread-safe: before exitsyscall can migrate goroutine)
  • First pure-Go FFI with correct errno capture on Linux
  • Platform support: __errno_location (Linux glibc/musl), __error (macOS/FreeBSD)
  • Windows: returns errno=0 (uses GetLastError via separate mechanism)

Why Always-Capture (not opt-in)

Opt-in CallFunctionErrno is a pit of failure — you don't know you need errno until the function fails, and by then the goroutine may be on a different OS thread. Pre-1.0 = do it right.

Cost: ~3-5 ns per call (CALL __errno_location + MOVL). At 200 calls/frame × 60 FPS = 0.02% of frame budget.

Migration

// Before (v0.5.x):
err := ffi.CallFunction(cif, fn, &result, args)

// After (v0.6.0):
errno, err := ffi.CallFunction(cif, fn, &result, args)
// Or:
_, err := ffi.CallFunction(cif, fn, &result, args)

Test plan

  • go build ./... — clean
  • go test ./... — all pass
  • Cross-compile all 8 platforms ✅
  • Examples build
  • golangci-lint: 0 issues
  • CI green (Linux + macOS + Windows × CGO=0/1)
  • errno test passes on Linux/macOS (captures ENOENT from open())

Closes #60

kolkov added 3 commits July 12, 2026 06:24
…60)

Add CallFunctionErrno/CallFunctionErrnoContext to the public API. Errno is
captured inside the assembly trampoline immediately after the C function
returns, before the Go runtime can migrate the goroutine to another OS thread.

Architecture:
- Extend syscallArgs struct with errno/errnoFn fields at the end (no ABI break)
- AMD64 Unix: call __errno_location/__error in assembly after saving returns,
  guarded by TESTQ so regular CallFunction has zero extra cost
- ARM64: same pattern using CBZ guard; R19/R20 are callee-saved across the call
- FunctionCallerErrno optional interface allows type assertion without changing
  existing FunctionCaller; unsupported arches fall back to plain Execute
- errno_stubs_amd64.s / errno_stubs_arm64.s: JMP trampolines to dynamic
  symbols goffi_errno_location (same pattern as internal/dl stubs)
- Windows: stub returns errno=0; use GetLastError/syscall.SyscallN third
  return value for Win32 errors instead

Platforms: Linux/macOS/FreeBSD amd64+arm64 (errno capture); Windows (stub).
All 8 cross-compile targets verified. Tests: TestCallFunctionErrnoBasic
validates ENOENT capture; TestCallFunctionErrnoSuccess validates zero errno.
BREAKING: CallFunction/CallFunctionContext now return (syscall.Errno, error).
Callers that don't need errno use: _, err := ffi.CallFunction(...)

errno is captured inside the assembly trampoline immediately after the C
function returns, before Go runtime can migrate to a different OS thread.
Cost: ~3-5 ns per call (CALL __errno_location + MOVL).

This makes goffi the first pure-Go FFI with correct errno capture on Linux.
Opt-in CallFunctionErrno removed -- always-capture is the enterprise approach.
- CHANGELOG: v0.6.0 entry with breaking changes and migration guide
- README: update all CallFunction examples to (_, err) pattern, add errno feature
- ARCHITECTURE: document errno capture in Layer 2 section
- PERFORMANCE: document +3-5ns errno overhead
@codecov

codecov Bot commented Jul 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
ffi/call.go 75.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@kolkov
kolkov merged commit 895a3fa into main Jul 12, 2026
13 checks passed
@kolkov
kolkov deleted the feat/errno-capture branch July 12, 2026 04:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: errno capture in assembly trampoline (CallFunctionErrno)

1 participant