Loading
Commits on Source 1
-
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:Claude Fable 5 <noreply@anthropic.com>