Commits on Source 1

  • cznic's avatar
    fix: restart sleep syscalls interrupted by the Go runtime's own signals · f4e070f6
    cznic authored
    A program translated to Go by ccgo shares its process with the Go runtime,
    which sends SIGURG to a thread to preempt goroutines asynchronously. The
    kernel then fails an in-progress nanosleep(2) with ERESTART_RESTARTBLOCK,
    which becomes EINTR for any signal that has a handler installed - the Go
    runtime's does - and SA_RESTART does not apply to that code. musl reports the
    interruption faithfully, but its callers routinely discard the return value.
    SQLite's unixSleep() is one, so its default busy handler loses whole steps of
    its 1, 2, 5, ... 25, 50, 100 ms back-off and gives up before the timeout the
    caller asked for: measured waits for a 1100 ms busy_timeout came out anywhere
    between 801 ms and 1101 ms, all a multiple of 25 ms short.
    
    That surfaces as sporadic, target-independent failures of upstream SQLite's
    timing assertions - busy2-2.2.* and walsetlk-2.2.* ("the busy wait must last
    longer than 900 ms") - in modernc.org/libsqlite3's Tcl suite. Interleaved runs
    of busy2.test + walsetlk.test on linux/amd64: 5 of 8 runs failed with libc as
    released, 0 of 8 with this change.
    
    ___syscall_cp() now restarts nanosleep(2) and clock_nanosleep(2), including
    the 32-bit time64 variant, for the time the kernel reports as remaining;
    TIMER_ABSTIME sleeps are simply re-issued, their deadline being unchanged.
    When the caller passed no place for the remaining time to be reported, one is
    supplied from the TLS: the kernel writes it in the same layout it reads the
    request in, so it can be handed back as the next request unchanged, which
    keeps this free of any per-target struct timespec knowledge.
    
    Note this makes sleeps on the musl targets uninterruptible by C-installed
    signal handlers too, which is already how the hand-written darwin, freebsd,
    netbsd and openbsd ports behave - their Xnanosleep sleeps in Go and can never
    be interrupted. Making it conditional on the translated program having
    installed a handler of its own is possible, but the two paths would then
    disagree, and a translated program cannot install one without displacing the
    Go runtime's handler for that signal anyway.
    
    Only sleeps are restarted here. Every other blocking syscall reached through
    ___syscall_cp() can still report a spurious EINTR to C code that does not
    retry; those need the same treatment if they turn up.
    
    Co-Authored-By: default avatarClaude Fable 5 <noreply@anthropic.com>
    f4e070f6
Loading
Loading