Skip to content

proposal: runtime + syscall: single-threaded mode + fork() support #77032

@gh2o

Description

@gh2o

Rationale

There are some cases in which it is desirable to run the Go runtime in a single thread. For example:

  • The ability to use fork(): fork() works reasonably well for single-threaded processes, but is intractably difficult to implement reliably for multi-threaded processes.
  • Ease of debugging: strace is much easier to interpret when all work occurs in a single process, may make debugging certain programs more deterministic.
  • Memory sharing: A parent process could presumably initialize a large set of heap structures before forking a set of child processes. Each child process can then modify their own "copy" of the structures without affecting other processes via fork()'s copy-on-write semantics.

Caveats

  • Goroutines will be scheduled sequentially (as if GOMAXPROCS=1).
  • Performing any blocking syscall will block the whole program indefinitely.
    • Can we get around this with setitimer() on posix platforms when Syscall() is used instead of RawSyscall()?
  • fork() will cause issues if non-Go code starts its own threads unless specifically handled.

API

Environment variable GOSINGLETHREAD

If the environment variable GOSINGLETHREAD is set to a non-empty string at runtime start, the runtime starts up in single-threaded mode. In single-threaded mode:

  • The P count is forced to 1.
  • runtime.LockOSThread() panics.
  • Auxiliary runtime threads (notably sysmon and templateThread) are not started.

New functions in package runtime

// InSingleThreadedMode returns true if the runtime was started
// in single-threaded mode by setting the environment variable
// GOSINGLETHREAD to a non-empty string.
func InSingleThreadedMode() bool

// ExitSingleThreadedMode exits single-threaded mode, restoring the runtime
// to normal multi-threaded mode.
//
// - The P count is restored to the previously configured value.
// - LockOSThread() becomes functional.
// - Auxiliary runtime threads are started.
//
// This is a no-op if the runtime is not in single-threaded mode.
func ExitSingleThreadedMode()

New functions in package syscall

// Fork clones the process as a child of the current process.
// It returns 0 in the child process, and the child PID in the
// parent process.
//
// If cgo is enabled, this calls fork() in the C library.
// Otherwise, this directly calls the underlying syscall.
//
// Fork returns an error if the runtime is not in single-threaded mode.
func Fork() (pid int, err error)

Changes to existing functions

  • runtime.LockOSThread(): Panics while running in single-threaded mode.
  • runtime.GOMAXPROCS(): When single-threaded mode is active, only configures and returns the value that will take effect after runtime.ExitSingleThreadedMode() is called.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Proposalcompiler/runtimeIssues related to the Go compiler and/or runtime.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions