Improve locks and signals

- Introduce fast spinlock API
- Double rand64() perf w/ spinlock
- Improve raise() on New Technology
- Support gettid() across platforms
- Implement SA_NODEFER on New Technology
- Move the lock intrinsics into LIBC_INTRIN
- Make SIGTRAP recoverable on New Technology
- Block SIGCHLD in wait4() on New Technology
- Add threading prototypes for XNU and FreeBSD
- Rewrite abort() fixing its minor bugs on XNU/NT
- Shave down a lot of the content in libc/bits/bits.h
- Let signal handlers modify CPU registers on New Technology
This commit is contained in:
Justine Tunney 2022-04-12 05:20:17 -07:00
parent f68f1789bd
commit 046c7ebd4a
110 changed files with 1514 additions and 876 deletions

View file

@ -107,6 +107,7 @@ include libc/fmt/fmt.mk #─┘
include libc/calls/calls.mk #─┐ include libc/calls/calls.mk #─┐
include libc/runtime/runtime.mk # ├──SYSTEMS RUNTIME include libc/runtime/runtime.mk # ├──SYSTEMS RUNTIME
include libc/crt/crt.mk # │ You can issue system calls include libc/crt/crt.mk # │ You can issue system calls
include libc/thread/thread.mk # │
include libc/rand/rand.mk # │ include libc/rand/rand.mk # │
include libc/unicode/unicode.mk # │ include libc/unicode/unicode.mk # │
include third_party/dlmalloc/dlmalloc.mk #─┘ include third_party/dlmalloc/dlmalloc.mk #─┘
@ -117,7 +118,6 @@ include libc/time/time.mk # │ You can finally call malloc()
include libc/alg/alg.mk # │ include libc/alg/alg.mk # │
include libc/stdio/stdio.mk # │ include libc/stdio/stdio.mk # │
include third_party/libcxx/libcxx.mk # │ include third_party/libcxx/libcxx.mk # │
include libc/thread/thread.mk # │
include net/net.mk # │ include net/net.mk # │
include libc/log/log.mk # │ include libc/log/log.mk # │
include third_party/bzip2/bzip2.mk # │ include third_party/bzip2/bzip2.mk # │
@ -282,7 +282,6 @@ COSMOPOLITAN_OBJECTS = \
LIBC_NT_PSAPI \ LIBC_NT_PSAPI \
LIBC_NT_POWERPROF \ LIBC_NT_POWERPROF \
LIBC_NT_PDH \ LIBC_NT_PDH \
LIBC_NT_KERNELBASE \
LIBC_NT_SHELL32 \ LIBC_NT_SHELL32 \
LIBC_NT_GDI32 \ LIBC_NT_GDI32 \
LIBC_NT_COMDLG32 \ LIBC_NT_COMDLG32 \

View file

@ -8,6 +8,7 @@
*/ */
#endif #endif
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/intrin/kprintf.h"
#include "libc/log/check.h" #include "libc/log/check.h"
#include "libc/log/log.h" #include "libc/log/log.h"
#include "libc/nt/nt/process.h" #include "libc/nt/nt/process.h"

34
libc/bits/asmflag.h Normal file
View file

@ -0,0 +1,34 @@
#ifndef COSMOPOLITAN_LIBC_BITS_ASMFLAG_H_
#define COSMOPOLITAN_LIBC_BITS_ASMFLAG_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
/*
* Constraints for virtual machine flags.
* @note we beseech clang devs for flag constraints
*/
#ifdef __GCC_ASM_FLAG_OUTPUTS__ /* GCC6+ CLANG10+ */
#define CFLAG_CONSTRAINT "=@ccc"
#define CFLAG_ASM(OP) OP
#define ZFLAG_CONSTRAINT "=@ccz"
#define ZFLAG_ASM(OP) OP
#define OFLAG_CONSTRAINT "=@cco"
#define OFLAG_ASM(OP) OP
#define SFLAG_CONSTRAINT "=@ccs"
#define SFLAG_ASM(SP) SP
#define ABOVE_CONSTRAINT "=@cca" /* i.e. !ZF && !CF */
#define ABOVEFLAG_ASM(OP) OP
#else
#define CFLAG_CONSTRAINT "=q"
#define CFLAG_ASM(OP) OP "\n\tsetc\t%b0"
#define ZFLAG_CONSTRAINT "=q"
#define ZFLAG_ASM(OP) OP "\n\tsetz\t%b0"
#define OFLAG_CONSTRAINT "=q"
#define OFLAG_ASM(OP) OP "\n\tseto\t%b0"
#define SFLAG_CONSTRAINT "=q"
#define SFLAG_ASM(SP) OP "\n\tsets\t%b0"
#define ABOVE_CONSTRAINT "=@cca"
#define ABOVEFLAG_ASM(OP) OP "\n\tseta\t%b0"
#endif
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_BITS_ASMFLAG_H_ */

View file

@ -1,6 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_BITS_ATOMIC_H_ #ifndef COSMOPOLITAN_LIBC_BITS_ATOMIC_H_
#define COSMOPOLITAN_LIBC_BITS_ATOMIC_H_ #define COSMOPOLITAN_LIBC_BITS_ATOMIC_H_
#include "libc/bits/bits.h" #include "libc/bits/bits.h"
#include "libc/intrin/lockcmpxchg.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
@ -31,8 +32,8 @@ COSMOPOLITAN_C_START_
}) })
#define atomic_init(PTR, VAL) atomic_store(PTR, VAL) #define atomic_init(PTR, VAL) atomic_store(PTR, VAL)
#define atomic_exchange(PTR, VAL) lockxchg(PTR, &(VAL)) #define atomic_exchange(PTR, VAL) lockxchg(PTR, &(VAL))
#define atomic_compare_exchange_strong(X, Y, Z) lockcmpxchg(X, Y, Z) #define atomic_compare_exchange_strong(X, Y, Z) _lockcmpxchg(X, Y, Z)
#define atomic_compare_exchange_weak(X, Y, Z) lockcmpxchg(X, Y, Z) #define atomic_compare_exchange_weak(X, Y, Z) _lockcmpxchg(X, Y, Z)
#define atomic_load_explicit(PTR, ORDER) atomic_load(PTR) #define atomic_load_explicit(PTR, ORDER) atomic_load(PTR)
#define atomic_store_explicit(PTR, VAL, ORDER) atomic_store(PTR, VAL) #define atomic_store_explicit(PTR, VAL, ORDER) atomic_store(PTR, VAL)
#define atomic_flag_clear_explicit(PTR, ORDER) atomic_store(PTR, 0) #define atomic_flag_clear_explicit(PTR, ORDER) atomic_store(PTR, 0)

45
libc/bits/bitop.h Normal file
View file

@ -0,0 +1,45 @@
#ifndef COSMOPOLITAN_LIBC_BITS_BITOP_H_
#define COSMOPOLITAN_LIBC_BITS_BITOP_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define bts(MEM, BIT) __BitOp("bts", BIT, MEM) /** bit test and set */
#define btr(MEM, BIT) __BitOp("btr", BIT, MEM) /** bit test and reset */
#define btc(MEM, BIT) __BitOp("btc", BIT, MEM) /** bit test and complement */
#define lockbts(MEM, BIT) __BitOp("lock bts", BIT, MEM)
#define lockbtr(MEM, BIT) __BitOp("lock btr", BIT, MEM)
#define lockbtc(MEM, BIT) __BitOp("lock btc", BIT, MEM)
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
#define __BitOp(OP, BIT, MEM) \
({ \
bool OldBit; \
if (__builtin_constant_p(BIT)) { \
asm(CFLAG_ASM(OP "%z1\t%2,%1") \
: CFLAG_CONSTRAINT(OldBit), \
"+m"((MEM)[(BIT) / (sizeof((MEM)[0]) * CHAR_BIT)]) \
: "J"((BIT) % (sizeof((MEM)[0]) * CHAR_BIT)) \
: "cc"); \
} else if (sizeof((MEM)[0]) == 2) { \
asm(CFLAG_ASM(OP "\t%w2,%1") \
: CFLAG_CONSTRAINT(OldBit), "+m"((MEM)[0]) \
: "r"(BIT) \
: "cc"); \
} else if (sizeof((MEM)[0]) == 4) { \
asm(CFLAG_ASM(OP "\t%k2,%1") \
: CFLAG_CONSTRAINT(OldBit), "+m"((MEM)[0]) \
: "r"(BIT) \
: "cc"); \
} else if (sizeof((MEM)[0]) == 8) { \
asm(CFLAG_ASM(OP "\t%q2,%1") \
: CFLAG_CONSTRAINT(OldBit), "+m"((MEM)[0]) \
: "r"(BIT) \
: "cc"); \
} \
OldBit; \
})
#endif /* __GNUC__ && !__STRICT_ANSI__ */
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_BITS_BITOP_H_ */

View file

@ -21,11 +21,6 @@ unsigned long roundup2pow(unsigned long) libcesque pureconst;
unsigned long roundup2log(unsigned long) libcesque pureconst; unsigned long roundup2log(unsigned long) libcesque pureconst;
unsigned long rounddown2pow(unsigned long) libcesque pureconst; unsigned long rounddown2pow(unsigned long) libcesque pureconst;
unsigned long hamming(unsigned long, unsigned long) pureconst; unsigned long hamming(unsigned long, unsigned long) pureconst;
intptr_t lockxchg(void *, void *, size_t);
bool cmpxchg(void *, intptr_t, intptr_t, size_t);
bool lockcmpxchg(void *, intptr_t, intptr_t, size_t);
intptr_t atomic_load(void *, size_t);
intptr_t atomic_store(void *, intptr_t, size_t);
unsigned bextra(const unsigned *, size_t, char); unsigned bextra(const unsigned *, size_t, char);
/*───────────────────────────────────────────────────────────────────────────│─╗ /*───────────────────────────────────────────────────────────────────────────│─╗
@ -136,84 +131,6 @@ unsigned bextra(const unsigned *, size_t, char);
*/ */
#if defined(__GNUC__) && !defined(__STRICT_ANSI__) #if defined(__GNUC__) && !defined(__STRICT_ANSI__)
/*
* Constraints for virtual machine flags.
* @note we beseech clang devs for flag constraints
*/
#ifdef __GCC_ASM_FLAG_OUTPUTS__ /* GCC6+ CLANG10+ */
#define CFLAG_CONSTRAINT "=@ccc"
#define CFLAG_ASM(OP) OP
#define ZFLAG_CONSTRAINT "=@ccz"
#define ZFLAG_ASM(OP) OP
#define OFLAG_CONSTRAINT "=@cco"
#define OFLAG_ASM(OP) OP
#define SFLAG_CONSTRAINT "=@ccs"
#define SFLAG_ASM(SP) SP
#define ABOVE_CONSTRAINT "=@cca" /* i.e. !ZF && !CF */
#define ABOVEFLAG_ASM(OP) OP
#else
#define CFLAG_CONSTRAINT "=q"
#define CFLAG_ASM(OP) OP "\n\tsetc\t%b0"
#define ZFLAG_CONSTRAINT "=q"
#define ZFLAG_ASM(OP) OP "\n\tsetz\t%b0"
#define OFLAG_CONSTRAINT "=q"
#define OFLAG_ASM(OP) OP "\n\tseto\t%b0"
#define SFLAG_CONSTRAINT "=q"
#define SFLAG_ASM(SP) OP "\n\tsets\t%b0"
#define ABOVE_CONSTRAINT "=@cca"
#define ABOVEFLAG_ASM(OP) OP "\n\tseta\t%b0"
#endif
/**
* Reads scalar from memory w/ one operation.
*
* @param MEM is alignas(𝑘) uint𝑘_t[hasatleast 1] where 𝑘 {8,16,32,64}
* @return *(MEM)
* @note defeats compiler load tearing optimizations
* @note alignas(𝑘) is implied if compiler knows type
* @note alignas(𝑘) only avoids multi-core / cross-page edge cases
* @see Intel's Six-Thousand Page Manual V.3A §8.2.3.1
* @see atomic_store()
*/
#define atomic_load(MEM) \
({ \
autotype(MEM) Mem = (MEM); \
typeof(*Mem) Reg; \
asm("mov\t%1,%0" : "=r"(Reg) : "m"(*Mem)); \
Reg; \
})
/**
* Saves scalar to memory w/ one operation.
*
* This is guaranteed to happen in either one or zero operations,
* depending on whether or not it's possible for *(MEM) to be read
* afterwards. This macro only forbids compiler from using >1 ops.
*
* @param MEM is alignas(𝑘) uint𝑘_t[hasatleast 1] where 𝑘 {8,16,32,64}
* @param VAL is uint𝑘_t w/ better encoding for immediates (constexpr)
* @return VAL
* @note alignas(𝑘) on nexgen32e only needed for end of page gotcha
* @note alignas(𝑘) is implied if compiler knows type
* @note needed to defeat store tearing optimizations
* @see Intel Six-Thousand Page Manual Manual V.3A §8.2.3.1
* @see atomic_load()
*/
#define atomic_store(MEM, VAL) \
({ \
autotype(VAL) Val = (VAL); \
typeof(&Val) Mem = (MEM); \
asm("mov%z1\t%1,%0" : "=m"(*Mem) : "r"(Val)); \
Val; \
})
#define bts(MEM, BIT) __BitOp("bts", BIT, MEM) /** bit test and set */
#define btr(MEM, BIT) __BitOp("btr", BIT, MEM) /** bit test and reset */
#define btc(MEM, BIT) __BitOp("btc", BIT, MEM) /** bit test and complement */
#define lockbts(MEM, BIT) __BitOp("lock bts", BIT, MEM)
#define lockbtr(MEM, BIT) __BitOp("lock btr", BIT, MEM)
#define lockbtc(MEM, BIT) __BitOp("lock btc", BIT, MEM)
#define lockinc(MEM) __ArithmeticOp1("lock inc", MEM) #define lockinc(MEM) __ArithmeticOp1("lock inc", MEM)
#define lockdec(MEM) __ArithmeticOp1("lock dec", MEM) #define lockdec(MEM) __ArithmeticOp1("lock dec", MEM)
#define locknot(MEM) __ArithmeticOp1("lock not", MEM) #define locknot(MEM) __ArithmeticOp1("lock not", MEM)
@ -225,66 +142,6 @@ unsigned bextra(const unsigned *, size_t, char);
#define lockandeq(MEM, VAL) __ArithmeticOp2("lock and", VAL, MEM) #define lockandeq(MEM, VAL) __ArithmeticOp2("lock and", VAL, MEM)
#define lockoreq(MEM, VAL) __ArithmeticOp2("lock or", VAL, MEM) #define lockoreq(MEM, VAL) __ArithmeticOp2("lock or", VAL, MEM)
/**
* Exchanges *MEMORY into *LOCALVAR w/ one operation.
*
* @param MEMORY is uint𝑘_t[hasatleast 1] where 𝑘 {8,16,32,64}
* @param LOCALVAR is uint𝑘_t[hasatleast 1]
* @return LOCALVAR[0]
* @see xchg()
*/
#define lockxchg(MEMORY, LOCALVAR) \
({ \
asm("xchg\t%0,%1" : "+%m"(*(MEMORY)), "+r"(*(LOCALVAR))); \
*(LOCALVAR); \
})
/**
* Compares and exchanges.
*
* @param IFTHING is uint𝑘_t[hasatleast 1] where 𝑘 {8,16,32,64}
* @return true if value was exchanged, otherwise false
* @see lockcmpxchg()
*/
#define cmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \
({ \
bool DidIt; \
autotype(IFTHING) IfThing = (IFTHING); \
typeof(*IfThing) IsEqualToMe = (ISEQUALTOME); \
typeof(*IfThing) ReplaceItWithMe = (REPLACEITWITHME); \
asm volatile(ZFLAG_ASM("cmpxchg\t%3,%1") \
: ZFLAG_CONSTRAINT(DidIt), "+m"(*IfThing), "+a"(IsEqualToMe) \
: "r"(ReplaceItWithMe) \
: "cc"); \
DidIt; \
})
/**
* Compares and exchanges w/ one operation.
*
* @param IFTHING is uint𝑘_t[hasatleast 1] where 𝑘 {8,16,32,64}
* @return true if value was exchanged, otherwise false
* @see lockcmpxchg()
*/
#define lockcmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \
({ \
bool DidIt; \
autotype(IFTHING) IfThing = (IFTHING); \
typeof(*IfThing) IsEqualToMe = (ISEQUALTOME); \
typeof(*IfThing) ReplaceItWithMe = (REPLACEITWITHME); \
asm volatile(ZFLAG_ASM("lock cmpxchg\t%3,%1") \
: ZFLAG_CONSTRAINT(DidIt), "+m"(*IfThing), "+a"(IsEqualToMe) \
: "r"(ReplaceItWithMe) \
: "cc"); \
DidIt; \
})
#define IsAddressCanonicalForm(P) \
({ \
intptr_t p2 = (intptr_t)(P); \
(0xffff800000000000l <= p2 && p2 <= 0x00007fffffffffffl); \
})
/*───────────────────────────────────────────────────────────────────────────│─╗ /*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § bits » implementation details cosmopolitan § bits » implementation details
*/ */
@ -301,44 +158,6 @@ unsigned bextra(const unsigned *, size_t, char);
MEM; \ MEM; \
}) })
#define __BitOp(OP, BIT, MEM) \
({ \
bool OldBit; \
if (__builtin_constant_p(BIT)) { \
asm(CFLAG_ASM(OP "%z1\t%2,%1") \
: CFLAG_CONSTRAINT(OldBit), \
"+m"((MEM)[(BIT) / (sizeof((MEM)[0]) * CHAR_BIT)]) \
: "J"((BIT) % (sizeof((MEM)[0]) * CHAR_BIT)) \
: "cc"); \
} else if (sizeof((MEM)[0]) == 2) { \
asm(CFLAG_ASM(OP "\t%w2,%1") \
: CFLAG_CONSTRAINT(OldBit), "+m"((MEM)[0]) \
: "r"(BIT) \
: "cc"); \
} else if (sizeof((MEM)[0]) == 4) { \
asm(CFLAG_ASM(OP "\t%k2,%1") \
: CFLAG_CONSTRAINT(OldBit), "+m"((MEM)[0]) \
: "r"(BIT) \
: "cc"); \
} else if (sizeof((MEM)[0]) == 8) { \
asm(CFLAG_ASM(OP "\t%q2,%1") \
: CFLAG_CONSTRAINT(OldBit), "+m"((MEM)[0]) \
: "r"(BIT) \
: "cc"); \
} \
OldBit; \
})
#else
#define cmpxchg(MEM, CMP, VAL) \
cmpxchg(MEM, (intptr_t)(CMP), (intptr_t)(VAL), sizeof(*(MEM)))
#define lockcmpxchg(MEM, CMP, VAL) \
lockcmpxchg(MEM, (intptr_t)(CMP), (intptr_t)(VAL), sizeof(*(MEM)))
#define lockxchg(MEM, VAR) \
lockxchg(MEM, VAR, sizeof(*(MEM)) / (sizeof(*(MEM)) == sizeof(*(VAR))))
#define atomic_store(MEM, VAL) \
atomic_store(MEM, VAL, sizeof(*(MEM)) / (sizeof(*(MEM)) == sizeof(*(VAL))))
#define atomic_load(MEM) atomic_load(MEM, sizeof(*(MEM)))
#endif /* __GNUC__ && !__STRICT_ANSI__ */ #endif /* __GNUC__ && !__STRICT_ANSI__ */
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -3,8 +3,11 @@
#include "libc/assert.h" #include "libc/assert.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && defined(__x86__)
/** /**
* Computes `(a + b) / 2` assuming unsigned. * Computes `(a + b) / 2` assuming unsigned.
*
* This implementation is the fastest on AMD Zen architecture.
*/ */
#define _midpoint(a, b) \ #define _midpoint(a, b) \
({ \ ({ \
@ -18,6 +21,12 @@
: "r"(b_)); \ : "r"(b_)); \
a_; \ a_; \
}) })
#else
/**
* Computes `(a + b) / 2` assuming unsigned.
*/
#define _midpoint(a, b) (((a) & (b)) + ((a) ^ (b)) / 2)
#endif /* __GNUC__ && !__STRICT_ANSI__ && x86 */
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_BITS_MIDPOINT_H_ */ #endif /* COSMOPOLITAN_LIBC_BITS_MIDPOINT_H_ */

View file

@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h" #include "libc/intrin/cmpxchg.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
@ -41,7 +41,7 @@ int atfork(void *fn, void *arg) {
for (;;) { for (;;) {
i = g_atfork.i; i = g_atfork.i;
if (i == ARRAYLEN(g_atfork.p)) return enomem(); if (i == ARRAYLEN(g_atfork.p)) return enomem();
if (cmpxchg(&g_atfork.i, i, i + 1)) { if (_cmpxchg(&g_atfork.i, i, i + 1)) {
g_atfork.p[i] = (struct AtForkCallback){.fn = fn, .arg = arg}; g_atfork.p[i] = (struct AtForkCallback){.fn = fn, .arg = arg};
return 0; return 0;
} }

View file

@ -123,6 +123,7 @@ int getdomainname(char *, size_t);
int gethostname(char *, size_t); int gethostname(char *, size_t);
int getpgid(int); int getpgid(int);
int getpid(void); int getpid(void);
int gettid(void);
int getppid(void); int getppid(void);
int getpriority(int, unsigned); int getpriority(int, unsigned);
int getrlimit(int, struct rlimit *); int getrlimit(int, struct rlimit *);
@ -227,7 +228,6 @@ uint32_t geteuid(void) nosideeffect;
uint32_t getgid(void) nosideeffect; uint32_t getgid(void) nosideeffect;
uint32_t getpgrp(void) nosideeffect; uint32_t getpgrp(void) nosideeffect;
uint32_t getsid(int) nosideeffect; uint32_t getsid(int) nosideeffect;
uint32_t gettid(void) nosideeffect;
uint32_t getuid(void) nosideeffect; uint32_t getuid(void) nosideeffect;
uint32_t umask(int32_t); uint32_t umask(int32_t);
void rewinddir(DIR *); void rewinddir(DIR *);

View file

@ -17,9 +17,9 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/assert.h" #include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/bits/weaken.h" #include "libc/bits/weaken.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
@ -40,7 +40,7 @@ int __ensurefds(int fd) {
if ((p2 = weaken(malloc)(n2 * sizeof(*p1)))) { if ((p2 = weaken(malloc)(n2 * sizeof(*p1)))) {
memcpy(p2, p1, n1 * sizeof(*p1)); memcpy(p2, p1, n1 * sizeof(*p1));
bzero(p2 + n1, (n2 - n1) * sizeof(*p1)); bzero(p2 + n1, (n2 - n1) * sizeof(*p1));
if (cmpxchg(&g_fds.p, p1, p2)) { if (_cmpxchg(&g_fds.p, p1, p2)) {
g_fds.n = n2; g_fds.n = n2;
if (weaken(free)) { if (weaken(free)) {
if (p1 == g_fds.__init_p) { if (p1 == g_fds.__init_p) {

View file

@ -16,10 +16,10 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/struct/flock.h" #include "libc/calls/struct/flock.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/accessmask.h"
#include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/enum/fileflagandattributes.h"
@ -45,8 +45,8 @@ static textwindows int sys_fcntl_nt_reservefd(int start) {
if (fd >= g_fds.n) { if (fd >= g_fds.n) {
if (__ensurefds(fd) == -1) return -1; if (__ensurefds(fd) == -1) return -1;
} }
cmpxchg(&g_fds.f, fd, fd + 1); _cmpxchg(&g_fds.f, fd, fd + 1);
if (cmpxchg(&g_fds.p[fd].kind, kFdEmpty, kFdReserved)) { if (_cmpxchg(&g_fds.p[fd].kind, kFdEmpty, kFdReserved)) {
return fd; return fd;
} }
} }

View file

@ -18,5 +18,6 @@
*/ */
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
cthread_spinlock_t __sig_lock;
unsigned __sighandrvas[NSIG]; unsigned __sighandrvas[NSIG];
unsigned __sighandflags[NSIG]; unsigned __sighandflags[NSIG];

View file

@ -15,6 +15,7 @@
#include "libc/calls/struct/winsize.h" #include "libc/calls/struct/winsize.h"
#include "libc/calls/ucontext.h" #include "libc/calls/ucontext.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/intrin/spinlock.h"
#include "libc/limits.h" #include "libc/limits.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/nt/struct/context.h" #include "libc/nt/struct/context.h"
@ -72,6 +73,7 @@ extern const struct Fd kEmptyFd;
hidden extern int __vforked; hidden extern int __vforked;
hidden extern bool __time_critical; hidden extern bool __time_critical;
hidden extern cthread_spinlock_t __sig_lock;
hidden extern unsigned __sighandrvas[NSIG]; hidden extern unsigned __sighandrvas[NSIG];
hidden extern unsigned __sighandflags[NSIG]; hidden extern unsigned __sighandflags[NSIG];
hidden extern struct Fds g_fds; hidden extern struct Fds g_fds;
@ -176,6 +178,7 @@ i32 sys_renameat(i32, const char *, i32, const char *) hidden;
i32 sys_sched_setaffinity(i32, u64, const void *) hidden; i32 sys_sched_setaffinity(i32, u64, const void *) hidden;
i32 sys_sched_yield(void) hidden; i32 sys_sched_yield(void) hidden;
i32 sys_setitimer(i32, const struct itimerval *, struct itimerval *) hidden; i32 sys_setitimer(i32, const struct itimerval *, struct itimerval *) hidden;
i32 sys_setpgid(i32, i32) hidden;
i32 sys_setpriority(i32, u32, i32) hidden; i32 sys_setpriority(i32, u32, i32) hidden;
i32 sys_setresgid(uint32_t, uint32_t, uint32_t) hidden; i32 sys_setresgid(uint32_t, uint32_t, uint32_t) hidden;
i32 sys_setresuid(uint32_t, uint32_t, uint32_t) hidden; i32 sys_setresuid(uint32_t, uint32_t, uint32_t) hidden;
@ -330,7 +333,8 @@ struct NtOverlapped *_offset2overlap(int64_t, struct NtOverlapped *) hidden;
unsigned __wincrash_nt(struct NtExceptionPointers *); unsigned __wincrash_nt(struct NtExceptionPointers *);
void *GetProcAddressModule(const char *, const char *) hidden; void *GetProcAddressModule(const char *, const char *) hidden;
void WinMainForked(void) hidden; void WinMainForked(void) hidden;
void ntcontext2linux(struct ucontext *, const struct NtContext *) hidden; void _ntcontext2linux(struct ucontext *, const struct NtContext *) hidden;
void _ntlinux2context(struct NtContext *, const ucontext_t *) hidden;
/*───────────────────────────────────────────────────────────────────────────│─╗ /*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § syscalls » metal cosmopolitan § syscalls » metal

View file

@ -17,10 +17,10 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/assert.h" #include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/bits/weaken.h" #include "libc/bits/weaken.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/nt/errors.h" #include "libc/nt/errors.h"
#include "libc/nt/iphlpapi.h" #include "libc/nt/iphlpapi.h"
#include "libc/nt/runtime.h" #include "libc/nt/runtime.h"
@ -275,7 +275,7 @@ static int createHostInfo(struct NtIpAdapterAddresses *firstAdapter) {
if (!node) goto err; if (!node) goto err;
if (!__hostInfo) { if (!__hostInfo) {
__hostInfo = node; __hostInfo = node;
if (cmpxchg(&once, false, true)) { if (_cmpxchg(&once, false, true)) {
atexit(freeHostInfo); atexit(freeHostInfo);
} }
} }

View file

@ -17,7 +17,7 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/getconsolectrlevent.h" #include "libc/calls/getconsolectrlevent.internal.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
@ -30,34 +30,60 @@
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
textwindows int sys_kill_nt(int pid, int sig) { textwindows int sys_kill_nt(int pid, int sig) {
bool ok; bool32 ok;
int64_t handle; int64_t handle;
int event, ntpid; int event, ntpid;
if (pid) {
pid = ABS(pid); // is killing everything except init really worth supporting?
if ((event = GetConsoleCtrlEvent(sig)) != -1) { if (pid == -1) return einval();
/* kill(pid, SIGINT|SIGQUIT) */
if (__isfdkind(pid, kFdProcess)) { // XXX: NT doesn't really have process groups. For instance the
ntpid = GetProcessId(g_fds.p[pid].handle); // CreateProcess() flag for starting a process group actually
} else if (!__isfdopen(pid)) { // just does an "ignore ctrl-c" internally.
/* XXX: this is sloppy (see fork-nt.c) */ pid = ABS(pid);
ntpid = pid;
} else { // If we're targeting current process group then just call raise().
return esrch(); if (!pid || pid == getpid()) {
}
ok = !!GenerateConsoleCtrlEvent(event, ntpid);
} else if (__isfdkind(pid, kFdProcess)) {
ok = !!TerminateProcess(g_fds.p[pid].handle, 128 + sig);
if (!ok && GetLastError() == kNtErrorAccessDenied) ok = true;
} else if ((handle = OpenProcess(kNtProcessTerminate, false, pid))) {
ok = !!TerminateProcess(handle, 128 + sig);
if (!ok && GetLastError() == kNtErrorAccessDenied) ok = true;
CloseHandle(handle);
} else {
ok = false;
}
return ok ? 0 : __winerr();
} else {
return raise(sig); return raise(sig);
} }
// GenerateConsoleCtrlEvent() will always signal groups and there's
// nothing we can do about it, unless we have a GUI GetMessage loop
// and alternatively create a centralized signal daemon like cygwin
if ((event = GetConsoleCtrlEvent(sig)) != -1) {
// we're killing with SIGINT or SIGQUIT which are the only two
// signals we can really use, since TerminateProcess() makes
// everything else effectively a SIGKILL ;_;
if (__isfdkind(pid, kFdProcess)) {
ntpid = GetProcessId(g_fds.p[pid].handle);
} else if (!__isfdopen(pid)) {
ntpid = pid; // XXX: suboptimal
} else {
return esrch();
}
if (GenerateConsoleCtrlEvent(event, ntpid)) {
return 0;
} else {
return -1;
}
}
// XXX: Is this a cosmo pid that was returned by fork_nt?
if (__isfdkind(pid, kFdProcess)) {
ok = TerminateProcess(g_fds.p[pid].handle, 128 + sig);
if (!ok && GetLastError() == kNtErrorAccessDenied) ok = true;
return 0;
}
// XXX: Is this a raw new technology pid? Because that's messy.
if ((handle = OpenProcess(kNtProcessTerminate, false, pid))) {
ok = TerminateProcess(handle, 128 + sig);
if (!ok && GetLastError() == kNtErrorAccessDenied) {
ok = true; // cargo culting other codebases here
}
CloseHandle(handle);
return 0;
} else {
return -1;
}
} }

View file

@ -18,7 +18,9 @@
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/str/str.h"
/** /**
* Sends signal to process. * Sends signal to process.
@ -38,9 +40,12 @@
* @asyncsignalsafe * @asyncsignalsafe
*/ */
int kill(int pid, int sig) { int kill(int pid, int sig) {
int rc;
if (!IsWindows()) { if (!IsWindows()) {
return sys_kill(pid, sig, 1); rc = sys_kill(pid, sig, 1);
} else { } else {
return sys_kill_nt(pid, sig); rc = sys_kill_nt(pid, sig);
} }
STRACE("kill(%d, %s) → %d% m", pid, strsignal(sig), rc);
return rc;
} }

View file

@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h" #include "libc/bits/asmflag.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h" #include "libc/calls/strace.internal.h"
#include "libc/dce.h" #include "libc/dce.h"

View file

@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h" #include "libc/bits/asmflag.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/strace.internal.h" #include "libc/calls/strace.internal.h"
#include "libc/sysv/consts/map.h" #include "libc/sysv/consts/map.h"

View file

@ -21,7 +21,7 @@
#include "libc/nt/struct/context.h" #include "libc/nt/struct/context.h"
#include "libc/str/str.h" #include "libc/str/str.h"
textwindows void ntcontext2linux(ucontext_t *ctx, const struct NtContext *cr) { textwindows void _ntcontext2linux(ucontext_t *ctx, const struct NtContext *cr) {
if (!cr) return; if (!cr) return;
ctx->uc_flags = cr->EFlags; ctx->uc_flags = cr->EFlags;
ctx->uc_mcontext.gregs[REG_EFL] = cr->EFlags; ctx->uc_mcontext.gregs[REG_EFL] = cr->EFlags;
@ -48,3 +48,30 @@ textwindows void ntcontext2linux(ucontext_t *ctx, const struct NtContext *cr) {
ctx->uc_mcontext.fpregs = &ctx->__fpustate; ctx->uc_mcontext.fpregs = &ctx->__fpustate;
memcpy(&ctx->__fpustate, &cr->FltSave, sizeof(ctx->__fpustate)); memcpy(&ctx->__fpustate, &cr->FltSave, sizeof(ctx->__fpustate));
} }
textwindows void _ntlinux2context(struct NtContext *cr, const ucontext_t *ctx) {
if (!cr) return;
cr->EFlags = ctx->uc_flags;
cr->EFlags = ctx->uc_mcontext.gregs[REG_EFL];
cr->Rax = ctx->uc_mcontext.rax;
cr->Rbx = ctx->uc_mcontext.rbx;
cr->Rcx = ctx->uc_mcontext.rcx;
cr->Rdx = ctx->uc_mcontext.rdx;
cr->Rdi = ctx->uc_mcontext.rdi;
cr->Rsi = ctx->uc_mcontext.rsi;
cr->Rbp = ctx->uc_mcontext.rbp;
cr->Rsp = ctx->uc_mcontext.rsp;
cr->Rip = ctx->uc_mcontext.rip;
cr->R8 = ctx->uc_mcontext.r8;
cr->R9 = ctx->uc_mcontext.r9;
cr->R10 = ctx->uc_mcontext.r10;
cr->R11 = ctx->uc_mcontext.r11;
cr->R12 = ctx->uc_mcontext.r12;
cr->R13 = ctx->uc_mcontext.r13;
cr->R14 = ctx->uc_mcontext.r14;
cr->R15 = ctx->uc_mcontext.r15;
cr->SegCs = ctx->uc_mcontext.cs;
cr->SegGs = ctx->uc_mcontext.gs;
cr->SegFs = ctx->uc_mcontext.fs;
memcpy(&cr->FltSave, &ctx->__fpustate, sizeof(ctx->__fpustate));
}

View file

@ -17,11 +17,14 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/sig.internal.h" #include "libc/calls/sig.internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/nexgen32e/nt2sysv.h"
#include "libc/nt/enum/ctrlevent.h" #include "libc/nt/enum/ctrlevent.h"
#include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/sig.h"
textwindows bool32 __onntconsoleevent(uint32_t dwCtrlType) { textwindows bool32 __onntconsoleevent(uint32_t dwCtrlType) {
STRACE("__onntconsoleevent(%u)", dwCtrlType);
switch (dwCtrlType) { switch (dwCtrlType) {
case kNtCtrlCEvent: case kNtCtrlCEvent:
__sig_add(SIGINT, SI_KERNEL); __sig_add(SIGINT, SI_KERNEL);

View file

@ -27,7 +27,7 @@
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
/** /**
* Waits for any signal. * Waits for signal.
* *
* This suspends execution until an unmasked signal is delivered and its * This suspends execution until an unmasked signal is delivered and its
* callback function has been called. The current signal mask is used. * callback function has been called. The current signal mask is used.

View file

@ -17,16 +17,25 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/getconsolectrlevent.h" #include "libc/calls/getconsolectrlevent.internal.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h" #include "libc/calls/sig.internal.h"
#include "libc/calls/strace.internal.h" #include "libc/calls/strace.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/nt/console.h" #include "libc/nt/console.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h" #include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/sig.h"
static textwindows inline bool HasWorkingConsole(void) {
return !!(__ntconsolemode[0] | __ntconsolemode[1] | __ntconsolemode[2]);
}
/** /**
* Sends signal to this process. * Sends signal to this process.
* *
@ -36,7 +45,7 @@
*/ */
int raise(int sig) { int raise(int sig) {
int rc, event; int rc, event;
STRACE("raise(%d) → [...]", sig); STRACE("raise(%s) → [...]", strsignal(sig));
if (sig == SIGTRAP) { if (sig == SIGTRAP) {
DebugBreak(); DebugBreak();
rc = 0; rc = 0;
@ -45,18 +54,27 @@ int raise(int sig) {
x = 1 / x; x = 1 / x;
rc = 0; rc = 0;
} else if (!IsWindows()) { } else if (!IsWindows()) {
// XXX: should be tkill() or tgkill() on linux
rc = sys_kill(getpid(), sig, 1); rc = sys_kill(getpid(), sig, 1);
} else { } else {
if ((event = GetConsoleCtrlEvent(sig)) != -1) { if (HasWorkingConsole() && (event = GetConsoleCtrlEvent(sig)) != -1) {
// XXX: MSDN says "If this parameter is zero, the signal is
// generated in all processes that share the console of the
// calling process." which seems to imply multiple process
// groups potentially. We just shouldn't use this because it
// doesn't make any sense and it's so evil.
if (GenerateConsoleCtrlEvent(event, 0)) { if (GenerateConsoleCtrlEvent(event, 0)) {
// XXX: we shouldn't need to sleep here ctrl-c is evil on nt
SleepEx(100, false);
__sig_check(false);
rc = 0; rc = 0;
} else { } else {
rc = __winerr(); rc = __winerr();
} }
} else { } else {
rc = __sig_add(sig, SI_USER); rc = __sig_raise(sig, SI_USER);
} }
} }
STRACE("[...] raise → %d% m", rc); STRACE("[...] raise(%s) → %d% m", strsignal(sig), rc);
return rc; return rc;
} }

View file

@ -16,8 +16,9 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/lockcmpxchg.h"
void __releasefd(int fd) { void __releasefd(int fd) {
int x; int x;
@ -26,6 +27,6 @@ void __releasefd(int fd) {
do { do {
x = g_fds.f; x = g_fds.f;
if (fd >= x) break; if (fd >= x) break;
} while (!cmpxchg(&g_fds.f, x, fd)); } while (!_lockcmpxchg(&g_fds.f, x, fd));
} }
} }

View file

@ -16,8 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
@ -31,8 +31,8 @@ int __reservefd(void) {
if (fd >= g_fds.n) { if (fd >= g_fds.n) {
if (__ensurefds(fd) == -1) return -1; if (__ensurefds(fd) == -1) return -1;
} }
cmpxchg(&g_fds.f, fd, fd + 1); _cmpxchg(&g_fds.f, fd, fd + 1);
if (cmpxchg(&g_fds.p[fd].kind, kFdEmpty, kFdReserved)) { if (_cmpxchg(&g_fds.p[fd].kind, kFdEmpty, kFdReserved)) {
return fd; return fd;
} }
} }

View file

@ -35,7 +35,7 @@ textwindows int __sample_pids(int pids[hasatleast 64],
bool exploratory) { bool exploratory) {
static uint64_t rando = 1; static uint64_t rando = 1;
uint32_t i, j, base, count; uint32_t i, j, base, count;
base = KnuthLinearCongruentialGenerator(&rando); base = KnuthLinearCongruentialGenerator(&rando) >> 32;
for (count = i = 0; i < g_fds.n; ++i) { for (count = i = 0; i < g_fds.n; ++i) {
j = (base + i) % g_fds.n; j = (base + i) % g_fds.n;
if (g_fds.p[j].kind == kFdProcess && (!exploratory || !g_fds.p[j].zombie)) { if (g_fds.p[j].kind == kFdProcess && (!exploratory || !g_fds.p[j].zombie)) {

58
libc/calls/setpgid.c Normal file
View file

@ -0,0 +1,58 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/nt/console.h"
#include "libc/sysv/errfuns.h"
/**
* Changes process group for process.
*/
int setpgid(int pid, int pgid) {
int rc, me;
if (!IsWindows()) {
rc = sys_setpgid(pid, pgid);
} else {
me = getpid();
if (pid == me && pgid == me) {
/*
* "When a process is created with CREATE_NEW_PROCESS_GROUP
* specified, an implicit call to SetConsoleCtrlHandler(NULL,TRUE)
* is made on behalf of the new process; this means that the new
* process has CTRL+C disabled. This lets shells handle CTRL+C
* themselves, and selectively pass that signal on to
* sub-processes. CTRL+BREAK is not disabled, and may be used to
* interrupt the process/process group."
* Quoth MSDN § CreateProcessW()
*/
if (SetConsoleCtrlHandler(0, 1)) {
rc = 0;
} else {
rc = __winerr();
}
} else {
// irregular use cases not supported on windows
rc = einval();
}
}
STRACE("setpgid(%d, %d) → %d% m", pid, pgid, rc);
return rc;
}

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h" #include "libc/calls/sig.internal.h"
@ -24,6 +23,9 @@
#include "libc/calls/strace.internal.h" #include "libc/calls/strace.internal.h"
#include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.h"
#include "libc/calls/typedef/sigaction_f.h" #include "libc/calls/typedef/sigaction_f.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/lockcmpxchg.h"
#include "libc/intrin/spinlock.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/runtime/internal.h" #include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
@ -37,18 +39,6 @@
* @threadsafe * @threadsafe
*/ */
#define LOCK \
for (;;) \
if (lockcmpxchg(&__sig.lock, false, true)) { \
asm volatile("" ::: "memory")
#define UNLOCK \
asm volatile("" ::: "memory"); \
__sig.lock = false; \
break; \
} \
else sched_yield()
struct Signal { struct Signal {
struct Signal *next; struct Signal *next;
bool used; bool used;
@ -57,7 +47,6 @@ struct Signal {
}; };
struct Signals { struct Signals {
bool lock;
sigset_t mask; sigset_t mask;
struct Signal *queue; struct Signal *queue;
struct Signal mem[__SIG_QUEUE_LENGTH]; struct Signal mem[__SIG_QUEUE_LENGTH];
@ -67,15 +56,19 @@ struct Signals __sig; // TODO(jart): Need TLS
/** /**
* Allocates piece of memory for storing pending signal. * Allocates piece of memory for storing pending signal.
* @assume lock is held
*/ */
static textwindows struct Signal *__sig_alloc(void) { static textwindows struct Signal *__sig_alloc(void) {
int i; int i;
struct Signal *res = 0;
for (i = 0; i < ARRAYLEN(__sig.mem); ++i) { for (i = 0; i < ARRAYLEN(__sig.mem); ++i) {
if (!__sig.mem[i].used && lockcmpxchg(&__sig.mem[i].used, false, true)) { if (!__sig.mem[i].used) {
return __sig.mem + i; __sig.mem[i].used = true;
res = __sig.mem + i;
break;
} }
} }
return 0; return res;
} }
/** /**
@ -92,7 +85,7 @@ static textwindows void __sig_free(struct Signal *mem) {
static textwindows struct Signal *__sig_remove(void) { static textwindows struct Signal *__sig_remove(void) {
struct Signal *prev, *res; struct Signal *prev, *res;
if (__sig.queue) { if (__sig.queue) {
LOCK; cthread_spinlock(&__sig_lock);
for (prev = 0, res = __sig.queue; res; prev = res, res = res->next) { for (prev = 0, res = __sig.queue; res; prev = res, res = res->next) {
if (!sigismember(&__sig.mask, res->sig)) { if (!sigismember(&__sig.mask, res->sig)) {
if (res == __sig.queue) { if (res == __sig.queue) {
@ -106,7 +99,7 @@ static textwindows struct Signal *__sig_remove(void) {
STRACE("%s is masked", strsignal(res->sig)); STRACE("%s is masked", strsignal(res->sig));
} }
} }
UNLOCK; cthread_spunlock(&__sig_lock);
} else { } else {
res = 0; res = 0;
} }
@ -114,28 +107,60 @@ static textwindows struct Signal *__sig_remove(void) {
} }
/** /**
* Delivers signal. * Delivers signal to callback.
* @note called from main thread * @note called from main thread
* @return true if EINTR should be returned by caller * @return true if EINTR should be returned by caller
*/ */
static textwindows bool __sig_deliver(bool restartable, struct Signal *sig, static textwindows bool __sig_deliver(bool restartable, int sig, int si_code,
unsigned rva) { ucontext_t *ctx) {
unsigned flags; unsigned rva, flags;
siginfo_t info; siginfo_t info, *infop;
flags = __sighandflags[sig->sig]; STRACE("delivering %s", strsignal(sig));
// TODO(jart): polyfill prevention of re-entry
if (flags & SA_RESETHAND) { // enter the signal
__sighandrvas[sig->sig] = (int32_t)(intptr_t)SIG_DFL; cthread_spinlock(&__sig_lock);
rva = __sighandrvas[sig];
flags = __sighandflags[sig];
if (~flags & SA_NODEFER) {
// by default we try to avoid reentering a signal handler. for
// example, if a sigsegv handler segfaults, then we'd want the
// second signal to just kill the process. doing this means we
// track state. that's bad if you want to longjmp() out of the
// signal handler. in that case you must use SA_NODEFER.
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
} }
STRACE("delivering %s", strsignal(sig->sig)); cthread_spunlock(&__sig_lock);
bzero(&info, sizeof(info));
info.si_signo = sig->sig; // setup the somewhat expensive information args
info.si_code = sig->si_code; // only if they're requested by the user in sigaction()
((sigaction_f)(_base + rva))(sig->sig, &info, 0); if (flags & SA_SIGINFO) {
bzero(&info, sizeof(info));
info.si_signo = sig;
info.si_code = si_code;
infop = &info;
} else {
infop = 0;
ctx = 0;
}
// handover control to user
((sigaction_f)(_base + rva))(sig, infop, ctx);
// leave the signal
cthread_spinlock(&__sig_lock);
if (~flags & SA_NODEFER) {
_cmpxchg(__sighandrvas + sig, (int32_t)(intptr_t)SIG_DFL, rva);
}
if (flags & SA_RESETHAND) {
STRACE("resetting oneshot signal handler");
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
}
cthread_spunlock(&__sig_lock);
if (!restartable) { if (!restartable) {
return true; // always send EINTR for wait4(), poll(), etc. return true; // always send EINTR for wait4(), poll(), etc.
} else if (flags & SA_RESTART) { } else if (flags & SA_RESTART) {
STRACE("restarting syscall on %s", strsignal(sig->sig)); STRACE("restarting syscall on %s", strsignal(sig));
return false; // resume syscall for read(), write(), etc. return false; // resume syscall for read(), write(), etc.
} else { } else {
return true; // default course is to raise EINTR return true; // default course is to raise EINTR
@ -149,27 +174,86 @@ static textwindows bool __sig_isfatal(int sig) {
return sig != SIGCHLD; return sig != SIGCHLD;
} }
/**
* Handles signal.
*
* @param restartable can be used to suppress true return if SA_RESTART
* @return true if signal was delivered
*/
textwindows bool __sig_handle(bool restartable, int sig, int si_code,
ucontext_t *ctx) {
bool delivered;
switch (__sighandrvas[sig]) {
case (intptr_t)SIG_DFL:
if (__sig_isfatal(sig)) {
STRACE("terminating on %s", strsignal(sig));
__restorewintty();
_Exit(128 + sig);
}
// fallthrough
case (intptr_t)SIG_IGN:
STRACE("ignoring %s", strsignal(sig));
delivered = false;
break;
default:
delivered = __sig_deliver(restartable, sig, si_code, ctx);
break;
}
return delivered;
}
/**
* Handles signal immediately if not blocked.
*
* @param restartable is for functions like read() but not poll()
* @return true if EINTR should be returned by caller
* @return 1 if delivered, 0 if enqueued, otherwise -1 w/ errno
* @note called from main thread
* @threadsafe
*/
textwindows int __sig_raise(int sig, int si_code) {
int rc;
int candeliver;
cthread_spinlock(&__sig_lock);
candeliver = !sigismember(&__sig.mask, sig);
cthread_spunlock(&__sig_lock);
switch (candeliver) {
case 1:
__sig_handle(false, sig, si_code, 0);
return 0;
case 0:
STRACE("%s is masked", strsignal(sig));
return __sig_add(sig, si_code);
default:
return -1; // sigismember() validates `sig`
}
}
/** /**
* Enqueues generic signal for delivery on New Technology. * Enqueues generic signal for delivery on New Technology.
* @return 0 if enqueued, otherwise -1 w/ errno
* @threadsafe * @threadsafe
*/ */
textwindows int __sig_add(int sig, int si_code) { textwindows int __sig_add(int sig, int si_code) {
int rc;
struct Signal *mem; struct Signal *mem;
if (1 <= sig && sig <= NSIG) { if (1 <= sig && sig <= NSIG) {
STRACE("enqueuing %s", strsignal(sig));
cthread_spinlock(&__sig_lock);
if ((mem = __sig_alloc())) { if ((mem = __sig_alloc())) {
mem->sig = sig; mem->sig = sig;
mem->si_code = si_code; mem->si_code = si_code;
LOCK;
mem->next = __sig.queue; mem->next = __sig.queue;
__sig.queue = mem; __sig.queue = mem;
UNLOCK; rc = 0;
return 0;
} else { } else {
return enomem(); rc = enomem();
} }
cthread_spunlock(&__sig_lock);
} else { } else {
return einval(); rc = einval();
} }
return rc;
} }
/** /**
@ -186,21 +270,7 @@ textwindows bool __sig_check(bool restartable) {
struct Signal *sig; struct Signal *sig;
delivered = false; delivered = false;
while ((sig = __sig_remove())) { while ((sig = __sig_remove())) {
switch ((rva = __sighandrvas[sig->sig])) { delivered |= __sig_handle(restartable, sig->sig, sig->si_code, 0);
case (intptr_t)SIG_DFL:
if (__sig_isfatal(sig->sig)) {
STRACE("terminating on %s", strsignal(sig->sig));
__restorewintty();
_Exit(128 + sig->sig);
}
// fallthrough
case (intptr_t)SIG_IGN:
STRACE("ignoring %s", strsignal(sig->sig));
break;
default:
delivered = __sig_deliver(restartable, sig, rva);
break;
}
__sig_free(sig); __sig_free(sig);
} }
return delivered; return delivered;
@ -208,13 +278,31 @@ textwindows bool __sig_check(bool restartable) {
/** /**
* Changes signal mask for main thread. * Changes signal mask for main thread.
* @return old mask * @return 0 on success, or -1 w/ errno
*/ */
textwindows sigset_t __sig_mask(const sigset_t *neu) { textwindows int __sig_mask(int how, const sigset_t *neu, sigset_t *old) {
sigset_t old; int i;
LOCK; uint64_t a, b;
old = __sig.mask; if (how == SIG_BLOCK || how == SIG_UNBLOCK || how == SIG_SETMASK) {
if (neu) __sig.mask = *neu; cthread_spinlock(&__sig_lock);
UNLOCK; if (old) {
return old; *old = __sig.mask;
}
if (neu) {
for (i = 0; i < ARRAYLEN(__sig.mask.__bits); ++i) {
if (how == SIG_BLOCK) {
__sig.mask.__bits[i] |= neu->__bits[i];
} else if (how == SIG_UNBLOCK) {
__sig.mask.__bits[i] &= ~neu->__bits[i];
} else {
__sig.mask.__bits[i] = neu->__bits[i];
}
}
__sig.mask.__bits[0] &= ~(SIGKILL | SIGSTOP);
}
cthread_spunlock(&__sig_lock);
return 0;
} else {
return einval();
}
} }

View file

@ -1,16 +1,20 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_ #ifndef COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_ #define COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_
#include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.h"
#include "libc/calls/ucontext.h"
#define __SIG_QUEUE_LENGTH 8 #define __SIG_QUEUE_LENGTH 8
#define __SIG_POLLING_INTERVAL_MS 50 #define __SIG_POLLING_INTERVAL_MS 50
#define __SIG_LOGGING_INTERVAL_MS 1700
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
bool __sig_check(bool) hidden; bool __sig_check(bool) hidden;
bool __sig_handle(bool, int, int, ucontext_t *) hidden;
int __sig_add(int, int) hidden; int __sig_add(int, int) hidden;
sigset_t __sig_mask(const sigset_t *) hidden; int __sig_mask(int, const sigset_t *, sigset_t *) hidden;
int __sig_raise(int, int) hidden;
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -32,6 +32,7 @@
#include "libc/calls/ucontext.h" #include "libc/calls/ucontext.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/intrin/asan.internal.h" #include "libc/intrin/asan.internal.h"
#include "libc/intrin/spinlock.h"
#include "libc/limits.h" #include "libc/limits.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
@ -212,6 +213,7 @@ static int __sigaction(int sig, const struct sigaction *act,
rc = 0; rc = 0;
} }
if (rc != -1 && !__vforked) { if (rc != -1 && !__vforked) {
cthread_spinlock(&__sig_lock);
if (oldact) { if (oldact) {
oldrva = __sighandrvas[sig]; oldrva = __sighandrvas[sig];
oldact->sa_sigaction = (sigaction_f)( oldact->sa_sigaction = (sigaction_f)(
@ -221,6 +223,7 @@ static int __sigaction(int sig, const struct sigaction *act,
__sighandrvas[sig] = rva; __sighandrvas[sig] = rva;
__sighandflags[sig] = act->sa_flags; __sighandflags[sig] = act->sa_flags;
} }
cthread_spunlock(&__sig_lock);
} }
return rc; return rc;
} }
@ -233,6 +236,58 @@ static int __sigaction(int sig, const struct sigaction *act,
* .sa_flags = SA_RESETHAND|SA_RESTART|SA_SIGINFO}; * .sa_flags = SA_RESETHAND|SA_RESTART|SA_SIGINFO};
* CHECK_NE(-1, sigaction(SIGINT, &sa, NULL)); * CHECK_NE(-1, sigaction(SIGINT, &sa, NULL));
* *
* Here's an example of the most professional way to handle signals.
* It's generally a best practice to have signal handlers do the fewest
* number of things possible. The trick is to have your signals work
* hand-in-glove with the EINTR errno returned by i/o.
*
* static volatile bool gotctrlc;
* void OnCtrlC(int sig) {
* gotctrlc = true;
* }
* int main() {
* size_t got;
* ssize_t rc;
* char buf[1];
* struct sigaction oldint;
* struct sigaction saint = {.sa_handler = GotCtrlC};
* if (sigaction(SIGINT, &saint, &oldint) == -1) {
* perror("sigaction");
* exit(1);
* }
* for (;;) {
* rc = read(0, buf, sizeof(buf));
* if (rc == -1) {
* if (errno == EINTR) {
* if (gotctrlc) {
* break;
* }
* } else {
* perror("read");
* exit(2);
* }
* }
* if (!(got = rc)) {
* break;
* }
* for (;;) {
* rc = write(1, buf, got);
* if (rc != -1) {
* assert(rc == 1);
* break;
* } else if (errno != EINTR) {
* perror("write");
* exit(3);
* }
* }
* }
* sigaction(SIGINT, &oldint, 0);
* }
*
* Please note that you can't do the above if you use SA_RESTART. Since
* the purpose of SA_RESTART is to restart i/o operations whose docs say
* that they're @restartable and read() is one such function.
*
* @return 0 on success or -1 w/ errno * @return 0 on success or -1 w/ errno
* @see xsigaction() for a much better api * @see xsigaction() for a much better api
* @asyncsignalsafe * @asyncsignalsafe
@ -241,7 +296,11 @@ static int __sigaction(int sig, const struct sigaction *act,
int(sigaction)(int sig, const struct sigaction *act, struct sigaction *oldact) { int(sigaction)(int sig, const struct sigaction *act, struct sigaction *oldact) {
int rc; int rc;
char buf[2][128]; char buf[2][128];
rc = __sigaction(sig, act, oldact); if (sig == SIGKILL || sig == SIGSTOP) {
rc = einval();
} else {
rc = __sigaction(sig, act, oldact);
}
STRACE("sigaction(%s, %s, [%s]) → %d% m", strsignal(sig), STRACE("sigaction(%s, %s, [%s]) → %d% m", strsignal(sig),
__strace_sigaction(buf[0], sizeof(buf[0]), 0, act), __strace_sigaction(buf[0], sizeof(buf[0]), 0, act),
__strace_sigaction(buf[1], sizeof(buf[1]), rc, oldact), rc); __strace_sigaction(buf[1], sizeof(buf[1]), rc, oldact), rc);

View file

@ -20,104 +20,12 @@
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/struct/sigaction-freebsd.internal.h" #include "libc/calls/struct/sigaction-freebsd.internal.h"
#include "libc/calls/struct/siginfo.h" #include "libc/calls/struct/siginfo.h"
#include "libc/calls/struct/ucontext-freebsd.internal.h"
#include "libc/calls/typedef/sigaction_f.h" #include "libc/calls/typedef/sigaction_f.h"
#include "libc/calls/ucontext.h" #include "libc/calls/ucontext.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/str/str.h" #include "libc/str/str.h"
union sigval_freebsd {
int32_t sival_int;
void *sival_ptr;
int32_t sigval_int;
void *sigval_ptr;
};
struct siginfo_freebsd {
int32_t si_signo;
int32_t si_errno;
int32_t si_code;
int32_t si_pid;
uint32_t si_uid;
int32_t si_status;
void *si_addr;
union sigval_freebsd si_value;
union {
struct {
int32_t _trapno;
} _fault;
struct {
int32_t _timerid;
int32_t _overrun;
} _timer;
struct {
int32_t _mqd;
} _mesgq;
struct {
int64_t _band;
} _poll;
struct {
int64_t __spare1__;
int32_t __spare2__[7];
} __spare__;
} _reason;
};
struct stack_freebsd {
void *ss_sp;
uint64_t ss_size;
int32_t ss_flags;
};
struct mcontext_freebsd {
int64_t mc_onstack;
int64_t mc_rdi;
int64_t mc_rsi;
int64_t mc_rdx;
int64_t mc_rcx;
int64_t mc_r8;
int64_t mc_r9;
int64_t mc_rax;
int64_t mc_rbx;
int64_t mc_rbp;
int64_t mc_r10;
int64_t mc_r11;
int64_t mc_r12;
int64_t mc_r13;
int64_t mc_r14;
int64_t mc_r15;
uint32_t mc_trapno;
uint16_t mc_fs;
uint16_t mc_gs;
int64_t mc_addr;
uint32_t mc_flags;
uint16_t mc_es;
uint16_t mc_ds;
int64_t mc_err;
int64_t mc_rip;
int64_t mc_cs;
int64_t mc_rflags;
int64_t mc_rsp;
int64_t mc_ss;
int64_t mc_len;
int64_t mc_fpformat;
int64_t mc_ownedfp;
int64_t mc_fpstate[64];
int64_t mc_fsbase;
int64_t mc_gsbase;
int64_t mc_xfpustate;
int64_t mc_xfpustate_len;
int64_t mc_spare[4];
};
struct ucontext_freebsd {
struct sigset_freebsd uc_sigmask;
struct mcontext_freebsd uc_mcontext;
struct ucontext_freebsd *uc_link;
struct stack_freebsd uc_stack;
int32_t uc_flags;
int32_t __spare__[4];
};
void __sigenter_freebsd(int sig, struct siginfo_freebsd *si, void __sigenter_freebsd(int sig, struct siginfo_freebsd *si,
struct ucontext_freebsd *ctx) { struct ucontext_freebsd *ctx) {
int rva; int rva;

View file

@ -21,12 +21,20 @@
#include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sa.h"
/** /**
* Installs kernel interrupt handler. * Installs kernel interrupt handler, e.g.
* *
* void GotCtrlC(int sig) { ... }
* CHECK_NE(SIG_ERR, signal(SIGINT, GotCtrlC));
*
* @return old signal handler on success or SIG_ERR w/ errno
* @note this function has BSD semantics, i.e. SA_RESTART
* @see sigaction() which has more features * @see sigaction() which has more features
*/ */
sighandler_t(signal)(int sig, sighandler_t func) { sighandler_t(signal)(int sig, sighandler_t func) {
struct sigaction sa_old, sa = {.sa_handler = func, .sa_flags = SA_RESTART}; struct sigaction old, sa = {.sa_handler = func, .sa_flags = SA_RESTART};
if ((sigaction)(sig, &sa, &sa_old) == -1) return SIG_ERR; if ((sigaction)(sig, &sa, &old) != -1) {
return sa_old.sa_handler; return old.sa_handler;
} else {
return SIG_ERR;
}
} }

View file

@ -62,6 +62,8 @@ int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) {
char buf[2][41]; char buf[2][41];
int res, rc, arg1; int res, rc, arg1;
const sigset_t *arg2; const sigset_t *arg2;
STRACE("sigprocmask(%s, %s, [...]", DescribeHow(howbuf, how),
__strace_sigset(buf[0], sizeof(buf[0]), 0, opt_set));
sigemptyset(&old); sigemptyset(&old);
if (IsAsan() && if (IsAsan() &&
((opt_set && !__asan_is_valid(opt_set, sizeof(*opt_set))) || ((opt_set && !__asan_is_valid(opt_set, sizeof(*opt_set))) ||
@ -86,14 +88,13 @@ int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) {
rc = -1; rc = -1;
} }
} else { // windows or metal } else { // windows or metal
old = __sig_mask(opt_set); rc = __sig_mask(how, opt_set, &old);
_check_interrupts(false, 0); _check_interrupts(false, 0);
rc = 0;
} }
if (rc != -1 && opt_out_oldset) { if (rc != -1 && opt_out_oldset) {
*opt_out_oldset = old; *opt_out_oldset = old;
} }
STRACE("sigprocmask(%s, %s, [%s]) → %d% m", DescribeHow(howbuf, how), STRACE("[...] sigprocmask(%s, %s, [%s]) → %d% m", DescribeHow(howbuf, how),
__strace_sigset(buf[0], sizeof(buf[0]), 0, opt_set), __strace_sigset(buf[0], sizeof(buf[0]), 0, opt_set),
__strace_sigset(buf[1], sizeof(buf[1]), rc, opt_out_oldset), rc); __strace_sigset(buf[1], sizeof(buf[1]), rc, opt_out_oldset), rc);
return rc; return rc;

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h" #include "libc/calls/sig.internal.h"
@ -24,20 +25,23 @@
#include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/intrin/asan.internal.h" #include "libc/intrin/asan.internal.h"
#include "libc/log/backtrace.internal.h"
#include "libc/nt/synchronization.h" #include "libc/nt/synchronization.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
/** /**
* Blocks until SIG MASK is delivered to process. * Blocks until SIG MASK is delivered to process.
* *
* @param ignore is a bitset of signals to block temporarily * @param ignore is a bitset of signals to block temporarily, which if
* @return -1 w/ EINTR or possibly EFAULT * NULL is equivalent to passing an empty signal set
* @return -1 w/ EINTR (or possibly EFAULT)
* @asyncsignalsafe * @asyncsignalsafe
* @norestart * @norestart
*/ */
int sigsuspend(const sigset_t *ignore) { int sigsuspend(const sigset_t *ignore) {
int rc; int rc;
char buf[41]; char buf[41];
long ms, totoms;
sigset_t save, mask, *arg; sigset_t save, mask, *arg;
STRACE("sigsuspend(%s) → [...]", STRACE("sigsuspend(%s) → [...]",
__strace_sigset(buf, sizeof(buf), 0, ignore)); __strace_sigset(buf, sizeof(buf), 0, ignore));
@ -62,15 +66,24 @@ int sigsuspend(const sigset_t *ignore) {
if (!IsWindows()) { if (!IsWindows()) {
rc = sys_sigsuspend(arg, 8); rc = sys_sigsuspend(arg, 8);
} else { } else {
save = __sig_mask(arg); __sig_mask(SIG_SETMASK, arg, &save);
ms = 0;
totoms = 0;
do { do {
if (_check_interrupts(false, g_fds.p)) { if (_check_interrupts(false, g_fds.p)) {
rc = eintr(); rc = eintr();
break; break;
} }
SleepEx(__SIG_POLLING_INTERVAL_MS, true); SleepEx(__SIG_POLLING_INTERVAL_MS, true);
#ifdef SYSDEBUG
ms += __SIG_POLLING_INTERVAL_MS;
if (ms >= __SIG_LOGGING_INTERVAL_MS) {
totoms += ms, ms = 0;
STRACE("[...] sigsuspending for %'lums...", totoms);
}
#endif
} while (1); } while (1);
__sig_mask(&save); __sig_mask(SIG_SETMASK, &save, 0);
} }
} else { } else {
// TODO(jart): sigsuspend metal support // TODO(jart): sigsuspend metal support

View file

@ -3,6 +3,7 @@
#include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.h"
#include "libc/calls/typedef/sigaction_f.h" #include "libc/calls/typedef/sigaction_f.h"
#include "libc/calls/typedef/sighandler_t.h" #include "libc/calls/typedef/sighandler_t.h"
#include "libc/dce.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
struct sigaction { /* cosmo abi */ struct sigaction { /* cosmo abi */

View file

@ -0,0 +1,102 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_FREEBSD_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_FREEBSD_INTERNAL_H_
#include "libc/calls/struct/sigaction-freebsd.internal.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
union sigval_freebsd {
int32_t sival_int;
void *sival_ptr;
int32_t sigval_int;
void *sigval_ptr;
};
struct siginfo_freebsd {
int32_t si_signo;
int32_t si_errno;
int32_t si_code;
int32_t si_pid;
uint32_t si_uid;
int32_t si_status;
void *si_addr;
union sigval_freebsd si_value;
union {
struct {
int32_t _trapno;
} _fault;
struct {
int32_t _timerid;
int32_t _overrun;
} _timer;
struct {
int32_t _mqd;
} _mesgq;
struct {
int64_t _band;
} _poll;
struct {
int64_t __spare1__;
int32_t __spare2__[7];
} __spare__;
} _reason;
};
struct stack_freebsd {
void *ss_sp;
uint64_t ss_size;
int32_t ss_flags;
};
struct mcontext_freebsd {
int64_t mc_onstack;
int64_t mc_rdi;
int64_t mc_rsi;
int64_t mc_rdx;
int64_t mc_rcx;
int64_t mc_r8;
int64_t mc_r9;
int64_t mc_rax;
int64_t mc_rbx;
int64_t mc_rbp;
int64_t mc_r10;
int64_t mc_r11;
int64_t mc_r12;
int64_t mc_r13;
int64_t mc_r14;
int64_t mc_r15;
uint32_t mc_trapno;
uint16_t mc_fs;
uint16_t mc_gs;
int64_t mc_addr;
uint32_t mc_flags;
uint16_t mc_es;
uint16_t mc_ds;
int64_t mc_err;
int64_t mc_rip;
int64_t mc_cs;
int64_t mc_rflags;
int64_t mc_rsp;
int64_t mc_ss;
int64_t mc_len;
int64_t mc_fpformat;
int64_t mc_ownedfp;
int64_t mc_fpstate[64];
int64_t mc_fsbase;
int64_t mc_gsbase;
int64_t mc_xfpustate;
int64_t mc_xfpustate_len;
int64_t mc_spare[4];
};
struct ucontext_freebsd {
struct sigset_freebsd uc_sigmask;
struct mcontext_freebsd uc_mcontext;
struct ucontext_freebsd *uc_link;
struct stack_freebsd uc_stack;
int32_t uc_flags;
int32_t __spare__[4];
};
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_FREEBSD_INTERNAL_H_ */

View file

@ -16,8 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/intrin/lockxchg.h"
/** /**
* Deletes file. * Deletes file.

View file

@ -19,6 +19,7 @@
#include "libc/assert.h" #include "libc/assert.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/sigbits.h" #include "libc/calls/sigbits.h"
#include "libc/calls/strace.internal.h" #include "libc/calls/strace.internal.h"
#include "libc/calls/struct/rusage.h" #include "libc/calls/struct/rusage.h"
@ -40,11 +41,13 @@
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/w.h" #include "libc/sysv/consts/w.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options, static textwindows int sys_wait4_nt_impl(int pid, int *opt_out_wstatus,
struct rusage *opt_out_rusage) { int options,
struct rusage *opt_out_rusage) {
int pids[64]; int pids[64];
int64_t handle; int64_t handle;
int64_t handles[64]; int64_t handles[64];
@ -129,3 +132,15 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
return pids[i]; return pids[i];
} }
} }
textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
struct rusage *opt_out_rusage) {
int rc;
sigset_t mask, oldmask;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
__sig_mask(SIG_BLOCK, &mask, &oldmask);
rc = sys_wait4_nt_impl(pid, opt_out_wstatus, options, opt_out_rusage);
__sig_mask(SIG_SETMASK, &oldmask, 0);
return rc;
}

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/strace.internal.h" #include "libc/calls/strace.internal.h"
#include "libc/calls/typedef/sigaction_f.h" #include "libc/calls/typedef/sigaction_f.h"
#include "libc/calls/ucontext.h" #include "libc/calls/ucontext.h"
@ -24,14 +25,14 @@
#include "libc/nt/enum/signal.h" #include "libc/nt/enum/signal.h"
#include "libc/nt/struct/ntexceptionpointers.h" #include "libc/nt/struct/ntexceptionpointers.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
textwindows unsigned __wincrash(struct NtExceptionPointers *ep) { textwindows unsigned __wincrash(struct NtExceptionPointers *ep) {
int sig, rva, code; int64_t rip;
struct Goodies { int sig, code;
ucontext_t ctx; ucontext_t ctx;
struct siginfo si;
} g;
STRACE("__wincrash"); STRACE("__wincrash");
switch (ep->ExceptionRecord->ExceptionCode) { switch (ep->ExceptionRecord->ExceptionCode) {
case kNtSignalBreakpoint: case kNtSignalBreakpoint:
@ -96,12 +97,22 @@ textwindows unsigned __wincrash(struct NtExceptionPointers *ep) {
default: default:
return kNtExceptionContinueSearch; return kNtExceptionContinueSearch;
} }
bzero(&g, sizeof(g)); rip = ep->ContextRecord->Rip;
g.si.si_code = code;
rva = __sighandrvas[sig]; if (__sighandflags[sig] & SA_SIGINFO) {
if (rva >= kSigactionMinRva) { _ntcontext2linux(&ctx, ep->ContextRecord);
ntcontext2linux(&g.ctx, ep->ContextRecord); __sig_handle(false, sig, code, &ctx);
((sigaction_f)(_base + rva))(sig, &g.si, &g.ctx); _ntlinux2context(ep->ContextRecord, &ctx);
} else {
__sig_handle(false, sig, code, 0);
} }
// Windows seems to be the only operating system that traps INT3 at
// addressof(INT3) rather than addressof(INT3)+1. So we must adjust
// RIP to prevent the same INT3 from being trapped forevermore.
if (sig == SIGTRAP && rip == ep->ContextRecord->Rip) {
ep->ContextRecord->Rip++;
}
return kNtExceptionContinueExecution; return kNtExceptionContinueExecution;
} }

View file

@ -18,6 +18,7 @@
*/ */
#include "libc/assert.h" #include "libc/assert.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/strace.internal.h" #include "libc/calls/strace.internal.h"
#include "libc/calls/struct/iovec.h" #include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/siginfo.h" #include "libc/calls/struct/siginfo.h"
@ -29,22 +30,10 @@
#include "libc/nt/struct/overlapped.h" #include "libc/nt/struct/overlapped.h"
#include "libc/runtime/internal.h" #include "libc/runtime/internal.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
static textwindows ssize_t sys_write_nt_epipe(int fd) {
siginfo_t info;
STRACE("WriteFile(%d:%p) → %m", fd, g_fds.p[fd].handle);
if (!__sighandrvas[SIGPIPE]) {
__restorewintty();
_Exit(128 + SIGPIPE);
} else if (__sighandrvas[SIGPIPE] >= kSigactionMinRva) {
bzero(&info, sizeof(info));
((sigaction_f)(_base + __sighandrvas[SIGPIPE]))(SIGPIPE, &info, 0);
}
return epipe();
}
static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size, static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size,
ssize_t offset) { ssize_t offset) {
uint32_t sent; uint32_t sent;
@ -53,7 +42,8 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size,
_offset2overlap(offset, &overlap))) { _offset2overlap(offset, &overlap))) {
return sent; return sent;
} else if (GetLastError() == kNtErrorBrokenPipe) { } else if (GetLastError() == kNtErrorBrokenPipe) {
return sys_write_nt_epipe(fd); __sig_raise(SIGPIPE, SI_KERNEL);
return epipe();
} else { } else {
return __winerr(); return __winerr();
} }

View file

@ -18,7 +18,6 @@
*/ */
#include "libc/alg/reverse.internal.h" #include "libc/alg/reverse.internal.h"
#include "libc/assert.h" #include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/bits/likely.h" #include "libc/bits/likely.h"
#include "libc/bits/weaken.h" #include "libc/bits/weaken.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
@ -29,6 +28,7 @@
#include "libc/intrin/asan.internal.h" #include "libc/intrin/asan.internal.h"
#include "libc/intrin/asancodes.h" #include "libc/intrin/asancodes.h"
#include "libc/intrin/kprintf.h" #include "libc/intrin/kprintf.h"
#include "libc/intrin/lockcmpxchg.h"
#include "libc/log/backtrace.internal.h" #include "libc/log/backtrace.internal.h"
#include "libc/log/internal.h" #include "libc/log/internal.h"
#include "libc/log/libfatal.internal.h" #include "libc/log/libfatal.internal.h"
@ -842,7 +842,7 @@ void *__asan_morgue_add(void *p) {
for (;;) { for (;;) {
i = __asan_morgue.i; i = __asan_morgue.i;
j = (i + 1) & (ARRAYLEN(__asan_morgue.p) - 1); j = (i + 1) & (ARRAYLEN(__asan_morgue.p) - 1);
if (cmpxchg(&__asan_morgue.i, i, j)) { if (_lockcmpxchg(&__asan_morgue.i, i, j)) {
r = __asan_morgue.p[i]; r = __asan_morgue.p[i];
__asan_morgue.p[i] = p; __asan_morgue.p[i] = p;
return r; return r;
@ -855,7 +855,7 @@ static void __asan_morgue_flush(void) {
void *p; void *p;
for (i = 0; i < ARRAYLEN(__asan_morgue.p); ++i) { for (i = 0; i < ARRAYLEN(__asan_morgue.p); ++i) {
p = __asan_morgue.p[i]; p = __asan_morgue.p[i];
if (cmpxchg(__asan_morgue.p + i, p, 0)) { if (_lockcmpxchg(__asan_morgue.p + i, p, 0)) {
if (weaken(dlfree)) { if (weaken(dlfree)) {
weaken(dlfree)(p); weaken(dlfree)(p);
} }
@ -1206,7 +1206,7 @@ void __asan_evil(uint8_t *addr, int size, const char *s1, const char *s2) {
} }
void __asan_report_load(uint8_t *addr, int size) { void __asan_report_load(uint8_t *addr, int size) {
if (cmpxchg(&__asan_noreentry, false, true)) { if (_lockcmpxchg(&__asan_noreentry, false, true)) {
if (!__vforked) { if (!__vforked) {
__asan_report_memory_fault(addr, size, "load")(); __asan_report_memory_fault(addr, size, "load")();
__asan_unreachable(); __asan_unreachable();
@ -1219,7 +1219,7 @@ void __asan_report_load(uint8_t *addr, int size) {
} }
void __asan_report_store(uint8_t *addr, int size) { void __asan_report_store(uint8_t *addr, int size) {
if (cmpxchg(&__asan_noreentry, false, true)) { if (_lockcmpxchg(&__asan_noreentry, false, true)) {
if (!__vforked) { if (!__vforked) {
__asan_report_memory_fault(addr, size, "store")(); __asan_report_memory_fault(addr, size, "store")();
__asan_unreachable(); __asan_unreachable();
@ -1364,7 +1364,7 @@ static textstartup void __asan_shadow_existing_mappings(void) {
textstartup void __asan_init(int argc, char **argv, char **envp, textstartup void __asan_init(int argc, char **argv, char **envp,
intptr_t *auxv) { intptr_t *auxv) {
static bool once; static bool once;
if (!cmpxchg(&once, false, true)) return; if (!_lockcmpxchg(&once, false, true)) return;
if (IsWindows() && NtGetVersion() < kNtVersionWindows10) { if (IsWindows() && NtGetVersion() < kNtVersionWindows10) {
__write_str("error: asan binaries require windows10\r\n"); __write_str("error: asan binaries require windows10\r\n");
__restorewintty(); __restorewintty();

View file

@ -17,9 +17,10 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/assert.h" #include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/bits/weaken.h" #include "libc/bits/weaken.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/kprintf.h" #include "libc/intrin/kprintf.h"
#include "libc/intrin/lockcmpxchg.h"
#include "libc/log/log.h" #include "libc/log/log.h"
#include "libc/runtime/internal.h" #include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
@ -29,17 +30,19 @@
*/ */
relegated wontreturn void __assert_fail(const char *expr, const char *file, relegated wontreturn void __assert_fail(const char *expr, const char *file,
int line) { int line) {
int rc;
static bool noreentry; static bool noreentry;
kprintf("%s:%d: assert(%s) failed%n", file, line, expr); kprintf("%s:%d: assert(%s) failed%n", file, line, expr);
if (cmpxchg(&noreentry, false, true)) { if (_lockcmpxchg(&noreentry, false, true)) {
if (weaken(__die)) { if (weaken(__die)) {
weaken(__die)(); weaken(__die)();
} else { } else {
kprintf("can't backtrace b/c `__die` not linked%n"); kprintf("can't backtrace b/c `__die` not linked%n");
} }
__restorewintty(); rc = 23;
_Exit(23); } else {
rc = 24;
} }
__restorewintty(); __restorewintty();
_Exit(24); _Exit(rc);
} }

View file

@ -16,15 +16,21 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h" #include "libc/intrin/atomic_load.h"
#include "libc/macros.internal.h"
#include "libc/str/str.h"
/** /**
* Atomically loads value. * Reads scalar from memory w/ one operation.
* *
* This macro is intended to prevent things like compiler load tearing * This macro is intended to prevent things like compiler load tearing
* optimizations. * optimizations.
*
* @param MEM is alignas(𝑘) uint𝑘_t[hasatleast 1] where 𝑘 {8,16,32,64}
* @return *(MEM)
* @note defeats compiler load tearing optimizations
* @note alignas(𝑘) is implied if compiler knows type
* @note alignas(𝑘) only avoids multi-core / cross-page edge cases
* @see Intel's Six-Thousand Page Manual V.3A §8.2.3.1
* @see atomic_store()
*/ */
intptr_t(atomic_load)(void *p, size_t n) { intptr_t(atomic_load)(void *p, size_t n) {
intptr_t x = 0; intptr_t x = 0;

22
libc/intrin/atomic_load.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef COSMOPOLITAN_LIBC_INTRIN_ATOMIC_LOAD_H_
#define COSMOPOLITAN_LIBC_INTRIN_ATOMIC_LOAD_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
intptr_t atomic_load(void *, size_t);
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
#define atomic_load(MEM) \
({ \
autotype(MEM) Mem = (MEM); \
typeof(*Mem) Reg; \
asm("mov\t%1,%0" : "=r"(Reg) : "m"(*Mem)); \
Reg; \
})
#else
#define atomic_load(MEM) atomic_load(MEM, sizeof(*(MEM)))
#endif /* GNUC && !ANSI && x86 */
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_INTRIN_ATOMIC_LOAD_H_ */

View file

@ -16,15 +16,23 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h" #include "libc/intrin/atomic_store.h"
#include "libc/macros.internal.h"
#include "libc/str/str.h"
/** /**
* Atomically stores value. * Saves scalar to memory w/ one operation.
* *
* This macro is intended to prevent things like compiler store tearing * This is guaranteed to happen in either one or zero operations,
* optimizations. * depending on whether or not it's possible for *(MEM) to be read
* afterwards. This macro only forbids compiler from using >1 ops.
*
* @param MEM is alignas(𝑘) uint𝑘_t[hasatleast 1] where 𝑘 {8,16,32,64}
* @param VAL is uint𝑘_t w/ better encoding for immediates (constexpr)
* @return VAL
* @note alignas(𝑘) on nexgen32e only needed for end of page gotcha
* @note alignas(𝑘) is implied if compiler knows type
* @note needed to defeat store tearing optimizations
* @see Intel Six-Thousand Page Manual Manual V.3A §8.2.3.1
* @see atomic_load()
*/ */
intptr_t(atomic_store)(void *p, intptr_t x, size_t n) { intptr_t(atomic_store)(void *p, intptr_t x, size_t n) {
switch (n) { switch (n) {

View file

@ -0,0 +1,23 @@
#ifndef COSMOPOLITAN_LIBC_INTRIN_ATOMIC_STORE_H_
#define COSMOPOLITAN_LIBC_INTRIN_ATOMIC_STORE_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
intptr_t atomic_store(void *, intptr_t, size_t);
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
#define atomic_store(MEM, VAL) \
({ \
autotype(VAL) Val = (VAL); \
typeof(&Val) Mem = (MEM); \
asm("mov%z1\t%1,%0" : "=m"(*Mem) : "r"(Val)); \
Val; \
})
#else
#define atomic_store(MEM, VAL) \
atomic_store(MEM, VAL, sizeof(*(MEM)) / (sizeof(*(MEM)) == sizeof(*(VAL))))
#endif /* __GNUC__ && !__STRICT_ANSI__ */
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_INTRIN_ATOMIC_STORE_H_ */

View file

@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h" #include "libc/intrin/cmpxchg.h"
/** /**
* Compares and exchanges. * Compares and exchanges.
@ -24,23 +24,23 @@
* @param ifthing is uint𝑘_t[hasatleast 1] where 𝑘 {8,16,32,64} * @param ifthing is uint𝑘_t[hasatleast 1] where 𝑘 {8,16,32,64}
* @param size is automatically supplied by macro wrapper * @param size is automatically supplied by macro wrapper
* @return true if value was exchanged, otherwise false * @return true if value was exchanged, otherwise false
* @see lockcmpxchg() * @see _lockcmpxchg()
*/ */
bool(cmpxchg)(void *ifthing, intptr_t isequaltome, intptr_t replaceitwithme, bool(_cmpxchg)(void *ifthing, intptr_t isequaltome, intptr_t replaceitwithme,
size_t size) { size_t size) {
switch (size) { switch (size) {
case 1: case 1:
return cmpxchg((int8_t *)ifthing, (int8_t)isequaltome, return _cmpxchg((int8_t *)ifthing, (int8_t)isequaltome,
(int8_t)replaceitwithme); (int8_t)replaceitwithme);
case 2: case 2:
return cmpxchg((int16_t *)ifthing, (int16_t)isequaltome, return _cmpxchg((int16_t *)ifthing, (int16_t)isequaltome,
(int16_t)replaceitwithme); (int16_t)replaceitwithme);
case 4: case 4:
return cmpxchg((int32_t *)ifthing, (int32_t)isequaltome, return _cmpxchg((int32_t *)ifthing, (int32_t)isequaltome,
(int32_t)replaceitwithme); (int32_t)replaceitwithme);
case 8: case 8:
return cmpxchg((int64_t *)ifthing, (int64_t)isequaltome, return _cmpxchg((int64_t *)ifthing, (int64_t)isequaltome,
(int64_t)replaceitwithme); (int64_t)replaceitwithme);
default: default:
return false; return false;
} }

29
libc/intrin/cmpxchg.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef COSMOPOLITAN_LIBC_INTRIN_CMPXCHG_H_
#define COSMOPOLITAN_LIBC_INTRIN_CMPXCHG_H_
#include "libc/bits/asmflag.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
bool _cmpxchg(void *, intptr_t, intptr_t, size_t);
#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && defined(__x86__)
#define _cmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \
({ \
bool DidIt; \
autotype(IFTHING) IfThing = (IFTHING); \
typeof(*IfThing) IsEqualToMe = (ISEQUALTOME); \
typeof(*IfThing) ReplaceItWithMe = (REPLACEITWITHME); \
asm volatile(ZFLAG_ASM("cmpxchg\t%3,%1") \
: ZFLAG_CONSTRAINT(DidIt), "+m"(*IfThing), "+a"(IsEqualToMe) \
: "r"(ReplaceItWithMe) \
: "cc"); \
DidIt; \
})
#else
#define _cmpxchg(MEM, CMP, VAL) \
_cmpxchg(MEM, (intptr_t)(CMP), (intptr_t)(VAL), sizeof(*(MEM)))
#endif /* GNUC && !ANSI && x86 */
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_INTRIN_CMPXCHG_H_ */

View file

@ -1,6 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_BITS_CMPXCHG16B_INTERNAL_H_ #ifndef COSMOPOLITAN_LIBC_BITS_CMPXCHG16B_INTERNAL_H_
#define COSMOPOLITAN_LIBC_BITS_CMPXCHG16B_INTERNAL_H_ #define COSMOPOLITAN_LIBC_BITS_CMPXCHG16B_INTERNAL_H_
#include "libc/bits/bits.h" #include "libc/bits/asmflag.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_

View file

@ -16,6 +16,8 @@ const char *DescribeNtFileFlagsAndAttributes(uint32_t);
const char *DescribeNtFileShareFlags(uint32_t); const char *DescribeNtFileShareFlags(uint32_t);
const char *DescribeNtFileAccessFlags(uint32_t); const char *DescribeNtFileAccessFlags(uint32_t);
const char *DescribeNtProcessAccessFlags(uint32_t); const char *DescribeNtProcessAccessFlags(uint32_t);
const char *DescribeNtConsoleModeInputFlags(uint32_t);
const char *DescribeNtConsoleModeOutputFlags(uint32_t);
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -0,0 +1,41 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/describeflags.internal.h"
#include "libc/macros.internal.h"
#include "libc/nt/enum/consolemodeflags.h"
static const struct DescribeFlags kConsoleModeInputFlags[] = {
{kNtEnableProcessedInput, "ProcessedInput"}, //
{kNtEnableLineInput, "LineInput"}, //
{kNtEnableEchoInput, "EchoInput"}, //
{kNtEnableWindowInput, "WindowInput"}, //
{kNtEnableMouseInput, "MouseInput"}, //
{kNtEnableInsertMode, "InsertMode"}, //
{kNtEnableQuickEditMode, "QuickEditMode"}, //
{kNtEnableExtendedFlags, "ExtendedFlags"}, //
{kNtEnableAutoPosition, "AutoPosition"}, //
{kNtEnableVirtualTerminalInput, "VirtualTerminalInput"}, //
};
const char *DescribeNtConsoleModeInputFlags(uint32_t x) {
static char consolemodeinputflags[256];
return DescribeFlags(consolemodeinputflags, sizeof(consolemodeinputflags),
kConsoleModeInputFlags, ARRAYLEN(kConsoleModeInputFlags),
"kNtEnable", x);
}

View file

@ -1,7 +1,7 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the any purpose with or without fee is hereby granted, provided that the
@ -16,46 +16,21 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/dce.h" #include "libc/intrin/describeflags.internal.h"
#include "libc/runtime/internal.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/nr.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
.privileged #include "libc/nt/enum/consolemodeflags.h"
// Terminates program abnormally. static const struct DescribeFlags kConsoleModeOutputFlags[] = {
// {kNtEnableProcessedOutput, "EnableProcessedOutput"}, //
// This function first tries to trigger your SIGABRT handler. If {kNtEnableWrapAtEolOutput, "EnableWrapAtEolOutput"}, //
// there isn't one or execution resumes, then abort() terminates {kNtEnableVirtualTerminalProcessing, "EnableVirtualTerminalProcessing"}, //
// the program using an escalating variety methods of increasing {kNtDisableNewlineAutoReturn, "DisableNewlineAutoReturn"}, //
// brutality. {kNtEnableLvbGridWorldwide, "EnableLvbGridWorldwide"}, //
// };
// @forcealignargpointer
// @asyncsignalsafe const char *DescribeNtConsoleModeOutputFlags(uint32_t x) {
// @noreturn static char consolemodeoutputflags[128];
abort: push %rbp return DescribeFlags(consolemodeoutputflags, sizeof(consolemodeoutputflags),
mov %rsp,%rbp kConsoleModeOutputFlags,
and $-16,%rsp ARRAYLEN(kConsoleModeOutputFlags), "kNt", x);
sub $16,%rsp }
#if SupportsWindows()
testb IsWindows()
jnz sys_abort_nt
#endif
mov SIG_SETMASK,%edi
mov %rsp,%rsi
push $0xffffffffffffffdf # all bits blocked but SIGABRT
push $0xffffffffffffffff # assumes von neum. arithmetic
pop 8(%rsi)
pop (%rsi)
xor %edx,%edx # don't care about old sigmask
pushpop 4*4,%r10 # sizeof(sigset_t) for systemd
mov __NR_sigprocmask,%eax # sys_sigprocmask is hookable
syscall
mov __NR_getpid,%eax
syscall
mov %eax,%edi
mov SIGABRT,%esi
mov __NR_kill,%eax
syscall # avoid hook and less bt noise
call _Exit
.endfn abort,globl,protected

View file

@ -21,20 +21,20 @@
#include "libc/nt/enum/processaccess.h" #include "libc/nt/enum/processaccess.h"
static const struct DescribeFlags kProcessAccessflags[] = { static const struct DescribeFlags kProcessAccessflags[] = {
{kNtProcessAllAccess, "AllAccess"}, {kNtProcessAllAccess, "AllAccess"}, //
{kNtProcessCreateProcess, "CreateProcess"}, {kNtProcessCreateProcess, "CreateProcess"}, //
{kNtProcessCreateThread, "CreateThread"}, {kNtProcessCreateThread, "CreateThread"}, //
{kNtProcessDupHandle, "DupHandle"}, {kNtProcessDupHandle, "DupHandle"}, //
{kNtProcessQueryInformation, "QueryInformation"}, {kNtProcessQueryInformation, "QueryInformation"}, //
{kNtProcessQueryLimitedInformation, "QueryLimitedInformation"}, {kNtProcessQueryLimitedInformation, "QueryLimitedInformation"}, //
{kNtProcessSetInformation, "SetInformation"}, {kNtProcessSetInformation, "SetInformation"}, //
{kNtProcessSetQuota, "SetQuota"}, {kNtProcessSetQuota, "SetQuota"}, //
{kNtProcessSuspendResume, "SuspendResume"}, {kNtProcessSuspendResume, "SuspendResume"}, //
{kNtProcessTerminate, "Terminate"}, {kNtProcessTerminate, "Terminate"}, //
{kNtProcessVmOperation, "VmOperation"}, {kNtProcessVmOperation, "VmOperation"}, //
{kNtProcessVmRead, "VmRead"}, {kNtProcessVmRead, "VmRead"}, //
{kNtProcessVmWrite, "VmWrite"}, {kNtProcessVmWrite, "VmWrite"}, //
{kNtProcessSynchronize, "Synchronize"}, {kNtProcessSynchronize, "Synchronize"}, //
}; };
const char *DescribeNtProcessAccessFlags(uint32_t x) { const char *DescribeNtProcessAccessFlags(uint32_t x) {

View file

@ -16,68 +16,63 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "dsp/core/core.h" #include "libc/calls/calls.h"
#include "dsp/mpeg/mpeg.h" #include "libc/dce.h"
#include "libc/log/check.h" #include "libc/nt/thread.h"
#include "libc/macros.internal.h"
#include "libc/rand/rand.h"
#include "libc/runtime/buffer.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
short pcm[8][8]; /**
float binary32[8][8]; * Returns current thread id.
struct GuardedBuffer b1, b2; * @asyncsignalsafe
*/
int gettid(void) {
int rc;
int64_t wut;
TEST(float2short, test) { if (IsLinux()) {
binary32[0][0] = -0.5; asm("syscall"
binary32[0][1] = 0.0; : "=a"(rc) // man says always succeeds
binary32[0][2] = 0.5; : "0"(186) // __NR_gettid
float2short(8, pcm, binary32); : "rcx", "r11", "memory");
EXPECT_EQ(-16384, pcm[0][0]); return rc;
EXPECT_EQ(0, pcm[0][1]);
EXPECT_EQ(16384, pcm[0][2]);
}
TEST(float2short, testOverflow) {
binary32[0][0] = -1.1;
binary32[0][1] = -1.0;
binary32[0][2] = 1.0;
binary32[0][3] = 1.1;
float2short(8, pcm, binary32);
EXPECT_EQ(-32768, pcm[0][0]);
EXPECT_EQ(-32768, pcm[0][1]);
EXPECT_EQ(32767, pcm[0][2]);
EXPECT_EQ(32767, pcm[0][3]);
}
void unclamped(size_t n, short pcm16[n][8], const float binary32[n][8]) {
size_t i, j;
for (i = 0; i < n; ++i) {
for (j = 0; j < 8; ++j) {
pcm16[i][j] = binary32[i][j] * 32768;
}
} }
}
plm_samples_t samps; if (IsXnu()) {
asm("syscall" // xnu/osfmk/kern/ipc_tt.c
void randomizeaudio(void) { : "=a"(rc) // assume success
size_t i; : "0"(0x1000000 | 27) // Mach thread_self_trap()
for (i = 0; i < ARRAYLEN(samps.interleaved); ++i) { : "rcx", "r11", "memory", "cc");
samps.interleaved[i] = randf(); return rc;
} }
}
void float2short_pure(void) { if (IsOpenbsd()) {
float2short(ARRAYLEN(samps.interleaved) / 8, pcm, (void *)samps.interleaved); asm("syscall"
} : "=a"(rc) // man says always succeeds
: "0"(299) // getthrid()
: "rcx", "r11", "memory", "cc");
return rc;
}
void float2short_unclamped(void) { if (IsNetbsd()) {
unclamped(ARRAYLEN(samps.interleaved) / 8, pcm, (void *)samps.interleaved); asm("syscall"
} : "=a"(rc) // man says always succeeds
: "0"(311) // _lwp_self()
: "rcx", "r11", "memory", "cc");
return rc;
}
BENCH(float2short, audioframe) { if (IsFreebsd()) {
EZBENCH(randomizeaudio(), float2short_pure()); asm("syscall"
EZBENCH(randomizeaudio(), float2short_unclamped()); : "=a"(rc), // only fails w/ EFAULT, which isn't possible
"=m"(wut) // must be 64-bit
: "0"(432), // thr_self()
"D"(&wut) // but not actually 64-bit
: "rcx", "r11", "memory", "cc");
return wut; // narrowing intentional
}
if (IsWindows()) {
return GetCurrentThreadId();
}
return getpid();
} }

View file

@ -26,6 +26,7 @@ LIBC_INTRIN_A_CHECKS = \
LIBC_INTRIN_A_DIRECTDEPS = \ LIBC_INTRIN_A_DIRECTDEPS = \
LIBC_STUBS \ LIBC_STUBS \
LIBC_SYSV \ LIBC_SYSV \
LIBC_SYSV_CALLS \
LIBC_NEXGEN32E \ LIBC_NEXGEN32E \
LIBC_NT_KERNEL32 LIBC_NT_KERNEL32
@ -66,6 +67,7 @@ o/$(MODE)/libc/intrin/kprintf.greg.o: \
o/$(MODE)/libc/intrin/createfile.greg.o \ o/$(MODE)/libc/intrin/createfile.greg.o \
o/$(MODE)/libc/intrin/createpipe.greg.o \ o/$(MODE)/libc/intrin/createpipe.greg.o \
o/$(MODE)/libc/intrin/closehandle.greg.o \ o/$(MODE)/libc/intrin/closehandle.greg.o \
o/$(MODE)/libc/intrin/openprocess.greg.o \
o/$(MODE)/libc/intrin/createthread.greg.o \ o/$(MODE)/libc/intrin/createthread.greg.o \
o/$(MODE)/libc/intrin/createprocess.greg.o \ o/$(MODE)/libc/intrin/createprocess.greg.o \
o/$(MODE)/libc/intrin/describeflags.greg.o \ o/$(MODE)/libc/intrin/describeflags.greg.o \
@ -75,9 +77,11 @@ o/$(MODE)/libc/intrin/flushviewoffile.greg.o \
o/$(MODE)/libc/intrin/deviceiocontrol.greg.o \ o/$(MODE)/libc/intrin/deviceiocontrol.greg.o \
o/$(MODE)/libc/intrin/createdirectory.greg.o \ o/$(MODE)/libc/intrin/createdirectory.greg.o \
o/$(MODE)/libc/intrin/flushfilebuffers.greg.o \ o/$(MODE)/libc/intrin/flushfilebuffers.greg.o \
o/$(MODE)/libc/intrin/terminateprocess.greg.o \
o/$(MODE)/libc/intrin/getfileattributes.greg.o \ o/$(MODE)/libc/intrin/getfileattributes.greg.o \
o/$(MODE)/libc/intrin/mapviewoffileexnuma.greg.o \ o/$(MODE)/libc/intrin/mapviewoffileexnuma.greg.o \
o/$(MODE)/libc/intrin/createfilemappingnuma.greg.o \ o/$(MODE)/libc/intrin/createfilemappingnuma.greg.o \
o/$(MODE)/libc/intrin/generateconsolectrlevent.greg.o \
o/$(MODE)/libc/intrin/kstarttsc.o \ o/$(MODE)/libc/intrin/kstarttsc.o \
o/$(MODE)/libc/intrin/nomultics.o \ o/$(MODE)/libc/intrin/nomultics.o \
o/$(MODE)/libc/intrin/ntconsolemode.o: \ o/$(MODE)/libc/intrin/ntconsolemode.o: \

View file

@ -17,7 +17,6 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#define ShouldUseMsabiAttribute() 1 #define ShouldUseMsabiAttribute() 1
#include "libc/bits/bits.h"
#include "libc/bits/likely.h" #include "libc/bits/likely.h"
#include "libc/bits/safemacros.internal.h" #include "libc/bits/safemacros.internal.h"
#include "libc/bits/weaken.h" #include "libc/bits/weaken.h"
@ -27,7 +26,10 @@
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/fmt/divmod10.internal.h" #include "libc/fmt/divmod10.internal.h"
#include "libc/fmt/fmt.h" #include "libc/fmt/fmt.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/kprintf.h" #include "libc/intrin/kprintf.h"
#include "libc/intrin/lockcmpxchg.h"
#include "libc/intrin/spinlock.h"
#include "libc/limits.h" #include "libc/limits.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/nexgen32e/rdtsc.h" #include "libc/nexgen32e/rdtsc.h"
@ -42,6 +44,7 @@
#include "libc/str/utf16.h" #include "libc/str/utf16.h"
#include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/nr.h"
#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/prot.h"
#include "libc/time/clockstonanos.internal.h"
struct Timestamps { struct Timestamps {
unsigned long long birth; unsigned long long birth;
@ -51,14 +54,7 @@ struct Timestamps {
extern int __pid; extern int __pid;
extern bool __replmode; extern bool __replmode;
extern bool __nomultics; extern bool __nomultics;
volatile unsigned long long __kbirth; unsigned long long __kbirth; // see fork-nt.c
static inline uint64_t ClocksToNanos(uint64_t x, uint64_t y) {
// approximation of round(x*.323018) which is usually
// the ratio between inva rdtsc ticks and nanoseconds
uint128_t difference = x - y;
return (difference * 338709) >> 20;
}
privileged static struct Timestamps kenter(void) { privileged static struct Timestamps kenter(void) {
struct Timestamps ts; struct Timestamps ts;
@ -67,7 +63,7 @@ privileged static struct Timestamps kenter(void) {
if (!ts.birth) { if (!ts.birth) {
ts.birth = kStartTsc; ts.birth = kStartTsc;
if (!ts.birth) ts.birth = 1; if (!ts.birth) ts.birth = 1;
cmpxchg(&__kbirth, 0, ts.birth); _lockcmpxchg(&__kbirth, 0, ts.birth);
} }
return ts; return ts;
} }
@ -78,7 +74,9 @@ privileged static void kleave(struct Timestamps ts) {
elapse = unsignedsubtract(finish, ts.start); elapse = unsignedsubtract(finish, ts.start);
adjust = ts.birth + elapse; adjust = ts.birth + elapse;
if (!adjust) adjust = 1; if (!adjust) adjust = 1;
cmpxchg(&__kbirth, ts.birth, adjust); /* ignore overlapping time intervals */ if (__kbirth == ts.birth) {
_lockcmpxchg(&__kbirth, ts.birth, adjust); // ignore overlapping intervals
}
} }
privileged static inline char *kadvance(char *p, char *e, long n) { privileged static inline char *kadvance(char *p, char *e, long n) {
@ -732,6 +730,32 @@ privileged size_t ksnprintf(char *b, size_t n, const char *fmt, ...) {
return m; return m;
} }
/**
* Privileged snprintf() w/o timestamp feature.
*
* This provides a marginal performance boost, but it means %T can no
* longer be used.
*
* snprintf(".") l: 25𝑐 8𝑛𝑠
* kusnprintf(".") l: 22𝑐 7𝑛𝑠
* ksnprintf(".") l: 54𝑐 17𝑛𝑠
*
* @param b is buffer, and guaranteed a NUL-terminator if `n>0`
* @param n is number of bytes available in buffer
* @return length of output excluding NUL, which may exceed `n`
* @see kprintf() for documentation
* @asyncsignalsafe
* @vforksafe
*/
privileged size_t kusnprintf(char *b, size_t n, const char *fmt, ...) {
size_t m;
va_list v;
va_start(v, fmt);
m = kformat(b, n, fmt, v, (struct Timestamps){0});
va_end(v);
return m;
}
/** /**
* Privileged vsnprintf(). * Privileged vsnprintf().
* *

View file

@ -5,6 +5,7 @@ COSMOPOLITAN_C_START_
void kprintf(const char *, ...); void kprintf(const char *, ...);
size_t ksnprintf(char *, size_t, const char *, ...); size_t ksnprintf(char *, size_t, const char *, ...);
size_t kusnprintf(char *, size_t, const char *, ...);
void kvprintf(const char *, va_list); void kvprintf(const char *, va_list);
size_t kvsnprintf(char *, size_t, const char *, va_list); size_t kvsnprintf(char *, size_t, const char *, va_list);
bool kisdangerous(const void *); bool kisdangerous(const void *);

View file

@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h" #include "libc/intrin/lockcmpxchg.h"
/** /**
* Compares and exchanges w/ lock prefix. * Compares and exchanges w/ lock prefix.
@ -24,23 +24,23 @@
* @param ifthing is uint𝑘_t[hasatleast 1] where 𝑘 {8,16,32,64} * @param ifthing is uint𝑘_t[hasatleast 1] where 𝑘 {8,16,32,64}
* @param size is automatically supplied by macro wrapper * @param size is automatically supplied by macro wrapper
* @return true if value was exchanged, otherwise false * @return true if value was exchanged, otherwise false
* @see cmpxchg() * @see cmpxchg() if only written by one thread
*/ */
bool(lockcmpxchg)(void *ifthing, intptr_t isequaltome, intptr_t replaceitwithme, bool(_lockcmpxchg)(void *ifthing, intptr_t isequaltome,
size_t size) { intptr_t replaceitwithme, size_t size) {
switch (size) { switch (size) {
case 1: case 1:
return lockcmpxchg((int8_t *)ifthing, (int8_t)isequaltome, return _lockcmpxchg((int8_t *)ifthing, (int8_t)isequaltome,
(int8_t)replaceitwithme); (int8_t)replaceitwithme);
case 2: case 2:
return lockcmpxchg((int16_t *)ifthing, (int16_t)isequaltome, return _lockcmpxchg((int16_t *)ifthing, (int16_t)isequaltome,
(int16_t)replaceitwithme); (int16_t)replaceitwithme);
case 4: case 4:
return lockcmpxchg((int32_t *)ifthing, (int32_t)isequaltome, return _lockcmpxchg((int32_t *)ifthing, (int32_t)isequaltome,
(int32_t)replaceitwithme); (int32_t)replaceitwithme);
case 8: case 8:
return lockcmpxchg((int64_t *)ifthing, (int64_t)isequaltome, return _lockcmpxchg((int64_t *)ifthing, (int64_t)isequaltome,
(int64_t)replaceitwithme); (int64_t)replaceitwithme);
default: default:
return false; return false;
} }

29
libc/intrin/lockcmpxchg.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef COSMOPOLITAN_LIBC_INTRIN_LOCKCMPXCHG_H_
#define COSMOPOLITAN_LIBC_INTRIN_LOCKCMPXCHG_H_
#include "libc/bits/asmflag.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
bool _lockcmpxchg(void *, intptr_t, intptr_t, size_t);
#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && defined(__x86__)
#define _lockcmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \
({ \
bool DidIt; \
autotype(IFTHING) IfThing = (IFTHING); \
typeof(*IfThing) IsEqualToMe = (ISEQUALTOME); \
typeof(*IfThing) ReplaceItWithMe = (REPLACEITWITHME); \
asm volatile(ZFLAG_ASM("lock cmpxchg\t%3,%1") \
: ZFLAG_CONSTRAINT(DidIt), "+m"(*IfThing), "+a"(IsEqualToMe) \
: "r"(ReplaceItWithMe) \
: "cc"); \
DidIt; \
})
#else
#define _lockcmpxchg(MEM, CMP, VAL) \
_lockcmpxchg(MEM, (intptr_t)(CMP), (intptr_t)(VAL), sizeof(*(MEM)))
#endif /* GNUC && !ANSI && x86 */
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_INTRIN_LOCKCMPXCHG_H_ */

View file

@ -1,9 +1,10 @@
#ifndef COSMOPOLITAN_LIBC_BITS_LOCKCMPXCHG16B_INTERNAL_H_ #ifndef COSMOPOLITAN_LIBC_BITS_LOCKCMPXCHG16B_H_
#define COSMOPOLITAN_LIBC_BITS_LOCKCMPXCHG16B_INTERNAL_H_ #define COSMOPOLITAN_LIBC_BITS_LOCKCMPXCHG16B_H_
#include "libc/bits/bits.h" #include "libc/bits/asmflag.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && defined(__x86__)
/** /**
* Compares and exchanges 128-bit value, i.e. * Compares and exchanges 128-bit value, i.e.
* *
@ -38,7 +39,8 @@ static inline bool _lockcmpxchg16b(uint128_t *IfThing, uint128_t *IsEqualToMe,
} }
return DidIt; return DidIt;
} }
#endif /* __GNUC__ && !__STRICT_ANSI__ */
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_BITS_LOCKCMPXCHG16B_INTERNAL_H_ */ #endif /* COSMOPOLITAN_LIBC_BITS_LOCKCMPXCHG16B_H_ */

View file

@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the any purpose with or without fee is hereby granted, provided that the
@ -16,12 +16,29 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/limits.h" #include "libc/intrin/lockxadd.h"
#include "libc/rand/internal.h" #include "libc/runtime/runtime.h"
#include "libc/rand/lcg.internal.h"
#include "libc/rand/rand.h"
float randf(void) { /**
return (double)(int)(KnuthLinearCongruentialGenerator(&g_rando) >> 32) / * Compares and exchanges w/ lock prefix.
INT_MAX; *
* @param ifthing is uint𝑘_t[hasatleast 1] where 𝑘 {8,16,32,64}
* @param size is automatically supplied by macro wrapper
* @return value at location `*ifthing` *before* addition
* @see InterlockedAdd() for a very similar API
* @see xadd() if only written by one thread
*/
intptr_t(_lockxadd)(void *ifthing, intptr_t replaceitwithme, size_t size) {
switch (size) {
case 1:
return _lockxadd((int8_t *)ifthing, (int8_t)replaceitwithme);
case 2:
return _lockxadd((int16_t *)ifthing, (int16_t)replaceitwithme);
case 4:
return _lockxadd((int32_t *)ifthing, (int32_t)replaceitwithme);
case 8:
return _lockxadd((int64_t *)ifthing, (int64_t)replaceitwithme);
default:
abort();
}
} }

View file

@ -1,8 +1,11 @@
#ifndef COSMOPOLITAN_LIBC_BITS_LOCKXADD_INTERNAL_H_ #ifndef COSMOPOLITAN_LIBC_BITS_LOCKXADD_H_
#define COSMOPOLITAN_LIBC_BITS_LOCKXADD_INTERNAL_H_ #define COSMOPOLITAN_LIBC_BITS_LOCKXADD_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
intptr_t _lockxadd(void *, intptr_t, size_t);
#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && defined(__x86__)
#define _lockxadd(PTR, VAL) \ #define _lockxadd(PTR, VAL) \
({ \ ({ \
typeof(*(PTR)) Res; \ typeof(*(PTR)) Res; \
@ -10,7 +13,10 @@ COSMOPOLITAN_C_START_
asm volatile("lock xadd\t%0,%1" : "=r"(Res), "+m"(*(PTR)) : "0"(Val)); \ asm volatile("lock xadd\t%0,%1" : "=r"(Res), "+m"(*(PTR)) : "0"(Val)); \
Res; /* contains *PTR before addition cf. InterlockedAdd() */ \ Res; /* contains *PTR before addition cf. InterlockedAdd() */ \
}) })
#else
#define _lockxadd(MEM, VAL) _lockxadd(MEM, (intptr_t)(VAL), sizeof(*(MEM)))
#endif /* GNUC && !ANSI && x86 */
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_BITS_LOCKXADD_INTERNAL_H_ */ #endif /* COSMOPOLITAN_LIBC_BITS_LOCKXADD_H_ */

View file

@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h" #include "libc/intrin/lockxchg.h"
/** /**
* Compares and exchanges w/ lock prefix. * Compares and exchanges w/ lock prefix.

29
libc/intrin/lockxchg.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef COSMOPOLITAN_LIBC_INTRIN_LOCKXCHG_H_
#define COSMOPOLITAN_LIBC_INTRIN_LOCKXCHG_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
intptr_t lockxchg(void *, void *, size_t);
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
/**
* Exchanges *MEMORY into *LOCALVAR w/ one operation.
*
* @param MEMORY is uint𝑘_t[hasatleast 1] where 𝑘 {8,16,32,64}
* @param LOCALVAR is uint𝑘_t[hasatleast 1]
* @return LOCALVAR[0]
* @see xchg()
*/
#define lockxchg(MEMORY, LOCALVAR) \
({ \
asm("xchg\t%0,%1" : "+%m"(*(MEMORY)), "+r"(*(LOCALVAR))); \
*(LOCALVAR); \
})
#else
#define lockxchg(MEM, VAR) \
lockxchg(MEM, VAR, sizeof(*(MEM)) / (sizeof(*(MEM)) == sizeof(*(VAR))))
#endif /* __GNUC__ && !__STRICT_ANSI__ */
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_INTRIN_LOCKXCHG_H_ */

View file

@ -18,4 +18,4 @@
*/ */
#include "libc/runtime/internal.h" #include "libc/runtime/internal.h"
uint32_t __ntconsolemode[2]; uint32_t __ntconsolemode[3];

View file

@ -23,7 +23,12 @@
#include "libc/runtime/internal.h" #include "libc/runtime/internal.h"
uint32_t __winmainpid; uint32_t __winmainpid;
const char kConsoleHandles[2] = {kNtStdInputHandle, kNtStdOutputHandle};
const char kConsoleHandles[3] = {
kNtStdInputHandle,
kNtStdOutputHandle,
kNtStdErrorHandle,
};
/** /**
* Puts cmd.exe gui back the way it was. * Puts cmd.exe gui back the way it was.
@ -31,7 +36,7 @@ const char kConsoleHandles[2] = {kNtStdInputHandle, kNtStdOutputHandle};
noasan void __restorewintty(void) { noasan void __restorewintty(void) {
int i; int i;
if (IsWindows() && GetCurrentProcessId() == __winmainpid) { if (IsWindows() && GetCurrentProcessId() == __winmainpid) {
for (i = 0; i < 2; ++i) { for (i = 0; i < 3; ++i) {
SetConsoleMode(GetStdHandle(kConsoleHandles[i]), __ntconsolemode[i]); SetConsoleMode(GetStdHandle(kConsoleHandles[i]), __ntconsolemode[i]);
} }
__winmainpid = 0; __winmainpid = 0;

View file

@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the any purpose with or without fee is hereby granted, provided that the
@ -16,24 +16,35 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/assert.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h"
#include "libc/dce.h" #include "libc/intrin/lockcmpxchg.h"
#include "libc/nt/thread.h" #include "libc/intrin/spinlock.h"
/** /**
* Returns current thread id. * Acquires spinlock.
* @asyncsignalsafe
*/ */
uint32_t gettid(void) { void cthread_spinlock(cthread_spinlock_t *lock) {
uint32_t res; #if 0
if (!IsWindows()) { // TODO(jart): possibly reenable for !NDEBUG when we have TLS
res = sys_gettid(); int me = gettid();
if (res <= 0) { if (lock->x && lock->owner == me) {
res = getpid(); assert(!"cosmo spinlock not intended to be reentrant");
} return;
return res;
} else {
return GetCurrentThreadId();
} }
#endif
do
while (lock->x) asm("pause");
while (!_lockcmpxchg(&lock->x, false, true));
#if 0
lock->owner = me;
#endif
}
/**
* Releases spinlock.
*/
void cthread_spunlock(cthread_spinlock_t *lock) {
lock->x = false;
} }

22
libc/intrin/spinlock.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_
#define COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/* "Place each synchronization variable alone,
separated by 128 bytes or in a separate cache line."
Intel Optimization Manual §8.3.1 */
struct cthread_spinlock_t {
bool x;
int owner;
char __ignore[128 - 1 - 4];
} forcealign(128);
typedef struct cthread_spinlock_t cthread_spinlock_t;
void cthread_spinlock(cthread_spinlock_t *) dontthrow;
void cthread_spunlock(cthread_spinlock_t *) dontthrow;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ */

View file

@ -16,9 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/intrin/kprintf.h" #include "libc/intrin/lockcmpxchg.h"
#include "libc/log/backtrace.internal.h" #include "libc/log/backtrace.internal.h"
#include "libc/log/internal.h" #include "libc/log/internal.h"
#include "libc/log/libfatal.internal.h" #include "libc/log/libfatal.internal.h"
@ -33,18 +32,18 @@
*/ */
relegated wontreturn void __die(void) { relegated wontreturn void __die(void) {
/* asan runtime depends on this function */ /* asan runtime depends on this function */
int rc;
static bool once; static bool once;
kprintf("__die() called%n"); if (_lockcmpxchg(&once, false, true)) {
if (lockcmpxchg(&once, false, true)) {
__restore_tty(1); __restore_tty(1);
if (IsDebuggerPresent(false)) { if (IsDebuggerPresent(false)) {
DebugBreak(); DebugBreak();
} }
ShowBacktrace(2, NULL); ShowBacktrace(2, NULL);
__restorewintty(); rc = 77;
_Exit(77); } else {
rc = 78;
} }
kprintf("panic: __die() died%n");
__restorewintty(); __restorewintty();
_Exit(78); _Exit(rc);
} }

View file

@ -20,6 +20,7 @@
#include "libc/calls/strace.internal.h" #include "libc/calls/strace.internal.h"
#include "libc/intrin/asan.internal.h" #include "libc/intrin/asan.internal.h"
#include "libc/intrin/kprintf.h" #include "libc/intrin/kprintf.h"
#include "libc/intrin/lockcmpxchg.h"
#include "libc/runtime/internal.h" #include "libc/runtime/internal.h"
#include "libc/runtime/memtrack.internal.h" #include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
@ -72,7 +73,7 @@ static noasan bool HasLeaks(void) {
*/ */
noasan void CheckForMemoryLeaks(void) { noasan void CheckForMemoryLeaks(void) {
struct mallinfo mi; struct mallinfo mi;
if (!lockcmpxchg(&once, false, true)) { if (!_lockcmpxchg(&once, false, true)) {
kprintf("CheckForMemoryLeaks() may only be called once\n"); kprintf("CheckForMemoryLeaks() may only be called once\n");
exit(1); exit(1);
} }

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h"
#include "libc/bits/weaken.h" #include "libc/bits/weaken.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/sigbits.h" #include "libc/calls/sigbits.h"
@ -25,6 +24,7 @@
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/asan.internal.h" #include "libc/intrin/asan.internal.h"
#include "libc/intrin/kprintf.h" #include "libc/intrin/kprintf.h"
#include "libc/intrin/lockcmpxchg.h"
#include "libc/log/backtrace.internal.h" #include "libc/log/backtrace.internal.h"
#include "libc/log/gdb.h" #include "libc/log/gdb.h"
#include "libc/log/internal.h" #include "libc/log/internal.h"
@ -301,7 +301,7 @@ relegated noinstrument void __oncrash(int sig, struct siginfo *si,
static bool noreentry, notpossible; static bool noreentry, notpossible;
st = __strace, __strace = 0; st = __strace, __strace = 0;
ft = g_ftrace, g_ftrace = 0; ft = g_ftrace, g_ftrace = 0;
if (lockcmpxchg(&noreentry, false, true)) { if (_lockcmpxchg(&noreentry, false, true)) {
if (!__vforked) { if (!__vforked) {
rip = ctx ? ctx->uc_mcontext.rip : 0; rip = ctx ? ctx->uc_mcontext.rip : 0;
err = errno; err = errno;
@ -331,7 +331,7 @@ relegated noinstrument void __oncrash(int sig, struct siginfo *si,
g_ftrace = ft; g_ftrace = ft;
__strace = st; __strace = st;
return; return;
} else if (lockcmpxchg(&notpossible, false, true)) { } else if (_lockcmpxchg(&notpossible, false, true)) {
__minicrash(sig, si, ctx, "WHILE CRASHING"); __minicrash(sig, si, ctx, "WHILE CRASHING");
} else { } else {
for (;;) { for (;;) {

View file

@ -1,6 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_NEXGEN32E_RDTSCP_H_ #ifndef COSMOPOLITAN_LIBC_NEXGEN32E_RDTSCP_H_
#define COSMOPOLITAN_LIBC_NEXGEN32E_RDTSCP_H_ #define COSMOPOLITAN_LIBC_NEXGEN32E_RDTSCP_H_
#include "libc/bits/bits.h" #include "libc/bits/asmflag.h"
#include "libc/nexgen32e/x86feature.h" #include "libc/nexgen32e/x86feature.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_

View file

@ -17,7 +17,6 @@ void rt_end(double *, double *, double *, double *, double *);
char *strfry(char *); char *strfry(char *);
int getentropy(void *, size_t); int getentropy(void *, size_t);
ssize_t getrandom(void *, size_t, unsigned); ssize_t getrandom(void *, size_t, unsigned);
float randf(void);
char *initstate(unsigned, char *, size_t); char *initstate(unsigned, char *, size_t);
char *setstate(char *); char *setstate(char *);
long random(void); long random(void);

View file

@ -18,11 +18,9 @@
*/ */
#include "libc/assert.h" #include "libc/assert.h"
#include "libc/bits/bits.h" #include "libc/bits/bits.h"
#include "libc/bits/lockcmpxchg16b.internal.h"
#include "libc/bits/lockxadd.internal.h"
#include "libc/bits/weaken.h" #include "libc/bits/weaken.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/intrin/kprintf.h" #include "libc/intrin/spinlock.h"
#include "libc/nexgen32e/rdtsc.h" #include "libc/nexgen32e/rdtsc.h"
#include "libc/nt/thread.h" #include "libc/nt/thread.h"
#include "libc/rand/rand.h" #include "libc/rand/rand.h"
@ -32,8 +30,8 @@
extern int __pid; extern int __pid;
static int thepid; static int thepid;
static int thecount;
static uint128_t thepool; static uint128_t thepool;
static cthread_spinlock_t rand64_lock;
/** /**
* Returns nondeterministic random data. * Returns nondeterministic random data.
@ -48,48 +46,35 @@ static uint128_t thepool;
* @see rdseed(), rdrand(), rand(), random(), rngset() * @see rdseed(), rdrand(), rand(), random(), rngset()
* @note this function is not intended for cryptography * @note this function is not intended for cryptography
* @note this function passes bigcrush and practrand * @note this function passes bigcrush and practrand
* @note this function takes at minimum 30 cycles * @note this function takes at minimum 15 cycles
* @asyncsignalsafe * @asyncsignalsafe
* @threadsafe * @threadsafe
* @vforksafe * @vforksafe
*/ */
uint64_t rand64(void) { uint64_t rand64(void) {
void *d; void *p;
int c1, p1, p2; uint128_t s;
uint128_t s1, s2; cthread_spinlock(&rand64_lock);
do { if (__pid == thepid) {
p1 = __pid; s = thepool; // normal path
p2 = thepid; } else {
c1 = thecount; if (!thepid) {
asm volatile("" ::: "memory"); if (AT_RANDOM && (p = (void *)getauxval(AT_RANDOM))) {
s1 = thepool; // linux / freebsd kernel supplied entropy
if (p1 == p2) { memcpy(&s, p, 16);
// fast path
s2 = s1;
} else {
// slow path
if (!p2) {
// first call so get some cheap entropy
if ((d = (void *)getauxval(AT_RANDOM))) {
memcpy(&s2, d, 16); // kernel entropy
} else {
s2 = kStartTsc; // rdtsc() @ _start()
}
} else { } else {
// process contention so blend a timestamp // otherwise initialize w/ cheap timestamp
s2 = s1 ^ rdtsc(); s = kStartTsc;
} }
// toss the new pid in there } else {
s2 ^= p1; // blend another timestamp on fork contention
// ordering for thepid probably doesn't matter s = thepool ^ rdtsc();
thepid = p1;
} }
// lemur64 pseudorandom number generator // blend the pid on startup and fork contention
s2 *= 15750249268501108917ull; s ^= __pid;
// sadly 128-bit values aren't atomic on x86 thepid = __pid;
_lockcmpxchg16b(&thepool, &s1, s2); }
// do it again if there's thread contention thepool = (s *= 15750249268501108917ull); // lemur64
} while (_lockxadd(&thecount, 1) != c1); cthread_spunlock(&rand64_lock);
// the most important step in the prng return s >> 64;
return s2 >> 64;
} }

View file

@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h" #include "libc/bits/asmflag.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/nexgen32e/x86feature.h" #include "libc/nexgen32e/x86feature.h"
#include "libc/rand/rand.h" #include "libc/rand/rand.h"

View file

@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h" #include "libc/bits/asmflag.h"
#include "libc/nexgen32e/x86feature.h" #include "libc/nexgen32e/x86feature.h"
#include "libc/rand/rand.h" #include "libc/rand/rand.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"

View file

@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the any purpose with or without fee is hereby granted, provided that the
@ -16,22 +16,32 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/internal.h" #include "libc/calls/calls.h"
#include "libc/calls/typedef/sigaction_f.h" #include "libc/calls/sig.internal.h"
#include "libc/calls/sigbits.h"
#include "libc/calls/struct/sigset.h"
#include "libc/dce.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/sig.h"
textwindows wontreturn void sys_abort_nt(void) { /**
int rva; * Terminates program abnormally.
siginfo_t info; *
bzero(&info, sizeof(info)); * This function first tries to trigger your SIGABRT handler. If
info.si_signo = SIGABRT; * there isn't one or execution resumes, then abort() terminates
rva = __sighandrvas[SIGABRT]; * the program using an escalating variety methods of increasing
if (rva >= kSigactionMinRva) { * brutality.
if (((sigaction_f)(_base + rva))) { *
((sigaction_f)(_base + rva))(SIGABRT, &info, NULL); * @asyncsignalsafe
} * @noreturn
} */
privileged void abort(void) {
sigset_t sm;
sigfillset(&sm);
sigdelset(&sm, SIGABRT);
sigprocmask(SIG_SETMASK, &sm, 0);
raise(SIGABRT);
__restorewintty();
_Exit(128 + SIGABRT); _Exit(128 + SIGABRT);
} }

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/asmflag.h"
#include "libc/bits/bits.h" #include "libc/bits/bits.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/dce.h" #include "libc/dce.h"
@ -113,12 +114,14 @@ static int arch_prctl_freebsd(int code, int64_t addr) {
static privileged dontinline int arch_prctl_xnu(int code, int64_t addr) { static privileged dontinline int arch_prctl_xnu(int code, int64_t addr) {
int ax; int ax;
bool failed;
switch (code) { switch (code) {
case ARCH_SET_GS: case ARCH_SET_GS:
asm volatile("syscall" asm volatile(CFLAG_ASM("syscall")
: "=a"(ax) : CFLAG_CONSTRAINT(failed), "=a"(ax)
: "0"(0x3000003), "D"(addr - 0x8a0 /* wat */) : "1"(0x3000003), "D"(addr - 0x8a0 /* wat */)
: "rcx", "r11", "memory", "cc"); : "rcx", "r11", "memory", "cc");
if (failed) errno = ax, ax = -1;
return ax; return ax;
case ARCH_GET_FS: case ARCH_GET_FS:
case ARCH_SET_FS: case ARCH_SET_FS:
@ -130,21 +133,30 @@ static privileged dontinline int arch_prctl_xnu(int code, int64_t addr) {
} }
static privileged dontinline int arch_prctl_openbsd(int code, int64_t addr) { static privileged dontinline int arch_prctl_openbsd(int code, int64_t addr) {
bool failed;
int64_t rax; int64_t rax;
switch (code) { switch (code) {
case ARCH_GET_FS: case ARCH_GET_FS:
asm volatile("syscall" asm volatile(CFLAG_ASM("syscall")
: "=a"(rax) : CFLAG_CONSTRAINT(failed), "=a"(rax)
: "0"(0x014a /* __get_tcb */) : "1"(0x014a /* __get_tcb */)
: "rcx", "r11", "cc", "memory"); : "rcx", "r11", "cc", "memory");
if (failed) {
errno = rax;
return -1;
}
*(int64_t *)addr = rax; *(int64_t *)addr = rax;
return 0; return 0;
case ARCH_SET_FS: case ARCH_SET_FS:
asm volatile("syscall" asm volatile(CFLAG_ASM("syscall")
: "=a"(rax) : CFLAG_CONSTRAINT(failed), "=a"(rax)
: "0"(0x0149 /* __set_tcb */), "D"(addr) : "1"(0x0149 /* __set_tcb */), "D"(addr)
: "rcx", "r11", "cc", "memory"); : "rcx", "r11", "cc", "memory");
return 0; if (failed) {
errno = rax;
rax = -1;
}
return rax;
case ARCH_GET_GS: case ARCH_GET_GS:
case ARCH_SET_GS: case ARCH_SET_GS:
return enosys(); return enosys();

View file

@ -16,8 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/intrin/lockxchg.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
/** /**

View file

@ -25,8 +25,10 @@
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/mem/alloca.h" #include "libc/mem/alloca.h"
#include "libc/nexgen32e/nt2sysv.h" #include "libc/nexgen32e/nt2sysv.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/filemapflags.h" #include "libc/nt/enum/filemapflags.h"
#include "libc/nt/enum/pageflags.h" #include "libc/nt/enum/pageflags.h"
#include "libc/nt/enum/processcreationflags.h"
#include "libc/nt/enum/startf.h" #include "libc/nt/enum/startf.h"
#include "libc/nt/ipc.h" #include "libc/nt/ipc.h"
#include "libc/nt/memory.h" #include "libc/nt/memory.h"
@ -49,6 +51,7 @@ extern unsigned char __data_start[]; /* αpε */
extern unsigned char __data_end[]; /* αpε */ extern unsigned char __data_end[]; /* αpε */
extern unsigned char __bss_start[]; /* αpε */ extern unsigned char __bss_start[]; /* αpε */
extern unsigned char __bss_end[]; /* αpε */ extern unsigned char __bss_end[]; /* αpε */
bool32 __onntconsoleevent_nt(uint32_t);
static textwindows char16_t *ParseInt(char16_t *p, int64_t *x) { static textwindows char16_t *ParseInt(char16_t *p, int64_t *x) {
*x = 0; *x = 0;
@ -219,6 +222,9 @@ textwindows void WinMainForked(void) {
if (weaken(__wincrash_nt)) { if (weaken(__wincrash_nt)) {
AddVectoredExceptionHandler(1, (void *)weaken(__wincrash_nt)); AddVectoredExceptionHandler(1, (void *)weaken(__wincrash_nt));
} }
if (weaken(__onntconsoleevent_nt)) {
SetConsoleCtrlHandler(weaken(__onntconsoleevent_nt), 1);
}
longjmp(jb, 1); longjmp(jb, 1);
} }
@ -257,7 +263,8 @@ textwindows int sys_fork_nt(void) {
} }
#endif #endif
if (ntspawn(GetProgramExecutableName(), args, environ, forkvar, if (ntspawn(GetProgramExecutableName(), args, environ, forkvar,
&kNtIsInheritable, NULL, true, 0, NULL, &startinfo, &kNtIsInheritable, NULL, true,
0 /* kNtCreateNewProcessGroup */, NULL, &startinfo,
&procinfo) != -1) { &procinfo) != -1) {
CloseHandle(reader); CloseHandle(reader);
CloseHandle(procinfo.hThread); CloseHandle(procinfo.hThread);

View file

@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h" #include "libc/intrin/lockxchg.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"

View file

@ -16,8 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.internal.h" #include "libc/bits/safemacros.internal.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/kprintf.h" #include "libc/intrin/kprintf.h"
#include "libc/log/libfatal.internal.h" #include "libc/log/libfatal.internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
@ -84,7 +84,7 @@ privileged noinstrument noasan noubsan void ftracer(void) {
uint64_t stamp; uint64_t stamp;
static bool noreentry; static bool noreentry;
struct StackFrame *frame; struct StackFrame *frame;
if (!cmpxchg(&noreentry, 0, 1)) return; if (!_cmpxchg(&noreentry, 0, 1)) return;
if (ftrace_enabled && g_symbols) { if (ftrace_enabled && g_symbols) {
stamp = rdtsc(); stamp = rdtsc();
frame = __builtin_frame_address(0); frame = __builtin_frame_address(0);

View file

@ -12,7 +12,7 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
extern uint32_t __ntconsolemode[2]; extern uint32_t __ntconsolemode[3];
extern const char v_ntsubsystem[] __attribute__((__weak__)); extern const char v_ntsubsystem[] __attribute__((__weak__));
extern const uintptr_t __fini_array_end[] __attribute__((__weak__)); extern const uintptr_t __fini_array_end[] __attribute__((__weak__));
extern const uintptr_t __fini_array_start[] __attribute__((__weak__)); extern const uintptr_t __fini_array_start[] __attribute__((__weak__));

View file

@ -16,8 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/lockcmpxchg.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
typedef void *pthread_t; typedef void *pthread_t;
@ -25,7 +25,7 @@ typedef bool pthread_once_t;
typedef int pthread_mutex_t; typedef int pthread_mutex_t;
int pthread_once(pthread_once_t *once, void init(void)) { int pthread_once(pthread_once_t *once, void init(void)) {
if (lockcmpxchg(once, 0, 1)) init(); if (_lockcmpxchg(once, 0, 1)) init();
return 0; return 0;
} }

View file

@ -80,15 +80,17 @@ struct WinArgs {
extern int __pid; extern int __pid;
extern bool __nomultics; extern bool __nomultics;
extern uint32_t __winmainpid; extern uint32_t __winmainpid;
extern const char kConsoleHandles[2]; extern const char kConsoleHandles[3];
static const short kConsoleModes[2] = { static const short kConsoleModes[3] = {
kNtEnableProcessedInput | kNtEnableLineInput | kNtEnableEchoInput | kNtEnableProcessedInput | kNtEnableLineInput | kNtEnableEchoInput |
kNtEnableMouseInput | kNtEnableQuickEditMode | kNtEnableExtendedFlags | kNtEnableMouseInput | kNtEnableQuickEditMode | kNtEnableExtendedFlags |
kNtEnableAutoPosition | kNtEnableInsertMode | kNtEnableAutoPosition | kNtEnableInsertMode |
kNtEnableVirtualTerminalInput, kNtEnableVirtualTerminalInput,
kNtEnableProcessedOutput | kNtEnableWrapAtEolOutput | kNtEnableProcessedOutput | kNtEnableWrapAtEolOutput |
kNtEnableVirtualTerminalProcessing, kNtEnableVirtualTerminalProcessing,
kNtEnableProcessedOutput | kNtEnableWrapAtEolOutput |
kNtEnableVirtualTerminalProcessing,
}; };
forceinline void MakeLongDoubleLongAgain(void) { forceinline void MakeLongDoubleLongAgain(void) {
@ -128,7 +130,7 @@ static noasan textwindows wontreturn noinstrument void WinMainNew(
STRACE("SetConsoleCP(kNtCpUtf8) → %hhhd", rc); STRACE("SetConsoleCP(kNtCpUtf8) → %hhhd", rc);
rc = SetConsoleOutputCP(kNtCpUtf8); rc = SetConsoleOutputCP(kNtCpUtf8);
STRACE("SetConsoleOutputCP(kNtCpUtf8) → %hhhd", rc); STRACE("SetConsoleOutputCP(kNtCpUtf8) → %hhhd", rc);
for (i = 0; i < 2; ++i) { for (i = 0; i < 3; ++i) {
hand = GetStdHandle(kConsoleHandles[i]); hand = GetStdHandle(kConsoleHandles[i]);
rc = GetConsoleMode(hand, __ntconsolemode + i); rc = GetConsoleMode(hand, __ntconsolemode + i);
STRACE("GetConsoleMode(%p, [%#x]) → %hhhd", hand, __ntconsolemode[i], rc); STRACE("GetConsoleMode(%p, [%#x]) → %hhhd", hand, __ntconsolemode[i], rc);

View file

@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h" #include "libc/intrin/lockxchg.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"
/** /**

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/macros.internal.inc"
.scall setpgid,0x052052052205206d,globl

View file

@ -0,0 +1,2 @@
.include "o/libc/sysv/macros.internal.inc"
.scall sys_setpgid,0x052052052205206d,globl,hidden

View file

@ -149,7 +149,7 @@ scall getpgrp 0x051051051205106f globl
scall sys_setsid 0x0930930932093070 globl hidden scall sys_setsid 0x0930930932093070 globl hidden
scall sys_getsid 0x11e0ff136213607c globl hidden scall sys_getsid 0x11e0ff136213607c globl hidden
scall sys_getpgid 0x0cf0cf0cf2097079 globl hidden scall sys_getpgid 0x0cf0cf0cf2097079 globl hidden
scall setpgid 0x052052052205206d globl scall sys_setpgid 0x052052052205206d globl hidden
scall geteuid 0xfff019019201906b globl scall geteuid 0xfff019019201906b globl
scall getegid 0xfff02b02b202b06c globl scall getegid 0xfff02b02b202b06c globl
scall getgroups 0x04f04f04f204f073 globl scall getgroups 0x04f04f04f204f073 globl

View file

@ -0,0 +1,112 @@
#ifndef COSMOPOLITAN_LIBC_THREAD_FREEBSD_INTERNAL_H_
#define COSMOPOLITAN_LIBC_THREAD_FREEBSD_INTERNAL_H_
#include "libc/bits/asmflag.h"
#include "libc/calls/struct/timespec.h"
#include "libc/errno.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/**
* @fileoverview FreeBSD Threading
*
* @note even though FreeBSD uses a 64-bit type for thread IDs the
* maximum legal range is PID_MAX + 2 (100001) through INT_MAX
*/
struct rtprio {
uint16_t type; /* scheduling class */
uint16_t prio;
};
struct thr_param {
void (*start_func)(void *);
void *arg;
char *stack_base;
uint64_t stack_size;
char *tls_base;
uint64_t tls_size;
int64_t *child_tid;
int64_t *parent_tid;
int32_t flags;
struct rtprio *rtp;
};
static inline wontreturn void thr_exit(int64_t *opt_out_state) {
long ax, di;
asm volatile("syscall"
: "=a"(ax)
: "0"(431), "D"(opt_out_state)
: "memory", "cc");
unreachable;
}
static inline int thr_new(struct thr_param *param, int param_size) {
bool failed;
long ax, di, si;
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(failed), "=a"(ax), "=D"(di), "=S"(si)
: "1"(455), "2"(param), "3"(param_size)
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory");
if (failed) ax = -ax;
return ax;
}
static inline int thr_kill(int64_t id, int sig) {
bool failed;
long ax, di, si;
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(failed), "=a"(ax), "=D"(di), "=S"(si)
: "1"(433), "2"(id), "3"(sig)
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory");
if (failed) ax = -ax;
return ax;
}
static inline int thr_suspend(const struct timespec *opt_timeout) {
bool failed;
long ax, di;
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(failed), "=a"(ax), "=D"(di)
: "1"(442), "2"(opt_timeout)
: "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory");
if (failed) ax = -ax;
return ax;
}
static inline int thr_wake(int64_t id) {
bool failed;
long ax, di;
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(failed), "=a"(ax), "=D"(di)
: "1"(443), "2"(id)
: "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory");
if (failed) ax = -ax;
return ax;
}
static inline int thr_set_name(int64_t id, const char *name) {
bool failed;
long ax, di, si;
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(failed), "=a"(ax), "=D"(di), "=S"(si)
: "1"(464), "2"(id), "3"(name)
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory");
if (failed) ax = -ax;
return ax;
}
static inline int thr_kill2(int pid, int64_t id, int sig) {
bool failed;
long ax, di, si, dx;
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(failed), "=a"(ax), "=D"(di), "=S"(si),
"=d"(dx)
: "1"(481), "2"(pid), "3"(id), "4"(sig)
: "rcx", "r8", "r9", "r10", "r11", "memory");
if (failed) ax = -ax;
return ax;
}
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_THREAD_FREEBSD_INTERNAL_H_ */

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/atomic.h" #include "libc/bits/atomic.h"
#include "libc/intrin/atomic_load.h"
#include "libc/thread/sem.h" #include "libc/thread/sem.h"
#include "libc/thread/wait.h" #include "libc/thread/wait.h"
#include "libc/thread/yield.h" #include "libc/thread/yield.h"
@ -37,6 +38,7 @@ int cthread_sem_init(cthread_sem_t* sem, int count) {
sem->linux.count = count; sem->linux.count = count;
return 0; return 0;
} }
int cthread_sem_destroy(cthread_sem_t* sem) { int cthread_sem_destroy(cthread_sem_t* sem) {
(void)sem; (void)sem;
return 0; return 0;
@ -44,7 +46,7 @@ int cthread_sem_destroy(cthread_sem_t* sem) {
int cthread_sem_signal(cthread_sem_t* sem) { int cthread_sem_signal(cthread_sem_t* sem) {
uint64_t count; uint64_t count;
asm volatile("lock xadd\t%1, %0" asm volatile("lock xadd\t%1,%0"
: "+m"(sem->linux.count), "=r"(count) : "+m"(sem->linux.count), "=r"(count)
: "1"(1) : "1"(1)
: "cc"); : "cc");
@ -62,7 +64,7 @@ int cthread_sem_wait_futex(cthread_sem_t* sem, const struct timespec* timeout) {
uint64_t count; uint64_t count;
// record current thread as waiter // record current thread as waiter
asm volatile("lock xadd\t%1, %0" asm volatile("lock xadd\t%1,%0"
: "+m"(sem->linux.count), "=r"(count) : "+m"(sem->linux.count), "=r"(count)
: "1"((uint64_t)1 << CTHREAD_THREAD_VAL_BITS) : "1"((uint64_t)1 << CTHREAD_THREAD_VAL_BITS)
: "cc"); : "cc");
@ -77,7 +79,7 @@ int cthread_sem_wait_futex(cthread_sem_t* sem, const struct timespec* timeout) {
return 0; return 0;
} }
} }
// WARNING: an offset of 4 bytes would be required on little-endian archs // WARNING: an offset of 4 bytes would be required on little-endian archs
void* wait_address = &sem->linux.count; void* wait_address = &sem->linux.count;
cthread_memory_wait32(wait_address, count, timeout); cthread_memory_wait32(wait_address, count, timeout);
@ -91,16 +93,17 @@ int cthread_sem_wait_spin(cthread_sem_t* sem, uint64_t count, int spin,
const struct timespec* timeout) { const struct timespec* timeout) {
// spin on pause // spin on pause
for (int attempt = 0; attempt < spin; ++attempt) { for (int attempt = 0; attempt < spin; ++attempt) {
//if ((count >> CTHREAD_THREAD_VAL_BITS) != 0) break; // if ((count >> CTHREAD_THREAD_VAL_BITS) != 0) break;
while ((uint32_t)count > 0) { while ((uint32_t)count > 0) {
// spin is useful if multiple waiters can acquire the semaphore at the same time // spin is useful if multiple waiters can acquire the semaphore at the
// same time
if (atomic_compare_exchange_weak(&sem->linux.count, count, count - 1)) { if (atomic_compare_exchange_weak(&sem->linux.count, count, count - 1)) {
return 0; return 0;
} }
} }
pause(attempt); pause(attempt);
} }
return cthread_sem_wait_futex(sem, timeout); return cthread_sem_wait_futex(sem, timeout);
} }
@ -110,11 +113,12 @@ int cthread_sem_wait(cthread_sem_t* sem, int spin,
// uncontended // uncontended
while ((uint32_t)count > 0) { while ((uint32_t)count > 0) {
// spin is useful if multiple waiters can acquire the semaphore at the same time // spin is useful if multiple waiters can acquire the semaphore at the same
// time
if (atomic_compare_exchange_weak(&sem->linux.count, count, count - 1)) { if (atomic_compare_exchange_weak(&sem->linux.count, count, count - 1)) {
return 0; return 0;
} }
} }
return cthread_sem_wait_spin(sem, count, spin, timeout); return cthread_sem_wait_spin(sem, count, spin, timeout);
} }

View file

@ -0,0 +1,25 @@
#ifndef COSMOPOLITAN_LIBC_THREAD_XNU_INTERNAL_H_
#define COSMOPOLITAN_LIBC_THREAD_XNU_INTERNAL_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/**
* XNU thread system calls.
* @see darwin-libpthread/kern/kern_support.c
*/
void *bsdthread_create(void *func, void *func_arg, void *stack, void *pthread,
uint32_t flags);
int bsdthread_terminate(void *stackaddr, size_t freesize, uint32_t port,
uint32_t sem);
int bsdthread_register(void *threadstart, void *wqthread, uint32_t flags,
void *stack_addr_hint, void *targetconc_ptr,
uint32_t dispatchqueue_offset, uint32_t tsd_offset);
int bsdthread_ctl(void *cmd, void *arg1, void *arg2, void *arg3);
uint64_t thread_selfid(void);
uint64_t thread_selfusage(void);
int thread_selfcounts(int type, void *buf, uint64_t nbytes);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_THREAD_XNU_INTERNAL_H_ */

View file

@ -1,5 +1,5 @@
#ifndef COSMOPOLITAN_TOOL_PLINKO_LIB_TIME_H_ #ifndef COSMOPOLITAN_LIBC_TIME_CLOCKSTONANOS_INTERNAL_H_
#define COSMOPOLITAN_TOOL_PLINKO_LIB_TIME_H_ #define COSMOPOLITAN_LIBC_TIME_CLOCKSTONANOS_INTERNAL_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
@ -12,4 +12,4 @@ static inline uint64_t ClocksToNanos(uint64_t x, uint64_t y) {
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_PLINKO_LIB_TIME_H_ */ #endif /* COSMOPOLITAN_LIBC_TIME_CLOCKSTONANOS_INTERNAL_H_ */

View file

@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h" #include "libc/intrin/lockcmpxchg.h"
#include "libc/log/check.h" #include "libc/log/check.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/x/x.h" #include "libc/x/x.h"
@ -58,7 +58,7 @@ void *xload(bool *o, void **t, const void *p, size_t n, size_t m) {
zs.next_out = (void *)q; zs.next_out = (void *)q;
inflateInit2(&zs, -MAX_WBITS); inflateInit2(&zs, -MAX_WBITS);
inflate(&zs, Z_NO_FLUSH); inflate(&zs, Z_NO_FLUSH);
if (lockcmpxchg(t, 0, q)) { if (_lockcmpxchg(t, 0, q)) {
__cxa_atexit(free, q, 0); __cxa_atexit(free, q, 0);
} else { } else {
free(q); free(q);

View file

@ -17,8 +17,8 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/assert.h" #include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/fmt/leb128.h" #include "libc/fmt/leb128.h"
#include "libc/intrin/lockcmpxchg.h"
#include "libc/nexgen32e/crc32.h" #include "libc/nexgen32e/crc32.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/x/x.h" #include "libc/x/x.h"
@ -69,7 +69,7 @@ void *xloadzd(bool *o, void **t, const void *p, size_t n, size_t m, size_t c,
} }
free(q); free(q);
assert(crc32_z(0, r, c * z) == s); assert(crc32_z(0, r, c * z) == s);
if (lockcmpxchg(t, 0, r)) { if (_lockcmpxchg(t, 0, r)) {
__cxa_atexit(free, r, 0); __cxa_atexit(free, r, 0);
} else { } else {
free(q); free(q);

View file

@ -21,6 +21,7 @@
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h" #include "libc/calls/struct/stat.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/lockcmpxchg.h"
#include "libc/log/check.h" #include "libc/log/check.h"
#include "libc/log/log.h" #include "libc/log/log.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
@ -129,7 +130,7 @@ int UncacheSslSession(void *data, mbedtls_ssl_session *session) {
ts = time(0); ts = time(0);
if (!(e->time <= ts && ts <= e->time + cache->lifetime)) { if (!(e->time <= ts && ts <= e->time + cache->lifetime)) {
DEBUGF("%u sslcache expired", i); DEBUGF("%u sslcache expired", i);
lockcmpxchg(&e->tick, tick, 0); _lockcmpxchg(&e->tick, tick, 0);
return 1; return 1;
} }
cert = 0; cert = 0;
@ -199,7 +200,7 @@ int CacheSslSession(void *data, const mbedtls_ssl_session *session) {
e->time = time(0); e->time = time(0);
tick = rdtsc(); tick = rdtsc();
asm volatile("" ::: "memory"); asm volatile("" ::: "memory");
if (tick && lockcmpxchg(&e->pid, pid, 0)) { if (tick && _lockcmpxchg(&e->pid, pid, 0)) {
DEBUGF("%u saved %s%s %`#.*s", i, DEBUGF("%u saved %s%s %`#.*s", i,
mbedtls_ssl_get_ciphersuite_name(session->ciphersuite), mbedtls_ssl_get_ciphersuite_name(session->ciphersuite),
session->compression ? " DEFLATE" : "", session->id_len, session->compression ? " DEFLATE" : "", session->id_len,

View file

@ -23,6 +23,7 @@
#include "libc/calls/ucontext.h" #include "libc/calls/ucontext.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/sig.h"
@ -39,37 +40,10 @@ void OnSigInt(int sig) {
void SetUp(void) { void SetUp(void) {
gotsigint = false; gotsigint = false;
/* TODO(jart): Windows needs huge signal overhaul */
if (IsWindows()) exit(0);
} }
TEST(sigaction, test) { ////////////////////////////////////////////////////////////////////////////////
int pid, status; // test raise()
sigset_t block, ignore, oldmask;
struct sigaction saint = {.sa_handler = OnSigInt};
sigemptyset(&block);
sigaddset(&block, SIGINT);
EXPECT_NE(-1, sigprocmask(SIG_BLOCK, &block, &oldmask));
sigfillset(&ignore);
sigdelset(&ignore, SIGINT);
EXPECT_NE(-1, sigaction(SIGINT, &saint, &oldsa));
ASSERT_NE(-1, (pid = fork()));
if (!pid) {
EXPECT_NE(-1, kill(getppid(), SIGINT));
EXPECT_EQ(-1, sigsuspend(&ignore));
EXPECT_EQ(EINTR, errno);
EXPECT_TRUE(gotsigint);
_exit(0);
}
EXPECT_EQ(-1, sigsuspend(&ignore));
EXPECT_NE(-1, kill(pid, SIGINT));
EXPECT_NE(-1, waitpid(pid, &status, 0));
EXPECT_EQ(1, WIFEXITED(status));
EXPECT_EQ(0, WEXITSTATUS(status));
EXPECT_EQ(0, WTERMSIG(status));
EXPECT_NE(-1, sigprocmask(SIG_SETMASK, &oldmask, NULL));
EXPECT_NE(-1, sigaction(SIGINT, &oldsa, NULL));
}
TEST(sigaction, raise) { TEST(sigaction, raise) {
struct sigaction saint = {.sa_handler = OnSigInt}; struct sigaction saint = {.sa_handler = OnSigInt};
@ -80,6 +54,58 @@ TEST(sigaction, raise) {
EXPECT_NE(-1, sigaction(SIGINT, &oldsa, NULL)); EXPECT_NE(-1, sigaction(SIGINT, &oldsa, NULL));
} }
////////////////////////////////////////////////////////////////////////////////
// test kill()
TEST(sigaction, testPingPongParentChildWithSigint) {
int pid, status;
sigset_t blockint, oldmask;
struct sigaction oldint;
struct sigaction ignoreint = {.sa_handler = SIG_IGN};
struct sigaction catchint = {.sa_handler = OnSigInt};
if (IsWindows()) {
// this works if it's run by itself on the command prompt. but it
// doesn't currently work if it's launched as a subprocess of some
// kind of runner. todo(fixme!)
return;
}
EXPECT_NE(-1, sigemptyset(&blockint));
EXPECT_NE(-1, sigaddset(&blockint, SIGINT));
EXPECT_NE(-1, sigprocmask(SIG_BLOCK, &blockint, &oldmask));
EXPECT_NE(-1, sigaction(SIGINT, &catchint, &oldint));
ASSERT_NE(-1, (pid = fork()));
if (!pid) {
// ping
EXPECT_NE(-1, kill(getppid(), SIGINT));
EXPECT_FALSE(gotsigint);
// pong
EXPECT_NE(-1, sigaction(SIGINT, &catchint, 0));
EXPECT_EQ(-1, sigsuspend(0));
EXPECT_EQ(EINTR, errno);
EXPECT_TRUE(gotsigint);
_exit(0);
}
// pong
EXPECT_FALSE(gotsigint);
EXPECT_NE(-1, sigaction(SIGINT, &catchint, 0));
EXPECT_EQ(-1, sigsuspend(0));
EXPECT_TRUE(gotsigint);
// ping
EXPECT_NE(-1, sigaction(SIGINT, &ignoreint, 0));
EXPECT_NE(-1, kill(pid, SIGINT));
// cleanup
EXPECT_NE(-1, wait4(pid, &status, 0, 0));
EXPECT_EQ(1, WIFEXITED(status));
EXPECT_EQ(0, WEXITSTATUS(status));
EXPECT_EQ(0, WTERMSIG(status));
EXPECT_NE(-1, sigaction(SIGINT, &oldint, 0));
EXPECT_NE(-1, sigprocmask(SIG_BLOCK, &oldmask, 0));
}
////////////////////////////////////////////////////////////////////////////////
// test int3 crash
// we expect this to be recoverable by default
volatile int trapeax; volatile int trapeax;
void OnTrap(int sig, struct siginfo *si, struct ucontext *ctx) { void OnTrap(int sig, struct siginfo *si, struct ucontext *ctx) {
@ -94,6 +120,10 @@ TEST(sigaction, debugBreak_handlerCanReadCpuState) {
EXPECT_NE(-1, sigaction(SIGTRAP, &oldsa, NULL)); EXPECT_NE(-1, sigaction(SIGTRAP, &oldsa, NULL));
} }
////////////////////////////////////////////////////////////////////////////////
// test fpu crash (unrecoverable)
// test signal handler can modify cpu registers (now it's recoverable!)
void OnFpe(int sig, struct siginfo *si, struct ucontext *ctx) { void OnFpe(int sig, struct siginfo *si, struct ucontext *ctx) {
struct XedDecodedInst xedd; struct XedDecodedInst xedd;
xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_LONG_64); xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_LONG_64);

View file

@ -24,13 +24,12 @@
#include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/sig.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
testonly void OnCtrlC(int sig) { testonly void OnUsr1(int sig) {
_exit(0); _exit(0);
} }
TEST(signal, test) { TEST(signal, test) {
if (IsWindows()) return; /* omg */ ASSERT_NE(SIG_ERR, signal(SIGUSR1, OnUsr1));
ASSERT_NE(SIG_ERR, signal(SIGINT, OnCtrlC)); ASSERT_NE(-1, raise(SIGUSR1));
ASSERT_NE(-1, raise(SIGINT));
__die(); __die();
} }

View file

@ -23,11 +23,12 @@
#include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.h"
#include "libc/calls/ucontext.h" #include "libc/calls/ucontext.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/sig.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
int n; volatile int n;
void OnSig(int sig, siginfo_t *si, ucontext_t *ctx) { void OnSig(int sig, siginfo_t *si, ucontext_t *ctx) {
++n; ++n;

Some files were not shown because too many files have changed in this diff Show more