EINTR = C.EINTR
EAGAIN = C.EAGAIN
ENOMEM = C.ENOMEM
+ ENOSYS = C.ENOSYS
PROT_NONE = C.PROT_NONE
PROT_READ = C.PROT_READ
EINTR = C.EINTR
EAGAIN = C.EAGAIN
ENOMEM = C.ENOMEM
+ ENOSYS = C.ENOSYS
PROT_NONE = C.PROT_NONE
PROT_READ = C.PROT_READ
_EINTR = 0x4
_EAGAIN = 0xb
_ENOMEM = 0xc
+ _ENOSYS = 0x26
_PROT_NONE = 0x0
_PROT_READ = 0x1
anon0 [48]byte
}
-type timespec struct {
+// The timespec structs and types are defined in Linux in
+// include/uapi/linux/time_types.h and include/uapi/asm-generic/posix_types.h.
+type timespec32 struct {
tv_sec int32
tv_nsec int32
}
//go:nosplit
-func (ts *timespec) setNsec(ns int64) {
+func (ts *timespec32) setNsec(ns int64) {
ts.tv_sec = timediv(ns, 1e9, &ts.tv_nsec)
}
+type timespec struct {
+ tv_sec int64
+ tv_nsec int64
+}
+
+//go:nosplit
+func (ts *timespec) setNsec(ns int64) {
+ var newNS int32
+ ts.tv_sec = int64(timediv(ns, 1e9, &newNS))
+ ts.tv_nsec = int64(newNS)
+}
+
type timeval struct {
tv_sec int32
tv_usec int32
}
type itimerspec struct {
- it_interval timespec
- it_value timespec
+ it_interval timespec32
+ it_value timespec32
}
type itimerval struct {
_EINTR = 0x4
_EAGAIN = 0xb
_ENOMEM = 0xc
+ _ENOSYS = 0x26
_PROT_NONE = 0x0
_PROT_READ = 0x1
_EINTR = 0x4
_ENOMEM = 0xc
_EAGAIN = 0xb
+ _ENOSYS = 0x26
_PROT_NONE = 0
_PROT_READ = 0x1
_SOCK_DGRAM = 0x2
)
-type timespec struct {
+// The timespec structs and types are defined in Linux in
+// include/uapi/linux/time_types.h and include/uapi/asm-generic/posix_types.h.
+type timespec32 struct {
tv_sec int32
tv_nsec int32
}
//go:nosplit
-func (ts *timespec) setNsec(ns int64) {
+func (ts *timespec32) setNsec(ns int64) {
ts.tv_sec = timediv(ns, 1e9, &ts.tv_nsec)
}
+type timespec struct {
+ tv_sec int64
+ tv_nsec int64
+}
+
+//go:nosplit
+func (ts *timespec) setNsec(ns int64) {
+ var newNS int32
+ ts.tv_sec = int64(timediv(ns, 1e9, &newNS))
+ ts.tv_nsec = int64(newNS)
+}
+
type stackt struct {
ss_sp *byte
ss_flags int32
}
type itimerspec struct {
- it_interval timespec
- it_value timespec
+ it_interval timespec32
+ it_value timespec32
}
type itimerval struct {
_EINTR = 0x4
_EAGAIN = 0xb
_ENOMEM = 0xc
+ _ENOSYS = 0x26
_PROT_NONE = 0x0
_PROT_READ = 0x1
_EINTR = 0x4
_EAGAIN = 0xb
_ENOMEM = 0xc
+ _ENOSYS = 0x26
_PROT_NONE = 0x0
_PROT_READ = 0x1
_EINTR = 0x4
_EAGAIN = 0xb
_ENOMEM = 0xc
+ _ENOSYS = 0x26
_PROT_NONE = 0x0
_PROT_READ = 0x1
_EINTR = 0x4
_EAGAIN = 0xb
_ENOMEM = 0xc
+ _ENOSYS = 0x26
_PROT_NONE = 0x0
_PROT_READ = 0x1
_SIGEV_THREAD_ID = 0x4
)
-type timespec struct {
+// The timespec structs and types are defined in Linux in
+// include/uapi/linux/time_types.h and include/uapi/asm-generic/posix_types.h.
+type timespec32 struct {
tv_sec int32
tv_nsec int32
}
//go:nosplit
-func (ts *timespec) setNsec(ns int64) {
+func (ts *timespec32) setNsec(ns int64) {
ts.tv_sec = timediv(ns, 1e9, &ts.tv_nsec)
}
+type timespec struct {
+ tv_sec int64
+ tv_nsec int64
+}
+
+//go:nosplit
+func (ts *timespec) setNsec(ns int64) {
+ var newNS int32
+ ts.tv_sec = int64(timediv(ns, 1e9, &newNS))
+ ts.tv_nsec = int64(newNS)
+}
+
type timeval struct {
tv_sec int32
tv_usec int32
}
type itimerspec struct {
- it_interval timespec
- it_value timespec
+ it_interval timespec32
+ it_value timespec32
}
type itimerval struct {
_EINTR = 0x4
_EAGAIN = 0xb
_ENOMEM = 0xc
+ _ENOSYS = 0x26
_PROT_NONE = 0x0
_PROT_READ = 0x1
_EINTR = 0x4
_EAGAIN = 0xb
_ENOMEM = 0xc
+ _ENOSYS = 0x26
_PROT_NONE = 0x0
_PROT_READ = 0x1
_EINTR = 0x4
_EAGAIN = 0xb
_ENOMEM = 0xc
+ _ENOSYS = 0x26
_PROT_NONE = 0x0
_PROT_READ = 0x1
_EINTR = 0x4
_EAGAIN = 0xb
_ENOMEM = 0xc
+ _ENOSYS = 0x26
_PROT_NONE = 0x0
_PROT_READ = 0x1
waitsema uint32 // semaphore for parking on locks
}
-//go:noescape
-func futex(addr unsafe.Pointer, op int32, val uint32, ts, addr2 unsafe.Pointer, val3 uint32) int32
-
// Linux futex.
//
// futexsleep(uint32 *addr, uint32 val)
var ts timespec
ts.setNsec(ns)
- futex(unsafe.Pointer(addr), _FUTEX_WAIT_PRIVATE, val, unsafe.Pointer(&ts), nil, 0)
+ futex(unsafe.Pointer(addr), _FUTEX_WAIT_PRIVATE, val, &ts, nil, 0)
}
// If any procs are sleeping on addr, wake up at most cnt.
--- /dev/null
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux && (386 || arm || mips || mipsle || ppc)
+
+package runtime
+
+import (
+ "internal/runtime/atomic"
+ "unsafe"
+)
+
+//go:noescape
+func futex_time32(addr unsafe.Pointer, op int32, val uint32, ts *timespec32, addr2 unsafe.Pointer, val3 uint32) int32
+
+//go:noescape
+func futex_time64(addr unsafe.Pointer, op int32, val uint32, ts *timespec, addr2 unsafe.Pointer, val3 uint32) int32
+
+var is32bitOnly atomic.Bool
+
+//go:nosplit
+func futex(addr unsafe.Pointer, op int32, val uint32, ts *timespec, addr2 unsafe.Pointer, val3 uint32) int32 {
+ if !is32bitOnly.Load() {
+ ret := futex_time64(addr, op, val, ts, addr2, val3)
+ // futex_time64 is only supported on Linux 5.0+
+ if ret != -_ENOSYS {
+ return ret
+ }
+ is32bitOnly.Store(true)
+ }
+ // Downgrade ts.
+ var ts32 timespec32
+ var pts32 *timespec32
+ if ts != nil {
+ ts32.setNsec(ts.tv_sec*1e9 + ts.tv_nsec)
+ pts32 = &ts32
+ }
+ return futex_time32(addr, op, val, pts32, addr2, val3)
+}
--- /dev/null
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux && !(386 || arm || mips || mipsle || ppc || s390)
+
+package runtime
+
+import (
+ "unsafe"
+)
+
+//go:noescape
+func futex(addr unsafe.Pointer, op int32, val uint32, ts *timespec, addr2 unsafe.Pointer, val3 uint32) int32
#define SYS_madvise 219
#define SYS_gettid 224
#define SYS_futex 240
+#define SYS_futex_time64 422
#define SYS_sched_getaffinity 242
#define SYS_set_thread_area 243
#define SYS_exit_group 252
MOVL AX, ret+12(FP)
RET
+// Linux: kernel/futex/syscalls.c, requiring COMPAT_32BIT_TIME
// int32 futex(int32 *uaddr, int32 op, int32 val,
-// struct timespec *timeout, int32 *uaddr2, int32 val2);
-TEXT runtime·futex(SB),NOSPLIT,$0
+// struct old_timespec32 *timeout, int32 *uaddr2, int32 val2);
+TEXT runtime·futex_time32(SB),NOSPLIT,$0
MOVL $SYS_futex, AX
MOVL addr+0(FP), BX
MOVL op+4(FP), CX
MOVL AX, ret+24(FP)
RET
+// Linux: kernel/futex/syscalls.c
+// int32 futex(int32 *uaddr, int32 op, int32 val,
+// struct timespec *timeout, int32 *uaddr2, int32 val2);
+TEXT runtime·futex_time64(SB),NOSPLIT,$0
+ MOVL $SYS_futex_time64, AX
+ MOVL addr+0(FP), BX
+ MOVL op+4(FP), CX
+ MOVL val+8(FP), DX
+ MOVL ts+12(FP), SI
+ MOVL addr2+16(FP), DI
+ MOVL val3+20(FP), BP
+ INVOKE_SYSCALL
+ MOVL AX, ret+24(FP)
+ RET
+
// int32 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void));
TEXT runtime·clone(SB),NOSPLIT,$0
MOVL $SYS_clone, AX
#define SYS_sigaltstack (SYS_BASE + 186)
#define SYS_mmap2 (SYS_BASE + 192)
#define SYS_futex (SYS_BASE + 240)
+#define SYS_futex_time64 (SYS_BASE + 422)
#define SYS_exit_group (SYS_BASE + 248)
#define SYS_munmap (SYS_BASE + 91)
#define SYS_madvise (SYS_BASE + 220)
RET
+// Linux: kernel/futex/syscalls.c, requiring COMPAT_32BIT_TIME
// int32 futex(int32 *uaddr, int32 op, int32 val,
-// struct timespec *timeout, int32 *uaddr2, int32 val2);
-TEXT runtime·futex(SB),NOSPLIT,$0
+// struct old_timespec32 *timeout, int32 *uaddr2, int32 val2);
+TEXT runtime·futex_time32(SB),NOSPLIT,$0
MOVW addr+0(FP), R0
MOVW op+4(FP), R1
MOVW val+8(FP), R2
MOVW R0, ret+24(FP)
RET
+// Linux: kernel/futex/syscalls.c
+// int32 futex(int32 *uaddr, int32 op, int32 val,
+// struct timespec *timeout, int32 *uaddr2, int32 val2);
+TEXT runtime·futex_time64(SB),NOSPLIT,$0
+ MOVW addr+0(FP), R0
+ MOVW op+4(FP), R1
+ MOVW val+8(FP), R2
+ MOVW ts+12(FP), R3
+ MOVW addr2+16(FP), R4
+ MOVW val3+20(FP), R5
+ MOVW $SYS_futex_time64, R7
+ SWI $0
+ MOVW R0, ret+24(FP)
+ RET
+
// int32 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void));
TEXT runtime·clone(SB),NOSPLIT,$0
MOVW flags+0(FP), R0
#define SYS_mincore 4217
#define SYS_gettid 4222
#define SYS_futex 4238
+#define SYS_futex_time64 4422
#define SYS_sched_getaffinity 4240
#define SYS_exit_group 4246
#define SYS_timer_create 4257
MOVW R2, ret+12(FP)
RET
-// int32 futex(int32 *uaddr, int32 op, int32 val, struct timespec *timeout, int32 *uaddr2, int32 val2);
-TEXT runtime·futex(SB),NOSPLIT,$20-28
+// Linux: kernel/futex/syscalls.c, requiring COMPAT_32BIT_TIME
+// int32 futex(int32 *uaddr, int32 op, int32 val,
+// struct old_timespec32 *timeout, int32 *uaddr2, int32 val2);
+TEXT runtime·futex_time32(SB),NOSPLIT,$20-28
MOVW addr+0(FP), R4
MOVW op+4(FP), R5
MOVW val+8(FP), R6
MOVW R2, ret+24(FP)
RET
+// Linux: kernel/futex/syscalls.c
+// int32 futex(int32 *uaddr, int32 op, int32 val,
+// struct timespec *timeout, int32 *uaddr2, int32 val2);
+TEXT runtime·futex_time64(SB),NOSPLIT,$20-28
+ MOVW addr+0(FP), R4
+ MOVW op+4(FP), R5
+ MOVW val+8(FP), R6
+ MOVW ts+12(FP), R7
+
+ MOVW addr2+16(FP), R8
+ MOVW val3+20(FP), R9
+
+ MOVW R8, 16(R29)
+ MOVW R9, 20(R29)
+
+ MOVW $SYS_futex_time64, R2
+ SYSCALL
+ BEQ R7, 2(PC)
+ SUBU R2, R0, R2 // caller expects negative errno
+ MOVW R2, ret+24(FP)
+ RET
// int32 clone(int32 flags, void *stk, M *mp, G *gp, void (*fn)(void));
TEXT runtime·clone(SB),NOSPLIT|NOFRAME,$0-24