Compare commits

..

No commits in common. "master" and "3.9.1" have entirely different histories.

757 changed files with 8456 additions and 16730 deletions

View file

@ -1,8 +1,5 @@
name: build
env:
COSMOCC_VERSION: 3.9.2
on:
push:
branches:
@ -22,48 +19,13 @@ jobs:
matrix:
mode: ["", tiny, rel, tinylinux, optlinux]
steps:
- uses: actions/checkout@v4
with:
# Full checkout needed for git-restore-mtime-bare.
fetch-depth: 0
# TODO(jart): fork this action.
- uses: chetan/git-restore-mtime-action@v2
- uses: actions/cache/restore@v4
id: cache
with:
path: |
.cosmocc
o
key: ${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-${{ github.sha }}
restore-keys: |
${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-
${{ env.COSMOCC_VERSION }}-
- name: Restore mtimes
if: steps.cache.outputs.cache-hit == 'true'
run: |
while read mtime file; do
[ -f "$file" ] && touch -d "@$mtime" "$file"
done < o/.mtimes
- uses: actions/checkout@v3
- name: support ape bins 1
run: sudo cp -a build/bootstrap/ape.elf /usr/bin/ape
run: sudo cp build/bootstrap/ape.elf /usr/bin/ape
- name: support ape bins 2
run: sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"
- name: make matrix
run: V=0 make -j2 MODE=${{ matrix.mode }}
- name: Save mtimes
run: |
find o -type f -exec stat -c "%Y %n" {} \; > o/.mtimes
- uses: actions/cache/save@v4
with:
path: |
.cosmocc
o
key: ${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-${{ github.sha }}

1
.gitignore vendored
View file

@ -15,4 +15,3 @@ __pycache__
/tool/emacs/*.elc
/perf.data
/perf.data.old
/qemu*core

View file

@ -1,5 +1,5 @@
{
"C_Cpp.default.compilerPath": ".cosmocc/3.9.2/bin/aarch64-linux-cosmo-c++",
"C_Cpp.default.compilerPath": ".cosmocc/3.8.0/bin/aarch64-linux-cosmo-c++",
"C_Cpp.default.compilerArgs": [
"-nostdinc",
"-nostdlib",

126
Makefile
View file

@ -77,8 +77,7 @@ COMMA := ,
PWD := $(shell pwd)
# detect wsl2 running cosmopolitan binaries on the host by checking whether:
# - user ran .cosmocc/current/bin/make, in which case make's working directory
# is in wsl
# - user ran build/bootstrap/make, in which case make's working directory is in wsl
# - user ran make, in which case cocmd's working directory is in wsl
ifneq ($(findstring //wsl.localhost/,$(CURDIR) $(PWD)),)
$(warning wsl2 interop is enabled)
@ -90,7 +89,7 @@ UNAME_S := $(shell uname -s)
# apple still distributes a 17 year old version of gnu make
ifeq ($(MAKE_VERSION), 3.81)
$(error please use https://cosmo.zip/pub/cosmos/bin/make)
$(error please use build/bootstrap/make)
endif
LC_ALL = C
@ -136,7 +135,7 @@ ARCH = aarch64
HOSTS ?= pi pi5 studio freebsdarm
else
ARCH = x86_64
HOSTS ?= freebsd rhel7 xnu openbsd netbsd win10 luna
HOSTS ?= freebsd rhel7 xnu openbsd netbsd win10
endif
ZIPOBJ_FLAGS += -a$(ARCH)
@ -148,10 +147,10 @@ export MODE
export SOURCE_DATE_EPOCH
export TMPDIR
COSMOCC = .cosmocc/3.9.2
COSMOCC = .cosmocc/3.8.0
BOOTSTRAP = $(COSMOCC)/bin
TOOLCHAIN = $(COSMOCC)/bin/$(ARCH)-linux-cosmo-
DOWNLOAD := $(shell build/download-cosmocc.sh $(COSMOCC) 3.9.2 f4ff13af65fcd309f3f1cfd04275996fb7f72a4897726628a8c9cf732e850193)
DOWNLOAD := $(shell build/download-cosmocc.sh $(COSMOCC) 3.8.0 813c6b2f95062d2e0a845307a79505424cb98cb038e8013334f8a22e3b92a474)
IGNORE := $(shell $(MKDIR) $(TMPDIR))
@ -275,16 +274,11 @@ include libc/BUILD.mk #─┘
include libc/sock/BUILD.mk #─┐
include net/http/BUILD.mk # ├──ONLINE RUNTIME
include third_party/musl/BUILD.mk # │ You can communicate with the network
include third_party/regex/BUILD.mk # │
include third_party/tr/BUILD.mk # │
include third_party/sed/BUILD.mk # │
include libc/system/BUILD.mk # │
include libc/x/BUILD.mk # │
include dsp/scale/BUILD.mk # │
include dsp/mpeg/BUILD.mk # │
include dsp/tty/BUILD.mk # │
include dsp/audio/BUILD.mk # │
include dsp/prog/BUILD.mk # │
include dsp/BUILD.mk # │
include third_party/stb/BUILD.mk # │
include third_party/mbedtls/BUILD.mk # │
@ -298,7 +292,8 @@ include third_party/libcxx/BUILD.mk # │
include third_party/openmp/BUILD.mk # │
include third_party/pcre/BUILD.mk # │
include third_party/less/BUILD.mk # │
include net/https/BUILD.mk #─┘
include net/https/BUILD.mk # │
include third_party/regex/BUILD.mk #─┘
include third_party/tidy/BUILD.mk
include third_party/BUILD.mk
include third_party/nsync/testing/BUILD.mk
@ -317,6 +312,8 @@ include third_party/double-conversion/test/BUILD.mk
include third_party/lua/BUILD.mk
include third_party/tree/BUILD.mk
include third_party/zstd/BUILD.mk
include third_party/tr/BUILD.mk
include third_party/sed/BUILD.mk
include third_party/awk/BUILD.mk
include third_party/hiredis/BUILD.mk
include third_party/make/BUILD.mk
@ -369,7 +366,6 @@ include test/libc/fmt/BUILD.mk
include test/libc/time/BUILD.mk
include test/libc/proc/BUILD.mk
include test/libc/stdio/BUILD.mk
include test/libc/system/BUILD.mk
include test/libc/BUILD.mk
include test/net/http/BUILD.mk
include test/net/https/BUILD.mk
@ -436,64 +432,64 @@ loc: o/$(MODE)/tool/build/summy
find -name \*.h -or -name \*.hpp -or -name \*.c -or -name \*.cc -or -name \*.cpp -or -name \*.S -or -name \*.mk | \
$(XARGS) wc -l | grep total | awk '{print $$1}' | $<
COSMOPOLITAN = \
# PLEASE: MAINTAIN TOPOLOGICAL ORDER
# FROM HIGHEST LEVEL TO LOWEST LEVEL
COSMOPOLITAN_OBJECTS = \
CTL \
DSP_AUDIO \
LIBC_CALLS \
LIBC_DLOPEN \
LIBC_ELF \
LIBC_FMT \
LIBC_INTRIN \
LIBC_IRQ \
LIBC_LOG \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_NT_ADVAPI32 \
LIBC_NT_BCRYPTPRIMITIVES \
LIBC_NT_COMDLG32 \
LIBC_NT_GDI32 \
LIBC_NT_IPHLPAPI \
LIBC_NT_KERNEL32 \
LIBC_NT_NTDLL \
LIBC_NT_PDH \
LIBC_NT_POWRPROF \
LIBC_NT_PSAPI \
LIBC_NT_REALTIME \
LIBC_NT_SHELL32 \
LIBC_NT_SYNCHRONIZATION \
LIBC_NT_USER32 \
LIBC_NT_WS2_32 \
LIBC_PROC \
LIBC_RUNTIME \
LIBC_SOCK \
LIBC_STDIO \
LIBC_STR \
LIBC_SYSTEM \
LIBC_SYSV \
LIBC_SYSV_CALLS \
LIBC_THREAD \
LIBC_TINYMATH \
LIBC_VGA \
LIBC_X \
NET_HTTP \
THIRD_PARTY_COMPILER_RT \
THIRD_PARTY_DLMALLOC \
THIRD_PARTY_DOUBLECONVERSION \
THIRD_PARTY_GDTOA \
THIRD_PARTY_OPENMP \
TOOL_ARGS \
NET_HTTP \
DSP_AUDIO \
LIBC_SOCK \
LIBC_NT_WS2_32 \
LIBC_NT_IPHLPAPI \
LIBC_X \
THIRD_PARTY_GETOPT \
LIBC_LOG \
THIRD_PARTY_TZ \
THIRD_PARTY_MUSL \
THIRD_PARTY_ZLIB_GZ \
THIRD_PARTY_LIBCXXABI \
THIRD_PARTY_LIBUNWIND \
THIRD_PARTY_MUSL \
THIRD_PARTY_NSYNC \
THIRD_PARTY_NSYNC_MEM \
THIRD_PARTY_OPENMP \
THIRD_PARTY_PUFF \
LIBC_STDIO \
THIRD_PARTY_GDTOA \
THIRD_PARTY_REGEX \
THIRD_PARTY_TZ \
THIRD_PARTY_XED \
LIBC_THREAD \
LIBC_PROC \
THIRD_PARTY_NSYNC_MEM \
LIBC_MEM \
THIRD_PARTY_DLMALLOC \
LIBC_DLOPEN \
LIBC_RUNTIME \
THIRD_PARTY_NSYNC \
LIBC_ELF \
LIBC_IRQ \
LIBC_CALLS \
LIBC_SYSV_CALLS \
LIBC_VGA \
LIBC_NT_REALTIME \
LIBC_NT_PSAPI \
LIBC_NT_POWRPROF \
LIBC_NT_PDH \
LIBC_NT_GDI32 \
LIBC_NT_COMDLG32 \
LIBC_NT_USER32 \
LIBC_NT_NTDLL \
LIBC_NT_ADVAPI32 \
LIBC_NT_SYNCHRONIZATION \
LIBC_FMT \
THIRD_PARTY_ZLIB \
THIRD_PARTY_ZLIB_GZ \
TOOL_ARGS \
THIRD_PARTY_PUFF \
THIRD_PARTY_COMPILER_RT \
LIBC_TINYMATH \
THIRD_PARTY_XED \
LIBC_STR \
LIBC_SYSV \
LIBC_INTRIN \
LIBC_NT_BCRYPTPRIMITIVES \
LIBC_NT_KERNEL32 \
LIBC_NEXGEN32E
COSMOPOLITAN_H_PKGS = \
APE \
@ -541,7 +537,7 @@ COSMOCC_PKGS = \
THIRD_PARTY_INTEL
o/$(MODE)/cosmopolitan.a: \
$(call reverse,$(call uniq,$(foreach x,$(COSMOPOLITAN),$($(x)))))
$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_A_OBJS))
COSMOCC_HDRS = \
$(wildcard libc/integral/*) \

View file

@ -3,12 +3,12 @@
[![build](https://github.com/jart/cosmopolitan/actions/workflows/build.yml/badge.svg)](https://github.com/jart/cosmopolitan/actions/workflows/build.yml)
# Cosmopolitan
[Cosmopolitan Libc](https://justine.lol/cosmopolitan/index.html) makes C/C++
[Cosmopolitan Libc](https://justine.lol/cosmopolitan/index.html) makes C
a build-once run-anywhere language, like Java, except it doesn't need an
interpreter or virtual machine. Instead, it reconfigures stock GCC and
Clang to output a POSIX-approved polyglot format that runs natively on
Linux + Mac + Windows + FreeBSD + OpenBSD 7.3 + NetBSD + BIOS with the
best possible performance and the tiniest footprint imaginable.
Linux + Mac + Windows + FreeBSD + OpenBSD + NetBSD + BIOS with the best
possible performance and the tiniest footprint imaginable.
## Background
@ -87,22 +87,15 @@ ape/apeinstall.sh
```
You can now build the mono repo with any modern version of GNU Make. To
bootstrap your build, you can install Cosmopolitan Make from this site:
https://cosmo.zip/pub/cosmos/bin/make
E.g.:
make life easier, we've included one in the cosmocc toolchain, which is
guaranteed to be compatible and furthermore includes our extensions for
doing build system sandboxing.
```sh
curl -LO https://cosmo.zip/pub/cosmos/bin/make
./make -j8
build/bootstrap/make -j8
o//examples/hello
```
After you've built the repo once, you can also use the make from your
cosmocc at `.cosmocc/current/bin/make`. You might even prefer to alias
make to `$COSMO/.cosmocc/current/bin/make`.
Since the Cosmopolitan repository is very large, you might only want to
build one particular thing. Here's an example of a target that can be
compiled relatively quickly, which is a simple POSIX test that only
@ -110,7 +103,7 @@ depends on core LIBC packages.
```sh
rm -rf o//libc o//test
.cosmocc/current/bin/make o//test/posix/signal_test
build/bootstrap/make o//test/posix/signal_test
o//test/posix/signal_test
```
@ -119,21 +112,21 @@ list out each individual one. For example if you wanted to build and run
all the unit tests in the `TEST_POSIX` package, you could say:
```sh
.cosmocc/current/bin/make o//test/posix
build/bootstrap/make o//test/posix
```
Cosmopolitan provides a variety of build modes. For example, if you want
really tiny binaries (as small as 12kb in size) then you'd say:
```sh
.cosmocc/current/bin/make m=tiny
build/bootstrap/make m=tiny
```
You can furthermore cut out the bloat of other operating systems, and
have Cosmopolitan become much more similar to Musl Libc.
```sh
.cosmocc/current/bin/make m=tinylinux
build/bootstrap/make m=tinylinux
```
For further details, see [//build/config.mk](build/config.mk).
@ -249,16 +242,16 @@ server. You're welcome to join us! <https://discord.gg/FwAVVu7eJ4>
## Support Vector
| Platform | Min Version | Circa |
| :--- | ---: | ---: |
| AMD | K8 | 2003 |
| Intel | Core | 2006 |
| Linux | 2.6.18 | 2007 |
| Windows | 8 [1] | 2012 |
| Darwin (macOS) | 23.1.0+ | 2023 |
| OpenBSD | 7.3 or earlier | 2023 |
| FreeBSD | 13 | 2020 |
| NetBSD | 9.2 | 2021 |
| Platform | Min Version | Circa |
| :--- | ---: | ---: |
| AMD | K8 | 2003 |
| Intel | Core | 2006 |
| Linux | 2.6.18 | 2007 |
| Windows | 8 [1] | 2012 |
| Darwin (macOS) | 23.1.0+ | 2023 |
| OpenBSD | 7 | 2021 |
| FreeBSD | 13 | 2020 |
| NetBSD | 9.2 | 2021 |
[1] See our [vista branch](https://github.com/jart/cosmopolitan/tree/vista)
for a community supported version of Cosmopolitan that works on Windows

View file

@ -259,9 +259,6 @@ SECTIONS {
.debug_ranges 0 : { *(.debug_ranges) }
.debug_macro 0 : { *(.debug_macro) }
.debug_addr 0 : { *(.debug_addr) }
.debug_names 0 : { *(.debug_names) }
.debug_loclists 0 : { *(.debug_loclists) }
.debug_str_offsets 0 : { *(.debug_str_offsets) }
.ARM.attributes 0 : { KEEP(*(.ARM.attributes)) KEEP(*(.gnu.attributes)) }
.note.gnu.arm.ident 0 : { KEEP(*(.note.gnu.arm.ident)) }

View file

@ -386,13 +386,6 @@ SECTIONS {
_tbss_end = .;
} :Tls
.eh_frame : {
__eh_frame_start = .;
KEEP(*(.eh_frame))
*(.eh_frame.*)
__eh_frame_end = .;
} :Ram
.data . : {
/*BEGIN: Read/Write Data */
#if SupportsWindows()
@ -433,6 +426,11 @@ SECTIONS {
KEEP(*(.dtors))
__fini_array_end = .;
__eh_frame_start = .;
KEEP(*(.eh_frame))
*(.eh_frame.*)
__eh_frame_end = .;
/*BEGIN: Post-Initialization Read-Only */
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
KEEP(*(SORT_BY_NAME(.piro.relo.sort.*)))
@ -441,6 +439,7 @@ SECTIONS {
KEEP(*(.piro.pad.data))
*(.igot.plt)
KEEP(*(.dataepilogue))
. = ALIGN(. != 0 ? CONSTANT(COMMONPAGESIZE) : 0);
/*END: NT FORK COPYING */
_edata = .;
@ -520,9 +519,6 @@ SECTIONS {
.debug_rnglists 0 : { *(.debug_rnglists) }
.debug_macro 0 : { *(.debug_macro) }
.debug_addr 0 : { *(.debug_addr) }
.debug_names 0 : { *(.debug_names) }
.debug_loclists 0 : { *(.debug_loclists) }
.debug_str_offsets 0 : { *(.debug_str_offsets) }
.gnu.attributes 0 : { KEEP(*(.gnu.attributes)) }
.GCC.command.line 0 : { *(.GCC.command.line) }
@ -586,11 +582,11 @@ ape_rom_memsz = ape_rom_filesz;
ape_rom_align = CONSTANT(COMMONPAGESIZE);
ape_rom_rva = RVA(ape_rom_vaddr);
ape_ram_vaddr = ADDR(.eh_frame);
ape_ram_vaddr = ADDR(.data);
ape_ram_offset = ape_ram_vaddr - __executable_start;
ape_ram_paddr = LOADADDR(.eh_frame);
ape_ram_filesz = ADDR(.bss) - ADDR(.eh_frame);
ape_ram_memsz = _end - ADDR(.eh_frame);
ape_ram_paddr = LOADADDR(.data);
ape_ram_filesz = ADDR(.bss) - ADDR(.data);
ape_ram_memsz = _end - ADDR(.data);
ape_ram_align = CONSTANT(COMMONPAGESIZE);
ape_ram_rva = RVA(ape_ram_vaddr);
@ -600,7 +596,7 @@ ape_stack_offset = 0;
ape_stack_vaddr = DEFINED(ape_stack_vaddr) ? ape_stack_vaddr : 0x700000000000;
ape_stack_paddr = ape_ram_paddr + ape_ram_filesz;
ape_stack_filesz = 0;
ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : 4 * 1024 * 1024;
ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : 8 * 1024 * 1024;
ape_note_offset = ape_cod_offset + (ape_note - ape_cod_vaddr);
ape_note_filesz = ape_note_end - ape_note;

View file

@ -10,8 +10,8 @@ if [ ! -f ape/loader.c ]; then
cd "$COSMO" || exit
fi
if [ -x .cosmocc/current/bin/make ]; then
MAKE=.cosmocc/current/bin/make
if [ -x build/bootstrap/make ]; then
MAKE=build/bootstrap/make
else
MAKE=make
fi

View file

@ -99,8 +99,3 @@ rm -f cosmocc.zip cosmocc.zip.sha256sum
# commit output directory
cd "${OLDPWD}" || die
mv "${OUTPUT_TMP}" "${OUTPUT_DIR}" || die
# update current symlink
BASE=$(basename "${OUTPUT_DIR}")
DIR=$(dirname "${OUTPUT_DIR}")
ln -sfn "$BASE" "$DIR/current"

View file

@ -6,14 +6,14 @@ if [ -n "$OBJDUMP" ]; then
fi
find_objdump() {
if [ -x .cosmocc/3.9.2/bin/$1-linux-cosmo-objdump ]; then
OBJDUMP=.cosmocc/3.9.2/bin/$1-linux-cosmo-objdump
elif [ -x .cosmocc/3.9.2/bin/$1-linux-musl-objdump ]; then
OBJDUMP=.cosmocc/3.9.2/bin/$1-linux-musl-objdump
elif [ -x "$COSMO/.cosmocc/3.9.2/bin/$1-linux-cosmo-objdump" ]; then
OBJDUMP="$COSMO/.cosmocc/3.9.2/bin/$1-linux-cosmo-objdump"
elif [ -x "$COSMO/.cosmocc/3.9.2/bin/$1-linux-musl-objdump" ]; then
OBJDUMP="$COSMO/.cosmocc/3.9.2/bin/$1-linux-musl-objdump"
if [ -x .cosmocc/3.8.0/bin/$1-linux-cosmo-objdump ]; then
OBJDUMP=.cosmocc/3.8.0/bin/$1-linux-cosmo-objdump
elif [ -x .cosmocc/3.8.0/bin/$1-linux-musl-objdump ]; then
OBJDUMP=.cosmocc/3.8.0/bin/$1-linux-musl-objdump
elif [ -x "$COSMO/.cosmocc/3.8.0/bin/$1-linux-cosmo-objdump" ]; then
OBJDUMP="$COSMO/.cosmocc/3.8.0/bin/$1-linux-cosmo-objdump"
elif [ -x "$COSMO/.cosmocc/3.8.0/bin/$1-linux-musl-objdump" ]; then
OBJDUMP="$COSMO/.cosmocc/3.8.0/bin/$1-linux-musl-objdump"
else
echo "error: toolchain not found (try running 'cosmocc --update' or 'make' in the cosmo monorepo)" >&2
exit 1

View file

@ -5,7 +5,6 @@
#include "exception.h"
#include "is_base_of.h"
#include "is_constructible.h"
#include "is_convertible.h"
#include "remove_extent.h"
#include "unique_ptr.h"
@ -97,12 +96,12 @@ class shared_ref
size_t use_count() const noexcept
{
return __atomic_load_n(&shared, __ATOMIC_RELAXED) + 1;
return shared + 1;
}
size_t weak_count() const noexcept
{
return __atomic_load_n(&weak, __ATOMIC_RELAXED);
return weak;
}
private:
@ -349,7 +348,7 @@ class shared_ptr
template<typename U>
bool owner_before(const weak_ptr<U>& r) const noexcept
{
return rc < r.rc;
return !r.owner_before(*this);
}
private:
@ -382,34 +381,6 @@ class weak_ptr
rc->keep_weak();
}
weak_ptr(const weak_ptr& r) noexcept : p(r.p), rc(r.rc)
{
if (rc)
rc->keep_weak();
}
template<typename U>
requires __::shared_ptr_compatible<T, U>
weak_ptr(const weak_ptr<U>& r) noexcept : p(r.p), rc(r.rc)
{
if (rc)
rc->keep_weak();
}
weak_ptr(weak_ptr&& r) noexcept : p(r.p), rc(r.rc)
{
r.p = nullptr;
r.rc = nullptr;
}
template<typename U>
requires __::shared_ptr_compatible<T, U>
weak_ptr(weak_ptr<U>&& r) noexcept : p(r.p), rc(r.rc)
{
r.p = nullptr;
r.rc = nullptr;
}
~weak_ptr()
{
if (rc)
@ -438,19 +409,6 @@ class weak_ptr
swap(rc, r.rc);
}
weak_ptr& operator=(weak_ptr r) noexcept
{
swap(r);
return *this;
}
template<typename U>
requires __::shared_ptr_compatible<T, U>
weak_ptr& operator=(weak_ptr<U> r) noexcept
{
weak_ptr<T>(move(r)).swap(*this);
}
shared_ptr<T> lock() const noexcept
{
if (expired())
@ -479,9 +437,6 @@ class weak_ptr
template<typename U>
friend class shared_ptr;
template<typename U, typename... Args>
friend shared_ptr<U> make_shared(Args&&...);
element_type* p = nullptr;
__::shared_ref* rc = nullptr;
};
@ -542,75 +497,19 @@ shared_ptr<T>::shared_ptr(U* const p, D d)
}
}
// Our make_shared supports passing a weak self reference as the first parameter
// to your constructor, e.g.:
//
// struct Tree : ctl::weak_self_base
// {
// ctl::shared_ptr<Tree> l, r;
// ctl::weak_ptr<Tree> parent;
// Tree(weak_ptr<Tree> const& self, auto&& l2, auto&& r2)
// : l(ctl::forward<decltype(l2)>(l2)),
// r(ctl::forward<decltype(r2)>(r2))
// {
// if (l) l->parent = self;
// if (r) r->parent = self;
// }
// };
//
// int main() {
// auto t = ctl::make_shared<Tree>(
// ctl::make_shared<Tree>(nullptr, nullptr), nullptr);
// return t->l->parent.lock().get() == t.get() ? 0 : 1;
// }
//
// As shown, passing the parameter at object construction time lets you complete
// object construction without needing a separate Init method. But because we go
// off spec as far as the STL is concerned, there is a potential ambiguity where
// you might have a constructor with a weak_ptr first parameter that is intended
// to be something other than a self-reference. So this feature is opt-in by way
// of inheriting from the following struct.
struct weak_self_base
{};
template<typename T, typename... Args>
shared_ptr<T>
make_shared(Args&&... args)
{
unique_ptr rc = __::shared_emplace<T>::make();
if constexpr (is_base_of_v<weak_self_base, T> &&
is_constructible_v<T, const weak_ptr<T>&, Args...>) {
// A __::shared_ref has a virtual weak reference that is owned by all of
// the shared references. We can avoid some unnecessary refcount changes
// by "borrowing" that reference and passing it to the constructor, then
// promoting it to a shared reference by swapping it with the shared_ptr
// that we return.
weak_ptr<T> w;
w.p = &rc->t;
w.rc = rc.get();
try {
rc->construct(const_cast<const weak_ptr<T>&>(w),
forward<Args>(args)...);
} catch (...) {
w.p = nullptr;
w.rc = nullptr;
throw;
}
rc.release();
shared_ptr<T> r;
swap(r.p, w.p);
swap(r.rc, w.rc);
return r;
} else {
rc->construct(forward<Args>(args)...);
shared_ptr<T> r;
r.p = &rc->t;
r.rc = rc.release();
if constexpr (is_base_of_v<enable_shared_from_this<T>, T>) {
r->weak_this = r;
}
return r;
auto rc = __::shared_emplace<T>::make();
rc->construct(forward<Args>(args)...);
shared_ptr<T> r;
r.p = &rc->t;
r.rc = rc.release();
if constexpr (is_base_of_v<enable_shared_from_this<T>, T>) {
r->weak_this = r;
}
return r;
}
} // namespace ctl

View file

@ -383,72 +383,4 @@ string::erase(const size_t pos, size_t count) noexcept
return *this;
}
void
string::append(const ctl::string_view& s, size_t pos, size_t count) noexcept
{
append(s.substr(pos, count));
}
size_t
string::find_last_of(char c, size_t pos) const noexcept
{
const char* b = data();
size_t n = size();
if (pos > n)
pos = n;
const char* p = (const char*)memrchr(b, c, pos);
return p ? p - b : npos;
}
size_t
string::find_last_of(ctl::string_view set, size_t pos) const noexcept
{
if (empty() || set.empty())
return npos;
bool lut[256] = {};
for (char c : set)
lut[c & 255] = true;
const char* b = data();
size_t last = size() - 1;
if (pos > last)
pos = last;
for (;;) {
if (lut[b[pos] & 255])
return pos;
if (!pos)
return npos;
--pos;
}
}
size_t
string::find_first_of(char c, size_t pos) const noexcept
{
size_t n = size();
if (pos >= n)
return npos;
const char* b = data();
const char* p = (const char*)memchr(b + pos, c, n - pos);
return p ? p - b : npos;
}
size_t
string::find_first_of(ctl::string_view set, size_t pos) const noexcept
{
if (set.empty())
return npos;
bool lut[256] = {};
for (char c : set)
lut[c & 255] = true;
const char* b = data();
size_t n = size();
for (;;) {
if (pos >= n)
return npos;
if (lut[b[pos] & 255])
return pos;
++pos;
}
}
} // namespace ctl

View file

@ -125,7 +125,6 @@ class string
void append(char, size_t) noexcept;
void append(unsigned long) noexcept;
void append(const void*, size_t) noexcept;
void append(const ctl::string_view&, size_t, size_t = npos) noexcept;
string& insert(size_t, ctl::string_view) noexcept;
string& erase(size_t = 0, size_t = npos) noexcept;
string substr(size_t = 0, size_t = npos) const noexcept;
@ -137,10 +136,6 @@ class string
bool starts_with(ctl::string_view) const noexcept;
size_t find(char, size_t = 0) const noexcept;
size_t find(ctl::string_view, size_t = 0) const noexcept;
size_t find_first_of(char, size_t = 0) const noexcept;
size_t find_first_of(ctl::string_view, size_t = 0) const noexcept;
size_t find_last_of(char, size_t = npos) const noexcept;
size_t find_last_of(ctl::string_view, size_t = npos) const noexcept;
void swap(string& s) noexcept
{
@ -307,7 +302,7 @@ class string
append(ch);
}
void append(const ctl::string_view& s) noexcept
void append(const ctl::string_view s) noexcept
{
append(s.p, s.n);
}

View file

@ -108,66 +108,4 @@ string_view::starts_with(const string_view s) const noexcept
return !memcmp(p, s.p, s.n);
}
size_t
string_view::find_last_of(char c, size_t pos) const noexcept
{
const char* b = data();
size_t n = size();
if (pos > n)
pos = n;
const char* p = (const char*)memrchr(b, c, pos);
return p ? p - b : npos;
}
size_t
string_view::find_last_of(ctl::string_view set, size_t pos) const noexcept
{
if (empty() || set.empty())
return npos;
bool lut[256] = {};
for (char c : set)
lut[c & 255] = true;
const char* b = data();
size_t last = size() - 1;
if (pos > last)
pos = last;
for (;;) {
if (lut[b[pos] & 255])
return pos;
if (!pos)
return npos;
--pos;
}
}
size_t
string_view::find_first_of(char c, size_t pos) const noexcept
{
size_t n = size();
if (pos >= n)
return npos;
const char* b = data();
const char* p = (const char*)memchr(b + pos, c, n - pos);
return p ? p - b : npos;
}
size_t
string_view::find_first_of(ctl::string_view set, size_t pos) const noexcept
{
if (set.empty())
return npos;
bool lut[256] = {};
for (char c : set)
lut[c & 255] = true;
const char* b = data();
size_t n = size();
for (;;) {
if (pos >= n)
return npos;
if (lut[b[pos] & 255])
return pos;
++pos;
}
}
} // namespace ctl

View file

@ -45,10 +45,6 @@ struct string_view
string_view substr(size_t = 0, size_t = npos) const noexcept;
size_t find(char, size_t = 0) const noexcept;
size_t find(string_view, size_t = 0) const noexcept;
size_t find_first_of(char, size_t = 0) const noexcept;
size_t find_first_of(ctl::string_view, size_t = 0) const noexcept;
size_t find_last_of(char, size_t = npos) const noexcept;
size_t find_last_of(ctl::string_view, size_t = npos) const noexcept;
constexpr string_view& operator=(const string_view s) noexcept
{
@ -113,12 +109,12 @@ struct string_view
return p[n - 1];
}
constexpr const_iterator begin() const noexcept
constexpr const_iterator begin() noexcept
{
return p;
}
constexpr const_iterator end() const noexcept
constexpr const_iterator end() noexcept
{
return p + n;
}

View file

@ -6,5 +6,4 @@ o/$(MODE)/dsp: o/$(MODE)/dsp/audio \
o/$(MODE)/dsp/core \
o/$(MODE)/dsp/mpeg \
o/$(MODE)/dsp/scale \
o/$(MODE)/dsp/prog \
o/$(MODE)/dsp/tty

View file

@ -469,16 +469,16 @@ COSMOAUDIO_ABI int cosmoaudio_poll(struct CosmoAudio* ca,
if (in_out_writeFrames && 1u + *in_out_writeFrames > ca->outputBufferFrames)
return COSMOAUDIO_ENOBUF;
for (;;) {
int done = 1;
int done = 0;
ma_uint32 readable = 0;
ma_uint32 writable = 0;
if (in_out_readFrames) {
readable = ma_pcm_rb_available_read(&ca->input);
done &= readable >= (ma_uint32)*in_out_readFrames;
done |= readable >= (ma_uint32)*in_out_readFrames;
}
if (in_out_writeFrames) {
writable = ma_pcm_rb_available_write(&ca->output);
done &= writable >= (ma_uint32)*in_out_writeFrames;
done |= writable >= (ma_uint32)*in_out_writeFrames;
}
if (done) {
if (in_out_readFrames)

View file

@ -1,43 +0,0 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#── vi: set noet ft=make ts=8 sw=8 fenc=utf-8 :vi ────────────────────┘
PKGS += DSP_PROG
DSP_PROG_FILES := $(wildcard dsp/prog/*)
DSP_PROG_HDRS = $(filter %.h,$(DSP_PROG_FILES))
DSP_PROG_SRCS = $(filter %.c,$(DSP_PROG_FILES))
DSP_PROG_OBJS = $(DSP_PROG_SRCS:%.c=o/$(MODE)/%.o)
DSP_PROG_COMS = $(DSP_PROG_SRCS:%.c=o/$(MODE)/%)
DSP_PROG_BINS = $(DSP_PROG_COMS) $(DSP_PROG_COMS:%=%.dbg)
DSP_PROG_DIRECTDEPS = \
DSP_AUDIO \
LIBC_CALLS \
LIBC_INTRIN \
LIBC_NEXGEN32E \
LIBC_RUNTIME \
LIBC_SOCK \
LIBC_STDIO \
LIBC_SYSV \
LIBC_TINYMATH \
THIRD_PARTY_MUSL \
DSP_PROG_DEPS := \
$(call uniq,$(foreach x,$(DSP_PROG_DIRECTDEPS),$($(x))))
o/$(MODE)/dsp/prog/prog.pkg: \
$(DSP_PROG_OBJS) \
$(foreach x,$(DSP_PROG_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/dsp/prog/%.dbg: \
$(DSP_PROG_DEPS) \
o/$(MODE)/dsp/prog/prog.pkg \
o/$(MODE)/dsp/prog/%.o \
$(CRT) \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
$(DSP_PROG_OBJS): dsp/prog/BUILD.mk
.PHONY: o/$(MODE)/dsp/prog
o/$(MODE)/dsp/prog: $(DSP_PROG_BINS)

View file

@ -1,41 +0,0 @@
#ifndef COSMOPOLITAN_DSP_PROG_LOUDNESS_H_
#define COSMOPOLITAN_DSP_PROG_LOUDNESS_H_
#include <math.h>
#include <stdio.h>
#define MIN_DECIBEL -60
#define MAX_DECIBEL 0
// computes root of mean squares
static double rms(float *p, int n) {
double s = 0;
for (int i = 0; i < n; ++i)
s += p[i] * p[i];
return sqrt(s / n);
}
// converts rms to decibel
static double rms_to_db(double rms) {
double db = 20 * log10(rms);
db = fmin(db, MAX_DECIBEL);
db = fmax(db, MIN_DECIBEL);
return db;
}
// char meter[21];
// format_decibel_meter(meter, 20, rms_to_db(rms(samps, count)))
static char *format_decibel_meter(char *meter, int width, double db) {
double range = MAX_DECIBEL - MIN_DECIBEL;
int filled = (db - MIN_DECIBEL) / range * width;
for (int i = 0; i < width; ++i) {
if (i < filled) {
meter[i] = '=';
} else {
meter[i] = ' ';
}
}
meter[width] = 0;
return meter;
}
#endif /* COSMOPOLITAN_DSP_PROG_LOUDNESS_H_ */

View file

@ -1,127 +0,0 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include <arpa/inet.h>
#include <assert.h>
#include <cosmoaudio.h>
#include <errno.h>
#include <math.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include "loudness.h"
/**
* @fileoverview plays audio from remote computer on speaker
* @see dsp/prog/sendaudio.c
*/
#define SAMPLING_RATE 44100
#define FRAMES_PER_SECOND 60
#define DEBUG_LOG 0
#define PORT 9834
#define CHUNK_FRAMES (SAMPLING_RATE / FRAMES_PER_SECOND)
static_assert(CHUNK_FRAMES * sizeof(short) < 1472,
"audio chunks won't fit in udp ethernet packet");
sig_atomic_t g_done;
void onsig(int sig) {
g_done = 1;
}
short toshort(float x) {
return fmaxf(-1, fminf(1, x)) * 32767;
}
float tofloat(short x) {
return x / 32768.f;
}
int main(int argc, char* argv[]) {
// listen on udp port for audio
int server;
if ((server = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("socket");
return 3;
}
struct sockaddr_in addr = {.sin_family = AF_INET, .sin_port = htons(PORT)};
if (bind(server, (struct sockaddr*)&addr, sizeof(addr))) {
perror("bind");
return 4;
}
// setup signals
struct sigaction sa;
sa.sa_flags = 0;
sa.sa_handler = onsig;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, 0);
// configure cosmo audio
struct CosmoAudioOpenOptions cao = {0};
cao.sizeofThis = sizeof(struct CosmoAudioOpenOptions);
cao.deviceType = kCosmoAudioDeviceTypePlayback;
cao.sampleRate = SAMPLING_RATE;
cao.bufferFrames = CHUNK_FRAMES * 2;
cao.debugLog = DEBUG_LOG;
cao.channels = 1;
// connect to microphone and speaker
int status;
struct CosmoAudio* ca;
status = cosmoaudio_open(&ca, &cao);
if (status != COSMOAUDIO_SUCCESS) {
fprintf(stderr, "failed to open audio: %d\n", status);
return 5;
}
while (!g_done) {
// read from network
ssize_t got;
short buf16[CHUNK_FRAMES];
if ((got = read(server, buf16, CHUNK_FRAMES * sizeof(short))) == -1) {
if (errno == EINTR)
continue;
perror("read");
return 7;
}
if (got != CHUNK_FRAMES * sizeof(short)) {
fprintf(stderr, "warning: got partial audio frame\n");
continue;
}
// write to speaker
float buf32[CHUNK_FRAMES];
for (int i = 0; i < CHUNK_FRAMES; ++i)
buf32[i] = tofloat(buf16[i]);
cosmoaudio_poll(ca, 0, (int[]){CHUNK_FRAMES});
cosmoaudio_write(ca, buf32, CHUNK_FRAMES);
// print loudness in ascii
char meter[21];
double db = rms_to_db(rms(buf32, CHUNK_FRAMES));
format_decibel_meter(meter, 20, db);
printf("\r%s| %+6.2f dB", meter, db);
fflush(stdout);
}
// clean up resources
cosmoaudio_flush(ca);
cosmoaudio_close(ca);
close(server);
}

View file

@ -1,149 +0,0 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include <arpa/inet.h>
#include <assert.h>
#include <cosmoaudio.h>
#include <errno.h>
#include <math.h>
#include <netdb.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include "loudness.h"
/**
* @fileoverview sends audio from microphone to remote computer
* @see dsp/prog/recvaudio.c
*/
#define SAMPLING_RATE 44100
#define FRAMES_PER_SECOND 60
#define DEBUG_LOG 0
#define PORT 9834
#define CHUNK_FRAMES (SAMPLING_RATE / FRAMES_PER_SECOND)
static_assert(CHUNK_FRAMES * sizeof(short) < 1472,
"audio chunks won't fit in udp ethernet packet");
sig_atomic_t g_done;
void onsig(int sig) {
g_done = 1;
}
short toshort(float x) {
return fmaxf(-1, fminf(1, x)) * 32767;
}
float tofloat(short x) {
return x / 32768.f;
}
uint32_t host2ip(const char* host) {
uint32_t ip;
if ((ip = inet_addr(host)) != -1u)
return ip;
int rc;
struct addrinfo* ai = NULL;
struct addrinfo hint = {AI_NUMERICSERV, AF_INET, SOCK_STREAM, IPPROTO_TCP};
if ((rc = getaddrinfo(host, "0", &hint, &ai))) {
fprintf(stderr, "%s: %s\n", host, gai_strerror(rc));
exit(50 + rc);
}
ip = ntohl(((struct sockaddr_in*)ai->ai_addr)->sin_addr.s_addr);
freeaddrinfo(ai);
return ip;
}
int main(int argc, char* argv[]) {
if (argc != 2) {
fprintf(stderr, "%s: missing host argument\n", argv[0]);
return 1;
}
// get host argument
const char* remote_host = argv[1];
uint32_t ip = host2ip(remote_host);
// connect to server
int client;
if ((client = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror(remote_host);
return 3;
}
struct sockaddr_in addr = {.sin_family = AF_INET,
.sin_port = htons(PORT),
.sin_addr.s_addr = htonl(ip)};
if (connect(client, (struct sockaddr*)&addr, sizeof(addr))) {
perror(remote_host);
return 4;
}
// setup signals
struct sigaction sa;
sa.sa_flags = 0;
sa.sa_handler = onsig;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, 0);
// configure cosmo audio
struct CosmoAudioOpenOptions cao = {0};
cao.sizeofThis = sizeof(struct CosmoAudioOpenOptions);
cao.deviceType = kCosmoAudioDeviceTypeCapture;
cao.sampleRate = SAMPLING_RATE;
cao.bufferFrames = CHUNK_FRAMES * 2;
cao.debugLog = DEBUG_LOG;
cao.channels = 1;
// connect to microphone and speaker
int status;
struct CosmoAudio* ca;
status = cosmoaudio_open(&ca, &cao);
if (status != COSMOAUDIO_SUCCESS) {
fprintf(stderr, "failed to open audio: %d\n", status);
return 5;
}
while (!g_done) {
// read from microphone
float buf32[CHUNK_FRAMES];
cosmoaudio_poll(ca, (int[]){CHUNK_FRAMES}, 0);
cosmoaudio_read(ca, buf32, CHUNK_FRAMES);
short buf16[CHUNK_FRAMES];
for (int i = 0; i < CHUNK_FRAMES; ++i)
buf16[i] = toshort(buf32[i]);
// send to server
if (write(client, buf16, CHUNK_FRAMES * sizeof(short)) == -1) {
if (errno == EINTR && g_done)
break;
perror(remote_host);
return 7;
}
// print loudness in ascii
char meter[21];
double db = rms_to_db(rms(buf32, CHUNK_FRAMES));
format_decibel_meter(meter, 20, db);
printf("\r%s| %+6.2f dB", meter, db);
fflush(stdout);
}
// clean up resources
cosmoaudio_close(ca);
close(client);
}

View file

@ -54,8 +54,8 @@ EXAMPLES_DIRECTDEPS = \
LIBC_NEXGEN32E \
LIBC_NT_ADVAPI32 \
LIBC_NT_IPHLPAPI \
LIBC_NT_KERNEL32 \
LIBC_NT_MEMORY \
LIBC_NT_KERNEL32 \
LIBC_NT_NTDLL \
LIBC_NT_USER32 \
LIBC_NT_WS2_32 \
@ -64,7 +64,6 @@ EXAMPLES_DIRECTDEPS = \
LIBC_SOCK \
LIBC_STDIO \
LIBC_STR \
LIBC_SYSTEM \
LIBC_SYSV \
LIBC_SYSV_CALLS \
LIBC_TESTLIB \
@ -82,8 +81,6 @@ EXAMPLES_DIRECTDEPS = \
THIRD_PARTY_GETOPT \
THIRD_PARTY_HIREDIS \
THIRD_PARTY_LIBCXX \
THIRD_PARTY_LIBCXXABI \
THIRD_PARTY_LIBUNWIND \
THIRD_PARTY_LINENOISE \
THIRD_PARTY_LUA \
THIRD_PARTY_MBEDTLS \
@ -97,10 +94,12 @@ EXAMPLES_DIRECTDEPS = \
THIRD_PARTY_TZ \
THIRD_PARTY_VQSORT \
THIRD_PARTY_XED \
THIRD_PARTY_LIBCXXABI \
THIRD_PARTY_LIBUNWIND \
THIRD_PARTY_ZLIB \
TOOL_ARGS \
TOOL_BUILD_LIB \
TOOL_VIZ_LIB \
TOOL_VIZ_LIB
EXAMPLES_DEPS := \
$(call uniq,$(foreach x,$(EXAMPLES_DIRECTDEPS),$($(x))))
@ -151,10 +150,6 @@ o/$(MODE)/examples/picol.o: private \
CPPFLAGS += \
-DSTACK_FRAME_UNLIMITED
o/$(MODE)/examples/nesemu1.o: private \
CPPFLAGS += \
-O3
o/$(MODE)/examples/picol.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/picol.o \

View file

@ -1,353 +0,0 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <iconv.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
/**
* @fileoverview program for viewing bbs art files
* @see https://github.com/blocktronics/artpacks
* @see http://www.textfiles.com/art/
*/
#define HELP \
"Usage:\n\
art [-b %d] [-f %s] [-t %s] FILE...\n\
\n\
Flags:\n\
-b NUMBER specifies simulated modem baud rate, which defaults to\n\
2400 since that was the most common modem speed in the\n\
later half of the 1980s during the BBS golden age; you\n\
could also say 300 for the slowest experience possible\n\
or you could say 14.4k to get more of a 90's feel, and\n\
there's also the infamous 56k to bring you back to y2k\n\
-f CHARSET specifies charset of input bytes, where the default is\n\
cp347 which means IBM Code Page 347 a.k.a. DOS\n\
-t CHARSET specifies output charset used by your terminal, and it\n\
defaults to utf8 a.k.a. thompson-pike encoding\n\
\n\
Supported charsets:\n\
utf8, ascii, wchar_t, ucs2be, ucs2le, utf16be, utf16le, ucs4be,\n\
ucs4le, utf16, ucs4, ucs2, eucjp, shiftjis, iso2022jp, gb18030, gbk,\n\
gb2312, big5, euckr, iso88591, latin1, iso88592, iso88593, iso88594,\n\
iso88595, iso88596, iso88597, iso88598, iso88599, iso885910,\n\
iso885911, iso885913, iso885914, iso885915, iso885916, cp1250,\n\
windows1250, cp1251, windows1251, cp1252, windows1252, cp1253,\n\
windows1253, cp1254, windows1254, cp1255, windows1255, cp1256,\n\
windows1256, cp1257, windows1257, cp1258, windows1258, koi8r, koi8u,\n\
cp437, cp850, cp866, ibm1047, cp1047.\n\
\n\
See also:\n\
http://www.textfiles.com/art/\n\
https://github.com/blocktronics/artpacks\n\
\n"
#define INBUFSZ 256
#define OUBUFSZ (INBUFSZ * 6)
#define SLIT(s) ((unsigned)s[3] << 24 | s[2] << 16 | s[1] << 8 | s[0])
// "When new technology comes out, people don't all buy it right away.
// If what they have works, some will wait until it doesn't. A few
// people do get the latest though. In 1984 2400 baud modems became
// available, so some people had them, but many didn't. A BBS list
// from 1986 shows operators were mostly 300 and 1200, but some were
// using 2400. The next 5 years were the hayday of the 2400."
//
// https://forum.vcfed.org/index.php?threads/the-2400-baud-modem.44241/
int baud_rate = 2400; // -b 2400
const char* from_charset = "CP437"; // -f CP437
const char* to_charset = "UTF-8"; // -t UTF-8
volatile sig_atomic_t done;
void on_signal(int sig) {
done = 1;
(void)sig;
}
void print(const char* s) {
(void)!write(STDOUT_FILENO, s, strlen(s));
}
int encode_character(char output[8], const char* codec, wchar_t character) {
size_t inbytesleft = sizeof(wchar_t);
size_t outbytesleft = 7;
char* inbuf = (char*)&character;
char* outbuf = output;
iconv_t cd = iconv_open(codec, "wchar_t");
if (cd == (iconv_t)-1)
return -1;
size_t result = iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
iconv_close(cd);
if (result == (size_t)-1)
return -1;
*outbuf = '\0';
return 7 - outbytesleft;
}
void append_replacement_character(char** b) {
int n = encode_character(*b, to_charset, 0xFFFD);
if (n == -1)
n = encode_character(*b, to_charset, '?');
if (n != -1)
*b += n;
}
int compare_time(struct timespec a, struct timespec b) {
int cmp;
if (!(cmp = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec)))
cmp = (a.tv_nsec > b.tv_nsec) - (a.tv_nsec < b.tv_nsec);
return cmp;
}
struct timespec add_time(struct timespec x, struct timespec y) {
x.tv_sec += y.tv_sec;
x.tv_nsec += y.tv_nsec;
if (x.tv_nsec >= 1000000000) {
x.tv_nsec -= 1000000000;
x.tv_sec += 1;
}
return x;
}
struct timespec subtract_time(struct timespec a, struct timespec b) {
a.tv_sec -= b.tv_sec;
if (a.tv_nsec < b.tv_nsec) {
a.tv_nsec += 1000000000;
a.tv_sec--;
}
a.tv_nsec -= b.tv_nsec;
return a;
}
struct timespec fromnanos(long long x) {
struct timespec ts;
ts.tv_sec = x / 1000000000;
ts.tv_nsec = x % 1000000000;
return ts;
}
void process_file(const char* path, int fd, iconv_t cd) {
size_t carry = 0;
struct timespec next;
char input_buffer[INBUFSZ];
clock_gettime(CLOCK_MONOTONIC, &next);
for (;;) {
// read from file
ssize_t bytes_read = read(fd, input_buffer + carry, INBUFSZ - carry);
if (!bytes_read)
return;
if (bytes_read == -1) {
perror(path);
done = 1;
return;
}
// modernize character set
char* input_ptr = input_buffer;
size_t input_left = carry + bytes_read;
char output_buffer[OUBUFSZ];
char* output_ptr = output_buffer;
size_t output_left = OUBUFSZ;
size_t ir = iconv(cd, &input_ptr, &input_left, &output_ptr, &output_left);
carry = 0;
if (ir == (size_t)-1) {
if (errno == EINVAL) {
// incomplete multibyte sequence encountered
memmove(input_buffer, input_ptr, input_left);
carry = input_left;
} else if (errno == EILSEQ && input_left) {
// EILSEQ means either
// 1. illegal input sequence encountered
// 2. code not encodable in output codec
//
// so we skip one byte of input, and insert <20> or ? in the output
// this isn't the most desirable behavior, but it is the best we
// can do, since we don't know specifics about the codecs in use
//
// unlike glibc cosmo's iconv implementation may handle case (2)
// automatically by inserting an asterisk in place of a sequence
++input_ptr;
--input_left;
memmove(input_buffer, input_ptr, input_left);
carry = input_left;
if (output_left >= 8)
append_replacement_character(&output_ptr);
} else {
perror(path);
done = 1;
return;
}
}
// write to terminal
for (char* p = output_buffer; p < output_ptr; p++) {
if (done)
return;
(void)!write(STDOUT_FILENO, p, 1);
// allow arrow keys to change baud rate
int have;
if (ioctl(STDIN_FILENO, FIONREAD, &have)) {
perror("ioctl");
done = 1;
return;
}
if (have > 0) {
char key[4] = {0};
if (read(STDIN_FILENO, key, sizeof(key)) > 0) {
if (SLIT(key) == SLIT("\33[A") || // up
SLIT(key) == SLIT("\33[C")) { // right
baud_rate *= 1.4;
} else if (SLIT(key) == SLIT("\33[B") || // down
SLIT(key) == SLIT("\33[D")) { // left
baud_rate *= 0.6;
}
if (baud_rate < 3)
baud_rate = 3;
if (baud_rate > 1000000000)
baud_rate = 1000000000;
}
}
// insert artificial delay for one byte. we divide by 10 to convert
// bits to bytes, because that is how many bits 8-N-1 encoding used
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
next = add_time(next, fromnanos(1e9 / (baud_rate / 10.)));
if (compare_time(next, now) > 0) {
struct timespec sleep = subtract_time(next, now);
nanosleep(&sleep, 0);
}
}
}
}
int main(int argc, char* argv[]) {
int opt;
while ((opt = getopt(argc, argv, "hb:f:t:")) != -1) {
switch (opt) {
case 'b': {
char* endptr;
double rate = strtod(optarg, &endptr);
if (*endptr == 'k') {
rate *= 1e3;
++endptr;
} else if (*endptr == 'm') {
rate *= 1e6;
++endptr;
}
if (*endptr || baud_rate <= 0) {
fprintf(stderr, "%s: invalid baud rate: %s\n", argv[0], optarg);
exit(1);
}
baud_rate = rate;
break;
}
case 'f':
from_charset = optarg;
break;
case 't':
to_charset = optarg;
break;
case 'h':
fprintf(stderr, HELP, baud_rate, from_charset, to_charset);
exit(0);
default:
fprintf(stderr, "protip: pass the -h flag for help\n");
exit(1);
}
}
if (optind == argc) {
fprintf(stderr, "%s: missing operand\n", argv[0]);
exit(1);
}
// create character transcoder
iconv_t cd = iconv_open(to_charset, from_charset);
if (cd == (iconv_t)-1) {
fprintf(stderr, "error: conversion from %s to %s not supported\n",
from_charset, to_charset);
exit(1);
}
// catch ctrl-c
signal(SIGINT, on_signal);
// don't wait until newline to read() keystrokes
struct termios t;
if (!tcgetattr(STDIN_FILENO, &t)) {
struct termios t2 = t;
t2.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &t2);
}
// Process each file specified on the command line
for (int i = optind; i < argc && !done; i++) {
// open file
int fd = open(argv[i], O_RDONLY);
if (fd == -1) {
perror(argv[i]);
break;
}
// wait between files
if (i > optind)
sleep(1);
print("\33[?25l"); // hide cursor
print("\33[H"); // move cursor to top-left
print("\33[J"); // erase display forward
print("\33[1;24r"); // set scrolling region to first 24 lines
print("\33[?7h"); // enable auto-wrap mode
print("\33[?3l"); // 80 column mode (deccolm) vt100
print("\33[H"); // move cursor to top-left, again
// get busy
process_file(argv[i], fd, cd);
close(fd);
}
// cleanup
iconv_close(cd);
print("\33[s"); // save cursor position
print("\33[?25h"); // show cursor
print("\33[0m"); // reset text attributes (color, bold, etc.)
print("\33[?1049l"); // exit alternate screen mode
print("\33(B"); // exit line drawing and other alt charset modes
print("\33[r"); // reset scrolling region
print("\33[?2004l"); // turn off bracketed paste mode
print("\33[4l"); // exit insert mode
print("\33[?1l\33>"); // exit application keypad mode
print("\33[?7h"); // reset text wrapping mode
print("\33[?12l"); // reset cursor blinking mode
print("\33[?6l"); // reset origin mode
print("\33[20l"); // reset auto newline mode
print("\33[u"); // restore cursor position
// restore terminal
tcsetattr(STDIN_FILENO, TCSANOW, &t);
}

View file

@ -1,353 +0,0 @@
// -*- mode:c; indent-tabs-mode:nil; c-basic-offset:4 -*-
// vi: set et ft=c ts=4 sts=4 sw=4 fenc=utf-8
// asteroids by tsotchke
// https://github.com/tsotchke/asteroids
// clang-format off
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include <termios.h>
#include <sys/select.h>
#define SCREEN_WIDTH 80
#define SCREEN_HEIGHT 24
#define MAX_ASTEROIDS 5
#define MAX_BULLETS 5
typedef struct {
float x, y;
} Vector2;
typedef struct {
Vector2 position;
Vector2 velocity;
float angle;
float radius;
} GameObject;
GameObject spaceship;
GameObject asteroids[MAX_ASTEROIDS];
GameObject bullets[MAX_BULLETS];
int score = 0;
time_t startTime;
int isGameOver = 0;
int shouldExit = 0;
int finalTime = 0; // To store final time at game over
char display[SCREEN_HEIGHT][SCREEN_WIDTH];
// Function to clear the screen buffer
void clearDisplay() {
memset(display, ' ', sizeof(display));
}
// Function to draw a pixel on the screen
void drawPixel(int x, int y) {
if (x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT) {
display[y][x] = '*';
}
}
// Function to draw a line using Bresenham's algorithm
void drawLine(int x1, int y1, int x2, int y2) {
int dx = abs(x2 - x1), sx = (x1 < x2) ? 1 : -1;
int dy = -abs(y2 - y1), sy = (y1 < y2) ? 1 : -1;
int error = dx + dy, e2;
while (1) {
drawPixel(x1, y1);
if (x1 == x2 && y1 == y2) break;
e2 = 2 * error;
if (e2 >= dy) { error += dy; x1 += sx; }
if (e2 <= dx) { error += dx; y1 += sy; }
}
}
// Function to draw a circle
void drawCircle(int centerX, int centerY, int radius) {
int x = radius - 1, y = 0, dx = 1, dy = 1, err = dx - (radius << 1);
while (x >= y) {
drawPixel(centerX + x, centerY + y);
drawPixel(centerX + y, centerY + x);
drawPixel(centerX - y, centerY + x);
drawPixel(centerX - x, centerY + y);
drawPixel(centerX - x, centerY - y);
drawPixel(centerX - y, centerY - x);
drawPixel(centerX + y, centerY - x);
drawPixel(centerX + x, centerY - y);
if (err <= 0) {
y++;
err += dy;
dy += 2;
}
if (err > 0) {
x--;
dx += 2;
err += dx - (radius << 1);
}
}
}
// Initialize a game object
void initializeGameObject(GameObject *obj, float x, float y, float angle, float radius) {
obj->position = (Vector2){x, y};
obj->velocity = (Vector2){0, 0};
obj->angle = angle;
obj->radius = radius;
}
// Wrap position of the spaceship and asteroids within screen bounds
void wrapPosition(Vector2 *pos) {
if (pos->x < 0) pos->x = SCREEN_WIDTH - 1;
if (pos->x >= SCREEN_WIDTH) pos->x = 0;
if (pos->y < 0) pos->y = SCREEN_HEIGHT - 1;
if (pos->y >= SCREEN_HEIGHT) pos->y = 0;
}
// Check if two game objects are colliding
int checkCollision(GameObject *a, GameObject *b) {
float deltaX = a->position.x - b->position.x;
float deltaY = a->position.y - b->position.y;
return sqrt(deltaX * deltaX + deltaY * deltaY) < (a->radius + b->radius);
}
// Initialize game state
void initGame() {
score = 0; // Reset the score
initializeGameObject(&spaceship, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 0, 2);
for (int i = 0; i < MAX_ASTEROIDS; i++) {
initializeGameObject(&asteroids[i],
rand() % SCREEN_WIDTH,
rand() % SCREEN_HEIGHT,
0,
2 + rand() % 3);
asteroids[i].velocity.x = ((float)rand() / RAND_MAX) * 2 - 1;
asteroids[i].velocity.y = ((float)rand() / RAND_MAX) * 2 - 1;
}
for (int i = 0; i < MAX_BULLETS; i++) {
bullets[i].position.x = -1; // Mark bullet as inactive
bullets[i].position.y = -1;
}
startTime = time(NULL);
isGameOver = 0;
finalTime = 0; // Reset final time
}
// Draw the spaceship on the screen
void drawSpaceship() {
int x = (int)spaceship.position.x;
int y = (int)spaceship.position.y;
int size = 3;
float cosAngle = cos(spaceship.angle);
float sinAngle = sin(spaceship.angle);
int x1 = x + size * cosAngle;
int y1 = y + size * sinAngle;
int x2 = x + size * cos(spaceship.angle + 2.5);
int y2 = y + size * sin(spaceship.angle + 2.5);
int x3 = x + size * cos(spaceship.angle - 2.5);
int y3 = y + size * sin(spaceship.angle - 2.5);
drawLine(x1, y1, x2, y2);
drawLine(x2, y2, x3, y3);
drawLine(x3, y3, x1, y1);
}
// Draw all entities on the screen
void drawEntities(GameObject *entities, int count, void (*drawFunc)(GameObject *)) {
for (int i = 0; i < count; i++) {
drawFunc(&entities[i]);
}
}
// Draw a bullet on the screen
void drawBullet(GameObject *bullet) { // Changed to non-const
if (bullet->position.x >= 0) {
drawPixel((int)bullet->position.x, (int)bullet->position.y);
}
}
// Draw an asteroid on the screen
void drawAsteroid(GameObject *asteroid) { // Changed to non-const
drawCircle((int)asteroid->position.x, (int)asteroid->position.y, (int)asteroid->radius);
}
// Refresh the display
void updateDisplay() {
clearDisplay();
if (!isGameOver) {
drawSpaceship();
drawEntities(asteroids, MAX_ASTEROIDS, drawAsteroid);
drawEntities(bullets, MAX_BULLETS, drawBullet);
}
// Print the screen buffer
printf("\033[H");
for (int y = 0; y < SCREEN_HEIGHT; y++) {
for (int x = 0; x < SCREEN_WIDTH; x++) {
putchar(display[y][x]);
}
putchar('\n');
}
// Display score and elapsed time
time_t currentTime = time(NULL);
int elapsedTime = isGameOver ? finalTime : (currentTime - startTime);
printf("Score: %d | Time: %02d:%02d | %s\n", score, elapsedTime / 60, elapsedTime % 60, isGameOver ? "Game Over!" : " ");
}
// Update the position of game objects
void updateGameObject(GameObject *obj, int isBullet) {
obj->position.x += obj->velocity.x;
obj->position.y += obj->velocity.y;
// If it's a bullet, check if it's out of bounds
if (isBullet) {
if (obj->position.x < 0 || obj->position.x >= SCREEN_WIDTH || obj->position.y < 0 || obj->position.y >= SCREEN_HEIGHT) {
obj->position.x = -1; // Deactivate bullet
obj->position.y = -1;
}
} else {
wrapPosition(&obj->position);
}
}
// Update the game state
void updateGame() {
if (isGameOver) return;
// Update spaceship and apply friction
updateGameObject(&spaceship, 0); // 0 indicates it's not a bullet
spaceship.velocity.x *= 0.98;
spaceship.velocity.y *= 0.98;
// Move asteroids and check for collisions
for (int i = 0; i < MAX_ASTEROIDS; i++) {
updateGameObject(&asteroids[i], 0);
if (checkCollision(&spaceship, &asteroids[i])) {
isGameOver = 1;
finalTime = time(NULL) - startTime;
return;
}
}
// Update bullet positions
for (int i = 0; i < MAX_BULLETS; i++) {
if (bullets[i].position.x >= 0) {
updateGameObject(&bullets[i], 1); // 1 indicates it's a bullet
}
}
// Check for bullet collisions with asteroids
for (int i = 0; i < MAX_BULLETS; i++) {
if (bullets[i].position.x >= 0) {
for (int j = 0; j < MAX_ASTEROIDS; j++) {
if (checkCollision(&bullets[i], &asteroids[j])) {
bullets[i].position.x = -1; // Deactivate bullet
bullets[i].position.y = -1;
asteroids[j].position.x = rand() % SCREEN_WIDTH;
asteroids[j].position.y = rand() % SCREEN_HEIGHT;
score += 100;
}
}
}
}
}
// Fire a bullet
void shootBullet() {
for (int i = 0; i < MAX_BULLETS; i++) {
if (bullets[i].position.x < 0) {
bullets[i].position = spaceship.position;
bullets[i].velocity.x = cos(spaceship.angle) * 2;
bullets[i].velocity.y = sin(spaceship.angle) * 2;
break;
}
}
}
// Check if a key was hit
int isKeyHit() {
struct timeval tv = { 0L, 0L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
return select(1, &fds, NULL, NULL, &tv);
}
// Configure terminal settings
void configureTerminal(struct termios *old_tio, struct termios *new_tio) {
tcgetattr(STDIN_FILENO, old_tio);
*new_tio = *old_tio;
new_tio->c_lflag &= (~ICANON & ~ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, new_tio);
}
// Restore terminal settings
void restoreTerminal(struct termios *old_tio) {
tcsetattr(STDIN_FILENO, TCSANOW, old_tio);
}
void onSignal(int sig) {
shouldExit = 1;
}
// Main game loop
int main() {
signal(SIGINT, onSignal); // Capture ^C
srand(time(NULL)); // Seed the random number generator
initGame(); // Initialize the game state
struct termios old_tio, new_tio;
configureTerminal(&old_tio, &new_tio);
printf("\033[?25l"); // Hide the cursor
while (!shouldExit) {
if (isKeyHit()) {
char input = getchar();
if (input == 27) { // ESC key
if (getchar() == '[') { // Handle arrow keys
switch (getchar()) {
case 'A': // Up arrow
spaceship.velocity.x += cos(spaceship.angle) * 0.2;
spaceship.velocity.y += sin(spaceship.angle) * 0.2;
break;
case 'B': // Down arrow
spaceship.velocity.x -= cos(spaceship.angle) * 0.2;
spaceship.velocity.y -= sin(spaceship.angle) * 0.2;
break;
case 'D': spaceship.angle -= 0.2; break; // Left arrow
case 'C': spaceship.angle += 0.2; break; // Right arrow
}
}
} else if (input == ' ') {
shootBullet(); // Fire a bullet
} else if (input == 'q') {
break; // Quit the game
} else if (input == 'r' && isGameOver) {
initGame(); // Restart the game
}
}
updateGame(); // Update game state
updateDisplay(); // Refresh the display
usleep(50000); // Wait for 50ms (20 FPS)
}
printf("\033[?25h"); // Show the cursor
restoreTerminal(&old_tio); // Restore terminal settings
return 0;
}

View file

@ -337,7 +337,7 @@ int main(int argc, char *argv[]) {
sigaddset(&block, SIGQUIT);
pthread_attr_t attr;
unassert(!pthread_attr_init(&attr));
unassert(!pthread_attr_setstacksize(&attr, 65536 - getpagesize()));
unassert(!pthread_attr_setstacksize(&attr, 65536));
unassert(!pthread_attr_setguardsize(&attr, getpagesize()));
unassert(!pthread_attr_setsigmask_np(&attr, &block));
unassert(!pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0));

View file

@ -13,6 +13,7 @@
#include "dsp/tty/tty.h"
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/itimerval.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/winsize.h"
#include "libc/calls/termios.h"
@ -35,17 +36,20 @@
#include "libc/str/str.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/itimer.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/consts/prio.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/w.h"
#include "libc/thread/thread.h"
#include "libc/time.h"
#include "libc/x/xasprintf.h"
#include "libc/x/xsigaction.h"
#include "libc/zip.h"
#include "third_party/getopt/getopt.internal.h"
#include "third_party/libcxx/__atomic/atomic.h"
#include "third_party/libcxx/vector"
#include "tool/viz/lib/knobs.h"
@ -120,6 +124,15 @@ typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
static const struct itimerval kNesFps = {
{0, 1. / FPS * 1e6},
{0, 1. / FPS * 1e6},
};
struct Frame {
char *p, *w, *mem;
};
struct Action {
int code;
int wait;
@ -135,13 +148,15 @@ struct ZipGames {
char** p;
};
static const struct timespec kNesFps = {0, 1. / FPS * 1e9};
static int frame_;
static size_t vtsize_;
static bool artifacts_;
static long tyn_, txn_;
static struct Frame vf_[2];
static const char* inputfn_;
static struct Status status_;
static volatile bool exited_;
static volatile bool timeout_;
static volatile bool resized_;
static struct CosmoAudio* ca_;
static struct TtyRgb* ttyrgb_;
@ -150,7 +165,7 @@ static struct ZipGames zipgames_;
static struct Action arrow_, button_;
static struct SamplingSolution* ssy_;
static struct SamplingSolution* ssx_;
static unsigned char (*pixels_)[3][DYN][DXN];
static unsigned char pixels_[3][DYN][DXN];
static unsigned char palette_[3][64][512][3];
static int joy_current_[2], joy_next_[2], joypos_[2];
@ -158,11 +173,6 @@ static int keyframes_ = 10;
static enum TtyBlocksSelection blocks_ = kTtyBlocksUnicode;
static enum TtyQuantizationAlgorithm quant_ = kTtyQuantXterm256;
static struct timespec deadline_;
static std::atomic<void*> pixels_ready_;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static int Clamp(int v) {
return MAX(0, MIN(255, v));
}
@ -222,8 +232,18 @@ void InitPalette(void) {
}
}
static ssize_t Write(int fd, const void* p, size_t n) {
int rc;
sigset_t ss, oldss;
sigfillset(&ss);
sigprocmask(SIG_SETMASK, &ss, &oldss);
rc = write(fd, p, n);
sigprocmask(SIG_SETMASK, &oldss, 0);
return rc;
}
static void WriteString(const char* s) {
write(STDOUT_FILENO, s, strlen(s));
Write(STDOUT_FILENO, s, strlen(s));
}
void Exit(int rc) {
@ -244,10 +264,28 @@ void OnCtrlC(void) {
exited_ = true;
}
void OnTimer(void) {
timeout_ = true;
}
void OnResize(void) {
resized_ = true;
}
void OnPiped(void) {
exited_ = true;
}
void OnSigChld(void) {
waitpid(-1, 0, WNOHANG);
cosmoaudio_close(ca_);
ca_ = 0;
}
void InitFrame(struct Frame* f) {
f->p = f->w = f->mem = (char*)realloc(f->mem, vtsize_);
}
long ChopAxis(long dn, long sn) {
while (HALF(sn) > dn) {
sn = HALF(sn);
@ -270,6 +308,11 @@ void GetTermSize(void) {
G = (unsigned char*)realloc(G, tyn_ * txn_);
B = (unsigned char*)realloc(B, tyn_ * txn_);
ttyrgb_ = (struct TtyRgb*)realloc(ttyrgb_, tyn_ * txn_ * 4);
vtsize_ = ((tyn_ * txn_ * strlen("\e[48;2;255;48;2;255m▄")) +
(tyn_ * strlen("\e[0m\r\n")) + 128);
frame_ = 0;
InitFrame(&vf_[0]);
InitFrame(&vf_[1]);
WriteString("\e[0m\e[H\e[J");
resized_ = false;
}
@ -277,7 +320,11 @@ void GetTermSize(void) {
void IoInit(void) {
GetTermSize();
xsigaction(SIGINT, (void*)OnCtrlC, 0, 0, NULL);
xsigaction(SIGPIPE, (void*)OnPiped, 0, 0, NULL);
xsigaction(SIGWINCH, (void*)OnResize, 0, 0, NULL);
xsigaction(SIGALRM, (void*)OnTimer, 0, 0, NULL);
xsigaction(SIGCHLD, (void*)OnSigChld, 0, 0, NULL);
setitimer(ITIMER_REAL, &kNesFps, NULL);
ttyhidecursor(STDOUT_FILENO);
ttyraw(kTtySigs);
ttyquantsetup(quant_, kTtyQuantRgb, blocks_);
@ -414,29 +461,57 @@ ssize_t ReadKeyboard(void) {
return rc;
}
void ScaleVideoFrameToTeletypewriter(unsigned char (*pixels)[3][DYN][DXN]) {
bool HasVideo(struct Frame* f) {
return f->w < f->p;
}
bool HasPendingVideo(void) {
return HasVideo(&vf_[0]) || HasVideo(&vf_[1]);
}
struct Frame* FlipFrameBuffer(void) {
frame_ = !frame_;
return &vf_[frame_];
}
void TransmitVideo(void) {
ssize_t rc;
struct Frame* f;
f = &vf_[frame_];
if (!HasVideo(f))
f = FlipFrameBuffer();
if ((rc = Write(STDOUT_FILENO, f->w, f->p - f->w)) != -1) {
f->w += rc;
} else if (errno == EAGAIN) {
// slow teletypewriter
} else if (errno == EPIPE) {
Exit(0);
}
}
void ScaleVideoFrameToTeletypewriter(void) {
long y, x, yn, xn;
yn = DYN, xn = DXN;
while (HALF(yn) > tyn_ || HALF(xn) > txn_) {
if (HALF(xn) > txn_) {
Magikarp2xX(DYN, DXN, (*pixels)[0], yn, xn);
Magikarp2xX(DYN, DXN, (*pixels)[1], yn, xn);
Magikarp2xX(DYN, DXN, (*pixels)[2], yn, xn);
Magikarp2xX(DYN, DXN, pixels_[0], yn, xn);
Magikarp2xX(DYN, DXN, pixels_[1], yn, xn);
Magikarp2xX(DYN, DXN, pixels_[2], yn, xn);
xn = HALF(xn);
}
if (HALF(yn) > tyn_) {
Magikarp2xY(DYN, DXN, (*pixels)[0], yn, xn);
Magikarp2xY(DYN, DXN, (*pixels)[1], yn, xn);
Magikarp2xY(DYN, DXN, (*pixels)[2], yn, xn);
Magikarp2xY(DYN, DXN, pixels_[0], yn, xn);
Magikarp2xY(DYN, DXN, pixels_[1], yn, xn);
Magikarp2xY(DYN, DXN, pixels_[2], yn, xn);
yn = HALF(yn);
}
}
GyaradosUint8(tyn_, txn_, R, DYN, DXN, (*pixels)[0], tyn_, txn_, yn, xn, 0,
255, ssy_, ssx_, true);
GyaradosUint8(tyn_, txn_, G, DYN, DXN, (*pixels)[1], tyn_, txn_, yn, xn, 0,
255, ssy_, ssx_, true);
GyaradosUint8(tyn_, txn_, B, DYN, DXN, (*pixels)[2], tyn_, txn_, yn, xn, 0,
255, ssy_, ssx_, true);
GyaradosUint8(tyn_, txn_, R, DYN, DXN, pixels_[0], tyn_, txn_, yn, xn, 0, 255,
ssy_, ssx_, true);
GyaradosUint8(tyn_, txn_, G, DYN, DXN, pixels_[1], tyn_, txn_, yn, xn, 0, 255,
ssy_, ssx_, true);
GyaradosUint8(tyn_, txn_, B, DYN, DXN, pixels_[2], tyn_, txn_, yn, xn, 0, 255,
ssy_, ssx_, true);
for (y = 0; y < tyn_; ++y) {
for (x = 0; x < txn_; ++x) {
ttyrgb_[y * txn_ + x] =
@ -453,104 +528,55 @@ void KeyCountdown(struct Action* a) {
}
}
void Raster(unsigned char (*pixels)[3][DYN][DXN]) {
struct TtyRgb bg = {0x12, 0x34, 0x56, 0};
struct TtyRgb fg = {0x12, 0x34, 0x56, 0};
ScaleVideoFrameToTeletypewriter(pixels);
char* ansi = (char*)malloc((tyn_ * txn_ * strlen("\e[48;2;255;48;2;255m▄")) +
(tyn_ * strlen("\e[0m\r\n")) + 128);
char* p = ansi;
p = stpcpy(p, "\e[0m\e[H");
p = ttyraster(p, ttyrgb_, tyn_, txn_, bg, fg);
free(pixels);
if (status_.wait) {
status_.wait--;
p = stpcpy(p, "\e[0m\e[H");
p = stpcpy(p, status_.text);
}
size_t n = p - ansi;
ssize_t wrote;
for (size_t i = 0; i < n; i += wrote) {
if ((wrote = write(STDOUT_FILENO, ansi + i, n - i)) == -1) {
exited_ = true;
break;
void PollAndSynchronize(void) {
do {
if (ReadKeyboard() == -1) {
if (errno != EINTR)
Exit(1);
if (exited_)
Exit(0);
if (resized_)
GetTermSize();
}
}
free(ansi);
} while (!timeout_);
TransmitVideo();
timeout_ = false;
KeyCountdown(&arrow_);
KeyCountdown(&button_);
joy_next_[0] = arrow_.code | button_.code;
joy_next_[1] = arrow_.code | button_.code;
}
void* RasterThread(void* arg) {
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGINT);
sigaddset(&ss, SIGHUP);
sigaddset(&ss, SIGQUIT);
sigaddset(&ss, SIGTERM);
sigaddset(&ss, SIGPIPE);
sigprocmask(SIG_SETMASK, &ss, 0);
for (;;) {
unsigned char(*pixels)[3][DYN][DXN];
pthread_mutex_lock(&lock);
while (!(pixels = (unsigned char(*)[3][DYN][DXN])pixels_ready_.load()))
pthread_cond_wait(&cond, &lock);
pixels_ready_.store(0);
pthread_mutex_unlock(&lock);
if (resized_)
GetTermSize();
Raster(pixels);
void Raster(void) {
struct Frame* f;
struct TtyRgb bg = {0x12, 0x34, 0x56, 0};
struct TtyRgb fg = {0x12, 0x34, 0x56, 0};
ScaleVideoFrameToTeletypewriter();
f = &vf_[!frame_];
f->p = f->w = f->mem;
f->p = stpcpy(f->p, "\e[0m\e[H");
f->p = ttyraster(f->p, ttyrgb_, tyn_, txn_, bg, fg);
if (status_.wait) {
status_.wait--;
f->p = stpcpy(f->p, "\e[0m\e[H");
f->p = stpcpy(f->p, status_.text);
}
PollAndSynchronize();
}
void FlushScanline(unsigned py) {
if (py != DYN - 1)
return;
pthread_mutex_lock(&lock);
if (!pixels_ready_) {
pixels_ready_.store(pixels_);
pixels_ = 0;
pthread_cond_signal(&cond);
if (py == DYN - 1) {
if (!timeout_)
Raster();
timeout_ = false;
}
pthread_mutex_unlock(&lock);
if (!pixels_)
pixels_ = (unsigned char(*)[3][DYN][DXN])malloc(3 * DYN * DXN);
if (exited_)
Exit(0);
do {
struct timespec now = timespec_mono();
struct timespec remain = timespec_subz(deadline_, now);
int remain_ms = timespec_tomillis(remain);
struct pollfd fds[] = {{STDIN_FILENO, POLLIN}};
int got = poll(fds, 1, remain_ms);
if (got == -1) {
if (errno == EINTR)
continue;
Exit(1);
}
if (got == 1) {
do {
if (ReadKeyboard() == -1) {
if (errno == EINTR)
continue;
Exit(1);
}
} while (0);
}
KeyCountdown(&arrow_);
KeyCountdown(&button_);
joy_next_[0] = arrow_.code | button_.code;
joy_next_[1] = arrow_.code | button_.code;
now = timespec_mono();
do
deadline_ = timespec_add(deadline_, kNesFps);
while (timespec_cmp(deadline_, now) <= 0);
} while (0);
}
static void PutPixel(unsigned px, unsigned py, unsigned pixel, int offset) {
static unsigned prev;
(*pixels_)[0][py][px] = palette_[offset][prev % 64][pixel][2];
(*pixels_)[1][py][px] = palette_[offset][prev % 64][pixel][1];
(*pixels_)[2][py][px] = palette_[offset][prev % 64][pixel][0];
pixels_[0][py][px] = palette_[offset][prev % 64][pixel][2];
pixels_[1][py][px] = palette_[offset][prev % 64][pixel][1];
pixels_[2][py][px] = palette_[offset][prev % 64][pixel][0];
prev = pixel;
}
@ -1706,18 +1732,8 @@ int PlayGame(const char* romfile, const char* opt_tasfile) {
return 3;
}
// initialize screen
pixels_ = (unsigned char(*)[3][DYN][DXN])malloc(3 * DYN * DXN);
InitPalette();
// start raster thread
errno_t err;
pthread_t th;
if ((err = pthread_create(&th, 0, RasterThread, 0))) {
fprintf(stderr, "pthread_create: %s\n", strerror(err));
exit(1);
}
// open speaker
struct CosmoAudioOpenOptions cao = {};
cao.sizeofThis = sizeof(struct CosmoAudioOpenOptions);
@ -1726,9 +1742,6 @@ int PlayGame(const char* romfile, const char* opt_tasfile) {
cao.channels = 1;
cosmoaudio_open(&ca_, &cao);
// initialize time
deadline_ = timespec_add(timespec_mono(), kNesFps);
// Read the ROM file header
u8 rom16count = fgetc(fp);
u8 vrom8count = fgetc(fp);

View file

@ -1,322 +0,0 @@
#/*────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#include <ctype.h>
#include <signal.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
/**
* @fileoverview cosmopolitan flash cards viewer
*/
struct Card {
char* qa[2];
};
atomic_int g_done;
void onsig(int sig) {
g_done = 1;
}
void* xmalloc(int n) {
void* p;
if ((p = malloc(n)))
return p;
perror("malloc");
exit(1);
}
void* xrealloc(void* p, int n) {
if ((p = realloc(p, n)))
return p;
perror("realloc");
exit(1);
}
char* xstrcat(const char* a, const char* b) {
char* p;
size_t n, m;
n = strlen(a);
m = strlen(b);
p = xmalloc(n + m + 1);
memcpy(p, a, n);
memcpy(p + n, b, m + 1);
return p;
}
void shuffle(struct Card* a, int n) {
while (n > 1) {
int i = rand() % n--;
struct Card t = a[i];
a[i] = a[n];
a[n] = t;
}
}
char* trim(char* s) {
int i;
if (s) {
while (isspace(*s))
++s;
for (i = strlen(s); i--;) {
if (isspace(s[i])) {
s[i] = 0;
} else {
break;
}
}
}
return s;
}
char* readline(FILE* f) {
for (;;) {
char* line = trim(fgetln(f, 0));
if (!line)
return 0;
if (*line != '#')
if (*line)
return line;
}
}
char* fill(const char* text, int max_line_width, int* out_line_count) {
int text_len = strlen(text);
char* result = xmalloc(text_len * 2 + 1);
int result_pos = 0;
int line_start = 0;
int line_count = 1;
int i = 0;
while (i < text_len && isspace(text[i]))
i++;
while (i < text_len) {
int word_end = i;
while (word_end < text_len && !isspace(text[word_end]))
word_end++;
int word_length = word_end - i;
if ((result_pos - line_start) + (result_pos > line_start ? 1 : 0) +
word_length >
max_line_width) {
if (result_pos > line_start) {
++line_count;
result[result_pos++] = '\n';
line_start = result_pos;
}
} else if (result_pos > line_start) {
result[result_pos++] = ' ';
}
memcpy(result + result_pos, text + i, word_length);
result_pos += word_length;
i = word_end;
while (i < text_len && isspace(text[i]))
i++;
}
result[result_pos] = '\0';
result = xrealloc(result, result_pos + 1);
if (out_line_count)
*out_line_count = line_count;
return result;
}
void show(const char* text, int i, int n) {
// get pseudoteletypewriter dimensions
struct winsize ws = {80, 25};
tcgetwinsize(1, &ws);
int width = ws.ws_col;
if (width > (int)(ws.ws_col * .9))
width = ws.ws_col * .9;
if (width > 80)
width = 80;
width &= -2;
// clear display
printf("\033[H\033[J");
// display flash card text in middle of display
char buf[32];
int line_count;
char* lines = fill(text, width, &line_count);
sprintf(buf, "%d/%d\r\n\r\n", i + 1, n);
line_count += 2;
char* extra = xstrcat(buf, lines);
free(lines);
char* tokens = extra;
for (int j = 0;; ++j) {
char* line = strtok(tokens, "\n");
tokens = 0;
if (!line)
break;
printf("\033[%d;%dH%s", ws.ws_row / 2 - line_count / 2 + j + 1,
ws.ws_col / 2 - strlen(line) / 2 + 1, line);
}
free(extra);
fflush(stdout);
}
void usage(FILE* f, const char* prog) {
fprintf(f,
"usage: %s FILE\n"
"\n"
"here's an example of what your file should look like:\n"
"\n"
" # cosmopolitan flash cards\n"
" # california dmv drivers test\n"
" \n"
" which of the following point totals could result in "
"your license being suspended by the dmv?\n"
" 4 points in 12 months (middle)\n"
" \n"
" at 55 mph under good conditions a passenger vehicle can stop "
"within\n"
" 300 feet (not 200, not 400, middle)\n"
" \n"
" two sets of solid double yellow lines spaced two or more feet "
"apart indicate\n"
" a BARRIER (do not cross unless there's an opening)\n"
"\n"
"more specifically, empty lines are ignored, lines starting with\n"
"a hash are ignored, then an even number of lines must remain,\n"
"where each two lines is a card, holding question and answer.\n",
prog);
}
int main(int argc, char* argv[]) {
// show help
if (argc != 2) {
usage(stderr, argv[0]);
return 1;
}
if (!strcmp(argv[1], "-?") || //
!strcmp(argv[1], "-h") || //
!strcmp(argv[1], "--help")) {
usage(stdout, argv[0]);
return 0;
}
// teletypewriter is required
if (!isatty(0) || !isatty(1)) {
perror("isatty");
return 2;
}
// load cards
FILE* f = fopen(argv[1], "r");
if (!f) {
perror(argv[1]);
return 3;
}
int count = 0;
struct Card* cards = 0;
for (;;) {
struct Card card;
if (!(card.qa[0] = readline(f)))
break;
card.qa[0] = strdup(card.qa[0]);
if (!(card.qa[1] = readline(f))) {
fprintf(stderr, "%s: flash card file has odd number of lines\n", argv[1]);
exit(1);
}
card.qa[1] = strdup(card.qa[1]);
cards = xrealloc(cards, (count + 1) * sizeof(struct Card));
cards[count++] = card;
}
fclose(f);
// randomize
srand(time(0));
shuffle(cards, count);
// catch ctrl-c
struct sigaction sa;
sa.sa_flags = 0;
sa.sa_handler = onsig;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, 0);
// enter raw mode
struct termios ot;
tcgetattr(1, &ot);
struct termios nt = ot;
cfmakeraw(&nt);
nt.c_lflag |= ISIG;
tcsetattr(1, TCSANOW, &nt);
printf("\033[?25l");
// show flash cards
int i = 0;
while (!g_done) {
show(cards[i / 2].qa[i % 2], i / 2, count);
// press any key
char b[8] = {0};
read(0, b, sizeof(b));
// q quits
if (b[0] == 'q')
break;
// b or ctrl-b goes backward
if (b[0] == 'b' || //
b[0] == ('B' ^ 0100)) {
if (--i < 0)
i = count * 2 - 1;
i &= -2;
continue;
}
// p or ctrl-p goes backward
if (b[0] == 'p' || //
b[0] == ('P' ^ 0100)) {
if (--i < 0)
i = count * 2 - 1;
i &= -2;
continue;
}
// up arrow goes backward
if (b[0] == 033 && //
b[1] == '[' && //
b[2] == 'A') {
if (--i < 0)
i = count * 2 - 1;
i &= -2;
continue;
}
// left arrow goes backward
if (b[0] == 033 && //
b[1] == '[' && //
b[2] == 'D') {
if (--i < 0)
i = count * 2 - 1;
i &= -2;
continue;
}
// only advance
if (++i == count * 2)
i = 0;
}
// free memory
for (int i = 0; i < count; ++i)
for (int j = 0; j < 2; ++j)
free(cards[i].qa[j]);
free(cards);
// cleanup terminal and show cursor
tcsetattr(1, TCSANOW, &ot);
printf("\033[?25h");
printf("\n");
}

View file

@ -49,10 +49,10 @@
* @fileoverview Terminal Screencast Recorder / Player, e.g.
*
* make o//examples/script.com
* o//examples/script.com -w80 -h24 -r recording.tty
* o//examples/script.com -r
* # type stuff..
* # CTRL-D
* o//examples/script.com -p recording.tty
* o//examples/script.com -p typescript
*
* @note works on Linux, OpenBSD, NetBSD, FreeBSD, MacOS
* @see https://asciinema.org/
@ -103,9 +103,9 @@ main(int argc, char *argv[])
fd_set rfd;
int fm_fd;
int aflg, Fflg, kflg, pflg, ch, k, n;
int flushtime, readstdin, width, height;
int flushtime, readstdin;
aflg = Fflg = kflg = pflg = height = width = 0;
aflg = Fflg = kflg = pflg = 0;
usesleep = 1;
rawout = 0;
flushtime = 30;
@ -115,7 +115,7 @@ main(int argc, char *argv[])
(void)fm_fd;
while ((ch = getopt(argc, argv, "adeFfkpqrt:w:h:")) != -1)
while ((ch = getopt(argc, argv, "adeFfkpqrt:")) != -1)
switch(ch) {
case 'a':
aflg = 1;
@ -145,12 +145,6 @@ main(int argc, char *argv[])
if (flushtime < 0)
err(1, "invalid flush time %d", flushtime);
break;
case 'w':
width = atoi(optarg);
break;
case 'h':
height = atoi(optarg);
break;
case '?':
default:
usage();
@ -178,10 +172,6 @@ main(int argc, char *argv[])
if (openpty(&master, &slave, NULL, NULL, NULL) == -1)
err(1, "openpty");
} else {
if (width)
win.ws_col = width;
if (height)
win.ws_row = height;
if (openpty(&master, &slave, NULL, &tt, &win) == -1)
err(1, "openpty");
ttyflg = 1;

View file

@ -7,13 +7,9 @@
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/dce.h"
#include "libc/intrin/maps.h"
#include "libc/mem/alg.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/runtime/winargs.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/x/xasprintf.h"
@ -71,18 +67,8 @@ int main(int argc, char *argv[]) {
Append((uintptr_t)&__auxv[i + 1],
xasprintf("&auxv[%d] = %#lx", i + 1, __auxv[i + 1]));
}
if (!IsWindows()) {
struct AddrSize stak = __get_main_stack();
Append((intptr_t)stak.addr + stak.size, "top of stack");
Append((intptr_t)stak.addr, "bottom of stack");
} else {
#ifdef __x86_64__
Append(GetStaticStackAddr(0) + GetStaticStackSize(), "top of stack");
Append(GetStaticStackAddr(0) + GetGuardSize(), "bottom of stack");
Append(GetStaticStackAddr(0), "bottom of guard region");
#endif
}
qsort(things.p, things.n, sizeof(*things.p), Compare);
for (int i = 0; i < things.n; ++i)
for (int i = 0; i < things.n; ++i) {
printf("%012lx %s\n", things.p[i].i, things.p[i].s);
}
}

View file

@ -1,17 +0,0 @@
#include <pthread.h>
#include <stdio.h>
// how to spawn a thread
void *my_thread(void *arg) {
printf("my_thread(%p) is running\n", arg);
return (void *)0x456L;
}
int main(int argc, char *argv[]) {
void *res;
pthread_t th;
pthread_create(&th, 0, my_thread, (void *)0x123L);
pthread_join(th, &res);
printf("my_thread() returned %p\n", res);
}

View file

@ -216,6 +216,12 @@ o//libc/calls/writev.o: private \
-mgeneral-regs-only
# these assembly files are safe to build on aarch64
o/$(MODE)/libc/calls/getcontext.o: libc/calls/getcontext.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/libc/calls/swapcontext.o: libc/calls/swapcontext.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/libc/calls/tailcontext.o: libc/calls/tailcontext.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/libc/calls/stackjump.o: libc/calls/stackjump.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<

View file

@ -59,7 +59,7 @@ textwindows int sys_clock_gettime_nt(int clock, struct timespec *ts) {
// —Quoth MSDN § Windows Time
//
QueryUnbiasedInterruptTimePrecise(&hectons);
*ts = WindowsDurationToTimeSpec(hectons);
*ts = timespec_fromnanos(hectons * 100);
return 0;
case _CLOCK_MONOTONIC_COARSE:
//
@ -83,7 +83,7 @@ textwindows int sys_clock_gettime_nt(int clock, struct timespec *ts) {
// —Quoth MSDN § QueryUnbiasedInterruptTimePrecise
//
QueryUnbiasedInterruptTime(&hectons);
*ts = WindowsDurationToTimeSpec(hectons);
*ts = timespec_fromnanos(hectons * 100);
return 0;
case _CLOCK_BOOTTIME:
//
@ -95,7 +95,7 @@ textwindows int sys_clock_gettime_nt(int clock, struct timespec *ts) {
// —Quoth MSDN § Interrupt Time
//
QueryInterruptTimePrecise(&hectons);
*ts = WindowsDurationToTimeSpec(hectons);
*ts = timespec_fromnanos(hectons * 100);
return 0;
case _CLOCK_PROCESS_CPUTIME_ID:
GetProcessTimes(GetCurrentProcess(), &ftCreation, &ftExit, &ftKernel,

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/atomic.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/timespec.h"
@ -24,37 +23,26 @@
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/ntdll.h"
#include "libc/stdio/sysparam.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/timer.h"
#include "libc/thread/tls.h"
#ifdef __x86_64__
static atomic_int usingRes;
static atomic_bool changedRes;
static textwindows int sys_clock_nanosleep_nt_impl(int clock,
struct timespec abs,
sigset_t waitmask) {
struct timespec now, wall;
uint32_t minRes, maxRes, oldRes;
sys_clock_gettime_nt(0, &wall);
if (sys_clock_gettime_nt(clock, &now))
return -1;
bool wantRes = clock == CLOCK_REALTIME || //
clock == CLOCK_MONOTONIC || //
clock == CLOCK_BOOTTIME;
if (wantRes && !atomic_fetch_add(&usingRes, 1))
changedRes = NtSuccess(NtQueryTimerResolution(&minRes, &maxRes, &oldRes)) &&
NtSuccess(NtSetTimerResolution(maxRes, true, &oldRes));
if (timespec_cmp(abs, now) > 0)
wall = timespec_add(wall, timespec_sub(abs, now));
int rc = _park_norestart(wall, waitmask);
if (wantRes && atomic_fetch_sub(&usingRes, 1) == 1 && changedRes)
NtSetTimerResolution(0, false, &minRes);
return rc;
uint32_t msdelay;
struct timespec now;
for (;;) {
if (sys_clock_gettime_nt(clock, &now))
return -1;
if (timespec_cmp(now, abs) >= 0)
return 0;
msdelay = timespec_tomillis(timespec_sub(abs, now));
msdelay = MIN(msdelay, -1u);
if (_park_norestart(msdelay, waitmask))
return -1;
}
}
textwindows int sys_clock_nanosleep_nt(int clock, int flags,

View file

@ -23,9 +23,9 @@
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/errfuns.h"
relegated int sys_clock_nanosleep_openbsd(int clock, int flags,
const struct timespec *req,
struct timespec *rem) {
int sys_clock_nanosleep_openbsd(int clock, int flags,
const struct timespec *req,
struct timespec *rem) {
int res;
struct timespec start, relative, remainder;
if (!flags) {

View file

@ -57,7 +57,6 @@
*
* @param clock may be
* - `CLOCK_REALTIME`
* - `CLOCK_BOOTTIME`
* - `CLOCK_MONOTONIC`
* - `CLOCK_REALTIME_COARSE` but is likely to sleep negative time
* - `CLOCK_MONTONIC_COARSE` but is likely to sleep negative time

View file

@ -83,11 +83,9 @@ static void copy_file_range_init(void) {
* @return number of bytes transferred, or -1 w/ errno
* @raise EXDEV if source and destination are on different filesystems
* @raise EBADF if `infd` or `outfd` aren't open files or append-only
* @raise EOPNOTSUPP if filesystem doesn't support this operation
* @raise EPERM if `fdout` refers to an immutable file on Linux
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINVAL if ranges overlap or `flags` is non-zero
* @raise EINVAL on eCryptFs filesystems that have a bug
* @raise EFBIG if `setrlimit(RLIMIT_FSIZE)` is exceeded
* @raise EFAULT if one of the pointers memory is bad
* @raise ERANGE if overflow happens computing ranges

View file

@ -17,10 +17,26 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/atomic.h"
#include "libc/fmt/internal.h"
#include "libc/intrin/atomic.h"
#include "libc/runtime/internal.h"
static textwindows char16_t *itoa16(char16_t p[21], uint64_t x) {
char t;
size_t a, b, i = 0;
do {
p[i++] = x % 10 + '0';
x = x / 10;
} while (x > 0);
if (i) {
for (a = 0, b = i - 1; a < b; ++a, --b) {
t = p[a];
p[a] = p[b];
p[b] = t;
}
}
return p + i;
}
// This function is called very early by WinMain().
textwindows char16_t *__create_pipe_name(char16_t *a) {
char16_t *p = a;
@ -28,9 +44,9 @@ textwindows char16_t *__create_pipe_name(char16_t *a) {
static atomic_uint x;
while (*q)
*p++ = *q++;
p = __itoa16(p, __pid);
p = itoa16(p, __pid);
*p++ = '-';
p = __itoa16(p, atomic_fetch_add(&x, 1));
p = itoa16(p, atomic_fetch_add(&x, 1));
*p = 0;
return a;
}

View file

@ -51,7 +51,6 @@
#include "libc/sysv/consts/fio.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
struct FileLock {
@ -68,9 +67,7 @@ struct FileLocks {
struct FileLock *free;
};
static struct FileLocks g_locks = {
.mu = PTHREAD_MUTEX_INITIALIZER,
};
static struct FileLocks g_locks;
static textwindows struct FileLock *NewFileLock(void) {
struct FileLock *fl;
@ -113,7 +110,7 @@ static textwindows bool EqualsFileLock(struct FileLock *fl, int64_t off,
textwindows void sys_fcntl_nt_lock_cleanup(int fd) {
struct FileLock *fl, *ft, **flp;
_pthread_mutex_lock(&g_locks.mu);
pthread_mutex_lock(&g_locks.mu);
for (flp = &g_locks.list, fl = *flp; fl;) {
if (fl->fd == fd) {
*flp = fl->next;
@ -125,7 +122,7 @@ textwindows void sys_fcntl_nt_lock_cleanup(int fd) {
fl = *flp;
}
}
_pthread_mutex_unlock(&g_locks.mu);
pthread_mutex_unlock(&g_locks.mu);
}
static textwindows int64_t GetfileSize(int64_t handle) {
@ -356,9 +353,9 @@ textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) {
} else if (cmd == F_SETLK || cmd == F_SETLKW || cmd == F_GETLK) {
struct Fd *f = g_fds.p + fd;
if (f->cursor) {
_pthread_mutex_lock(&g_locks.mu);
pthread_mutex_lock(&g_locks.mu);
rc = sys_fcntl_nt_lock(f, fd, cmd, arg);
_pthread_mutex_unlock(&g_locks.mu);
pthread_mutex_unlock(&g_locks.mu);
} else {
rc = ebadf();
}

View file

@ -122,29 +122,34 @@ textwindows int sys_fstat_nt_handle(int64_t handle, const char16_t *path,
st.st_blksize = 4096;
st.st_gid = st.st_uid = sys_getuid_nt();
// We'll use the "umask" to fake out the mode bits.
uint32_t umask = atomic_load_explicit(&__umask, memory_order_acquire);
switch (GetFileType(handle)) {
case kNtFileTypeUnknown:
break;
case kNtFileTypeChar:
st.st_mode = S_IFCHR | 0664;
st.st_mode = S_IFCHR | (0666 & ~umask);
st.st_dev = 0x66666666;
st.st_ino = handle;
break;
case kNtFileTypePipe:
st.st_mode = S_IFIFO | 0664;
st.st_mode = S_IFIFO | (0666 & ~umask);
st.st_dev = 0x55555555;
st.st_ino = handle;
break;
case kNtFileTypeDisk: {
struct NtByHandleFileInformation wst;
if (GetFileInformationByHandle(handle, &wst)) {
st.st_mode = 0444;
st.st_mode = 0444 & ~umask;
if ((wst.dwFileAttributes & kNtFileAttributeDirectory) ||
IsWindowsExecutable(handle, path))
st.st_mode |= 0111;
IsWindowsExecutable(handle, path)) {
st.st_mode |= 0111 & ~umask;
}
st.st_flags = wst.dwFileAttributes;
if (!(wst.dwFileAttributes & kNtFileAttributeReadonly))
st.st_mode |= 0220;
if (!(wst.dwFileAttributes & kNtFileAttributeReadonly)) {
st.st_mode |= 0222 & ~umask;
}
if (wst.dwFileAttributes & kNtFileAttributeReparsePoint) {
st.st_mode |= S_IFLNK;
} else if (wst.dwFileAttributes & kNtFileAttributeDirectory) {
@ -190,7 +195,7 @@ textwindows int sys_fstat_nt_handle(int64_t handle, const char16_t *path,
} else if (GetVolumeInformationByHandle(
handle, 0, 0, &wst.dwVolumeSerialNumber, 0, 0, 0, 0)) {
st.st_dev = wst.dwVolumeSerialNumber;
st.st_mode = S_IFDIR | 0555;
st.st_mode = S_IFDIR | (0444 & ~umask);
} else {
// both GetFileInformationByHandle and
// GetVolumeInformationByHandle failed

View file

@ -26,9 +26,7 @@
// @see setcontext()
.ftrace1
getcontext:
beg
.ftrace2
#include "libc/intrin/getcontext.inc"
#include "libc/calls/getcontext.inc"
jmp __getcontextsig
end
.endfn getcontext,globl

View file

@ -34,7 +34,6 @@
mov %rbp,120(%rdi)
mov %rbx,128(%rdi)
mov %rdx,136(%rdi)
mov %rax,144(%rdi)
mov %rcx,152(%rdi)
lea 8(%rsp),%rax
mov %rax,160(%rdi) // rsp = caller's rsp

View file

@ -21,6 +21,7 @@
#include "libc/dce.h"
#include "libc/intrin/describeflags.h"
#include "libc/intrin/strace.h"
#include "libc/stdckdint.h"
#include "libc/sysv/errfuns.h"
/**

View file

@ -21,23 +21,24 @@
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/fmt/conv.h"
#include "libc/intrin/cxaatexit.h"
#include "libc/macros.h"
#include "libc/nt/accounting.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/thread.h"
#define CTOR __attribute__((__constructor__(99)))
#define FT(x) (x.dwLowDateTime | (uint64_t)x.dwHighDateTime << 32)
static int cpus;
static double load;
static pthread_spinlock_t lock;
static struct NtFileTime idle1, kern1, user1;
textwindows int sys_getloadavg_nt(double *a, int n) {
int i, rc;
uint64_t elapsed, used;
struct NtFileTime idle, kern, user;
__cxa_lock();
BLOCK_SIGNALS;
pthread_spin_lock(&lock);
if (GetSystemTimes(&idle, &kern, &user)) {
elapsed = (FT(kern) - FT(kern1)) + (FT(user) - FT(user1));
if (elapsed) {
@ -53,11 +54,12 @@ textwindows int sys_getloadavg_nt(double *a, int n) {
} else {
rc = __winerr();
}
__cxa_unlock();
pthread_spin_unlock(&lock);
ALLOW_SIGNALS;
return rc;
}
CTOR static textstartup void sys_getloadavg_nt_init(void) {
__attribute__((__constructor__(40))) static textstartup void ntinitload(void) {
if (IsWindows()) {
load = 1;
cpus = __get_cpu_count() / 2;

View file

@ -28,8 +28,8 @@
// @return rdi is rdi+edx
.text.startup
__getntsyspath:
beg
pro
push %rbp
mov %rsp,%rbp
push %rdx
movpp %rdi,%rcx # call f=%rax(p1=%rcx,p2=%rdx)
sub $40,%rsp
@ -55,7 +55,6 @@ __getntsyspath:
jne 2f
movb $'/',-1(%rdi)
2: .loop 1b
epi
leave
ret
end
.endfn __getntsyspath,globl,hidden

View file

@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2024 Justine Alexandra Roberts Tunney
Copyright 2021 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
@ -16,14 +16,22 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/nt/memory.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/nt/process.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/processbasicinformation.h"
/**
* Allocates memory on The New Technology.
*/
textwindows void *VirtualAlloc(void *lpAddress, uint64_t dwSize,
uint32_t flAllocationType, uint32_t flProtect) {
return VirtualAllocEx(GetCurrentProcess(), lpAddress, dwSize,
flAllocationType, flProtect);
textwindows int sys_getppid_nt(void) {
struct NtProcessBasicInformation ProcessInformation;
uint32_t gotsize = 0;
if (!NtError(
NtQueryInformationProcess(GetCurrentProcess(), 0, &ProcessInformation,
sizeof(ProcessInformation), &gotsize)) &&
gotsize >= sizeof(ProcessInformation) &&
ProcessInformation.InheritedFromUniqueProcessId) {
return ProcessInformation.InheritedFromUniqueProcessId;
}
return GetCurrentProcessId();
}

View file

@ -96,8 +96,9 @@ static int OldApeLoader(char *s) {
static int CopyWithCwd(const char *q, char *p, char *e) {
char c;
if (*q != '/') {
if (q[0] == '.' && q[1] == '/')
if (q[0] == '.' && q[1] == '/') {
q += 2;
}
int got = __getcwd(p, e - p - 1 /* '/' */);
if (got != -1) {
p += got - 1;
@ -117,10 +118,9 @@ static int CopyWithCwd(const char *q, char *p, char *e) {
// if q exists then turn it into an absolute path.
static int TryPath(const char *q) {
if (!q)
return 0;
if (!CopyWithCwd(q, g_prog.u.buf, g_prog.u.buf + sizeof(g_prog.u.buf)))
if (!CopyWithCwd(q, g_prog.u.buf, g_prog.u.buf + sizeof(g_prog.u.buf))) {
return 0;
}
return !sys_faccessat(AT_FDCWD, g_prog.u.buf, F_OK, 0);
}
@ -129,8 +129,9 @@ static int TryPath(const char *q) {
void __init_program_executable_name(void) {
if (__program_executable_name && *__program_executable_name != '/' &&
CopyWithCwd(__program_executable_name, g_prog.u.buf,
g_prog.u.buf + sizeof(g_prog.u.buf)))
g_prog.u.buf + sizeof(g_prog.u.buf))) {
__program_executable_name = g_prog.u.buf;
}
}
static inline void InitProgramExecutableNameImpl(void) {
@ -211,12 +212,14 @@ static inline void InitProgramExecutableNameImpl(void) {
}
// don't trust argv or envp if set-id.
if (issetugid())
if (issetugid()) {
goto UseEmpty;
}
// try argv[0], then then $_.
if (TryPath(__argv[0]) || TryPath(__getenv(__envp, "_").s))
if (TryPath(__argv[0]) || TryPath(__getenv(__envp, "_").s)) {
goto UseBuf;
}
// give up and just copy argv[0] into it
if ((q = __argv[0])) {

View file

@ -114,8 +114,7 @@ static ssize_t GetDevUrandom(char *p, size_t n, unsigned f) {
ssize_t __getrandom(void *p, size_t n, unsigned f) {
ssize_t rc;
if (IsWindows()) {
ProcessPrng(p, n); // never fails
rc = n;
rc = ProcessPrng(p, n) ? n : __winerr();
} else if (have_getrandom) {
if (IsXnu() || IsOpenbsd()) {
rc = GetRandomBsd(p, n, GetRandomEntropy);
@ -185,7 +184,9 @@ ssize_t __getrandom(void *p, size_t n, unsigned f) {
* @raise EFAULT if the `n` bytes at `p` aren't valid memory
* @raise EINTR if we needed to block and a signal was delivered instead
* @cancelationpoint
* @asyncsignalsafe
* @restartable
* @vforksafe
*/
ssize_t getrandom(void *p, size_t n, unsigned f) {
ssize_t rc;

View file

@ -21,7 +21,6 @@
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/describeflags.h"
#include "libc/intrin/rlimit.h"
#include "libc/intrin/strace.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
@ -48,7 +47,8 @@ int getrlimit(int resource, struct rlimit *rlim) {
} else if (!IsWindows()) {
rc = sys_getrlimit(resource, rlim);
} else if (resource == RLIMIT_STACK) {
*rlim = __rlimit_stack_get();
rlim->rlim_cur = GetStaticStackSize();
rlim->rlim_max = GetStaticStackSize();
rc = 0;
} else if (resource == RLIMIT_AS) {
rlim->rlim_cur = __virtualmax;

View file

@ -3,7 +3,6 @@
#include "libc/atomic.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigval.h"
#include "libc/calls/struct/timespec.h"
#include "libc/dce.h"
#include "libc/intrin/fds.h"
#include "libc/macros.h"
@ -31,10 +30,8 @@ int CountConsoleInputBytes(void);
int FlushConsoleInputBytes(void);
int64_t GetConsoleInputHandle(void);
int64_t GetConsoleOutputHandle(void);
void EchoConsoleNt(const char *, size_t, bool);
int IsWindowsExecutable(int64_t, const char16_t *);
void InterceptTerminalCommands(const char *, size_t);
void sys_read_nt_wipe_keystrokes(void);
forceinline bool __isfdopen(int fd) {
return 0 <= fd && fd < g_fds.n && g_fds.p[fd].kind != kFdEmpty;
@ -48,8 +45,8 @@ int _check_signal(bool);
int _check_cancel(void);
bool _is_canceled(void);
int sys_close_nt(int, int);
int _park_norestart(struct timespec, uint64_t);
int _park_restartable(struct timespec, uint64_t);
int _park_norestart(uint32_t, uint64_t);
int _park_restartable(uint32_t, uint64_t);
int sys_openat_metal(int, const char *, int, unsigned);
#ifdef __x86_64__

View file

@ -1,6 +1,5 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_MAKEDEV_H_
#define COSMOPOLITAN_LIBC_CALLS_MAKEDEV_H_
COSMOPOLITAN_C_START_
uint64_t makedev(uint32_t, uint32_t) libcesque;
uint32_t major(uint64_t) libcesque;
@ -10,5 +9,4 @@ uint32_t minor(uint64_t) libcesque;
#define minor(x) minor(x)
#define makedev(x, y) makedev(x, y)
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_CALLS_MAKEDEV_H_ */

View file

@ -67,9 +67,10 @@ textstartup void InitializeMetalFile(void) {
size_t size = ROUNDUP(_ezip - __executable_start, 4096);
// TODO(jart): Restore support for ZIPOS on metal.
void *copied_base;
void *addr = sys_mmap_metal(NULL, size, PROT_READ | PROT_WRITE,
MAP_SHARED_linux | MAP_ANONYMOUS_linux, -1, 0);
copied_base = addr;
struct DirectMap dm;
dm = sys_mmap_metal(NULL, size, PROT_READ | PROT_WRITE,
MAP_SHARED_linux | MAP_ANONYMOUS_linux, -1, 0);
copied_base = dm.addr;
npassert(copied_base != (void *)-1);
memcpy(copied_base, (void *)(BANE + IMAGE_BASE_PHYSICAL), size);
__ape_com_base = copied_base;

View file

@ -17,7 +17,6 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/proc/ntspawn.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/intrin/strace.h"
@ -39,15 +38,12 @@
#include "libc/nt/struct/procthreadattributelist.h"
#include "libc/nt/struct/startupinfo.h"
#include "libc/nt/struct/startupinfoex.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/proc/ntspawn.h"
#include "libc/stdalign.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
#ifdef __x86_64__
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
struct SpawnBlock {
char16_t path[PATH_MAX];
char16_t cmdline[32767];
@ -67,12 +63,10 @@ static textwindows ssize_t ntspawn_read(intptr_t fh, char *buf, size_t len) {
bool ok;
uint32_t got;
struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 0, 0, 0)};
ok = overlap.hEvent &&
(ReadFile(fh, buf, len, 0, &overlap) ||
ok = (ReadFile(fh, buf, len, 0, &overlap) ||
GetLastError() == kNtErrorIoPending) &&
GetOverlappedResult(fh, &overlap, &got, true);
if (overlap.hEvent)
__imp_CloseHandle(overlap.hEvent);
CloseHandle(overlap.hEvent);
return ok ? got : -1;
}
@ -92,7 +86,7 @@ static textwindows int ntspawn2(struct NtSpawnArgs *a, struct SpawnBlock *sb) {
if (fh == -1)
return -1;
ssize_t got = ntspawn_read(fh, p, pe - p);
__imp_CloseHandle(fh);
CloseHandle(fh);
if (got < 3)
return enoexec();
pe = p + got;

View file

@ -49,9 +49,11 @@ int sys_openat_metal(int dirfd, const char *file, int flags, unsigned mode) {
if ((fd = __reservefd(-1)) == -1)
return -1;
if (!_weaken(calloc) || !_weaken(free)) {
state = sys_mmap_metal(NULL, ROUNDUP(sizeof(struct MetalFile), 4096),
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
-1, 0);
struct DirectMap dm;
dm = sys_mmap_metal(NULL, ROUNDUP(sizeof(struct MetalFile), 4096),
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1,
0);
state = dm.addr;
if (state == (void *)-1)
return -1;
} else {

View file

@ -17,98 +17,37 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/fmt/wintime.internal.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/weaken.h"
#include "libc/nt/events.h"
#include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#ifdef __x86_64__
// returns 0 if deadline is reached
// returns 0 on timeout or spurious wakeup
// raises EINTR if a signal delivery interrupted wait operation
// raises ECANCELED if this POSIX thread was canceled in masked mode
textwindows static int _park_thread(struct timespec deadline, sigset_t waitmask,
textwindows static int _park_thread(uint32_t msdelay, sigset_t waitmask,
bool restartable) {
for (;;) {
uint32_t handl = 0;
intptr_t hands[2];
// create event object
intptr_t sigev;
if (!(sigev = CreateEvent(0, 0, 0, 0)))
return __winerr();
hands[handl++] = sigev;
// create high precision timer if needed
if (memcmp(&deadline, &timespec_max, sizeof(struct timespec))) {
intptr_t hTimer;
if ((hTimer = CreateWaitableTimer(NULL, true, NULL))) {
int64_t due = TimeSpecToWindowsTime(deadline);
if (SetWaitableTimer(hTimer, &due, 0, NULL, NULL, false)) {
hands[handl++] = hTimer;
} else {
CloseHandle(hTimer);
}
}
}
// perform wait operation
struct PosixThread *pt = _pthread_self();
pt->pt_event = sigev;
pt->pt_blkmask = waitmask;
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_EVENT,
memory_order_release);
//!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!//
int sig = 0;
uint32_t wi = 0;
if (!_is_canceled() &&
!(_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))))
wi = WaitForMultipleObjects(handl, hands, false, -1u);
//!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!//
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
for (int i = 0; i < handl; ++i)
CloseHandle(hands[i]);
// recursion is now safe
if (wi == 1)
return 0;
if (wi == -1u)
return __winerr();
int handler_was_called = 0;
if (!sig) {
if (_check_cancel())
return -1;
if (_weaken(__sig_get))
sig = _weaken(__sig_get)(waitmask);
}
if (sig)
handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
if (_check_cancel())
return -1;
if (handler_was_called & SIG_HANDLED_NO_RESTART)
return eintr();
if (handler_was_called & SIG_HANDLED_SA_RESTART)
if (!restartable)
return eintr();
}
if (__sigcheck(waitmask, restartable) == -1)
return -1;
int expect = 0;
atomic_int futex = 0;
struct PosixThread *pt = _pthread_self();
pt->pt_blkmask = waitmask;
atomic_store_explicit(&pt->pt_blocker, &futex, memory_order_release);
bool32 ok = WaitOnAddress(&futex, &expect, sizeof(int), msdelay);
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
if (ok && __sigcheck(waitmask, restartable) == -1)
return -1;
return 0;
}
textwindows int _park_norestart(struct timespec deadline, sigset_t waitmask) {
return _park_thread(deadline, waitmask, false);
textwindows int _park_norestart(uint32_t msdelay, sigset_t waitmask) {
return _park_thread(msdelay, waitmask, false);
}
textwindows int _park_restartable(struct timespec deadline, sigset_t waitmask) {
return _park_thread(deadline, waitmask, true);
textwindows int _park_restartable(uint32_t msdelay, sigset_t waitmask) {
return _park_thread(msdelay, waitmask, true);
}
#endif /* __x86_64__ */

View file

@ -17,21 +17,14 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/syscall_support-nt.internal.h"
#ifdef __x86_64__
textwindows int sys_pause_nt(void) {
// we don't strictly need to block signals, but it reduces signal
// delivery latency, by preventing other threads from delivering a
// signal asynchronously. it takes about ~5us to deliver a signal
// using SetEvent() whereas it takes ~30us to use SuspendThread(),
// GetThreadContext(), SetThreadContext(), and ResumeThread().
BLOCK_SIGNALS;
_park_norestart(timespec_max, 0);
ALLOW_SIGNALS;
return -1;
int rc;
while (!(rc = _park_norestart(-1u, 0)))
donothing;
return rc;
}
#endif /* __x86_64__ */

View file

@ -56,6 +56,7 @@ static textwindows int sys_pipe_nt_impl(int pipefd[2], unsigned flags) {
__fds_unlock();
hin = CreateNamedPipe(pipename, kNtPipeAccessInbound | kNtFileFlagOverlapped,
mode, 1, PIPE_BUF, PIPE_BUF, 0, &kNtIsInheritable);
__fds_lock();
if (hin != -1) {
if ((hout = CreateFile(
pipename, kNtGenericWrite,
@ -72,6 +73,7 @@ static textwindows int sys_pipe_nt_impl(int pipefd[2], unsigned flags) {
g_fds.p[writer].handle = hout;
pipefd[0] = reader;
pipefd[1] = writer;
__fds_unlock();
return 0;
} else {
CloseHandle(hin);
@ -79,6 +81,7 @@ static textwindows int sys_pipe_nt_impl(int pipefd[2], unsigned flags) {
}
__releasefd(writer);
__releasefd(reader);
__fds_unlock();
return -1;
}

View file

@ -694,7 +694,6 @@ static const uint16_t kPledgeStdio[] = {
__NR_linux_sched_getaffinity, //
__NR_linux_sched_setaffinity, //
__NR_linux_sigtimedwait, //
__NR_linux_getcpu, //
};
static const uint16_t kPledgeFlock[] = {

View file

@ -17,7 +17,6 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigset.internal.h"
@ -26,7 +25,6 @@
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/fds.h"
#include "libc/intrin/weaken.h"
#include "libc/macros.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/filetype.h"
@ -43,7 +41,6 @@
#include "libc/sock/internal.h"
#include "libc/sock/struct/pollfd.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#ifdef __x86_64__
@ -87,7 +84,7 @@ textwindows static int sys_poll_nt_actual(struct pollfd *fds, uint64_t nfds,
struct PosixThread *pt;
int i, rc, ev, kind, gotsocks;
struct sys_pollfd_nt sockfds[64];
uint32_t cm, fi, sn, pn, avail, waitfor, already_slept;
uint32_t cm, fi, wi, sn, pn, avail, waitfor, already_slept;
// ensure revents is cleared
for (i = 0; i < nfds; ++i)
@ -231,36 +228,22 @@ textwindows static int sys_poll_nt_actual(struct pollfd *fds, uint64_t nfds,
// this ensures low latency for apps like emacs which with no sock
// here we shall actually report that something can be written too
if (!already_slept) {
intptr_t sigev;
if (!(sigev = CreateEvent(0, 0, 0, 0)))
return __winerr();
filehands[pn] = sigev;
if (__sigcheck(waitmask, false))
return -1;
pt = _pthread_self();
pt->pt_event = sigev;
pt->pt_blkmask = waitmask;
filehands[pn] = pt->pt_event = CreateEvent(0, 0, 0, 0);
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_EVENT,
memory_order_release);
//!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!//
int sig = 0;
uint32_t wi = pn;
if (!_is_canceled() &&
!(_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))))
wi = WaitForMultipleObjects(pn + 1, filehands, 0, waitfor);
//!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!//
wi = WaitForMultipleObjects(pn + 1, filehands, 0, waitfor);
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
CloseHandle(sigev);
if (wi == -1u)
CloseHandle(filehands[pn]);
if (wi == -1u) {
// win32 wait failure
return __winerr();
if (wi == pn) {
} else if (wi == pn) {
// our signal event was signalled
int handler_was_called = 0;
if (sig)
handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
if (_check_cancel() == -1)
if (__sigcheck(waitmask, false))
return -1;
if (handler_was_called)
return eintr();
} else if ((wi ^ kNtWaitAbandoned) < pn) {
// this is possibly because a process or thread was killed
fds[fileindices[wi ^ kNtWaitAbandoned]].revents = POLLERR_;
@ -304,6 +287,8 @@ textwindows static int sys_poll_nt_actual(struct pollfd *fds, uint64_t nfds,
} else {
// should only be possible on kNtWaitTimeout or semaphore abandoned
// keep looping for events and we'll catch timeout when appropriate
if (__sigcheck(waitmask, false))
return -1;
}
}
@ -318,15 +303,8 @@ textwindows static int sys_poll_nt_actual(struct pollfd *fds, uint64_t nfds,
textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
struct timespec deadline,
const sigset_t waitmask) {
uint32_t waitms;
int i, n, rc, got = 0;
struct timespec now, next, target;
// we normally don't check for signals until we decide to wait, since
// it's nice to have functions like write() be unlikely to EINTR, but
// ppoll is a function where users are surely thinking about signals,
// since ppoll actually allows them to block signals everywhere else.
if (__sigcheck(waitmask, false))
return -1;
// fast path
if (nfds <= 63)
@ -344,28 +322,20 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
}
if (got)
return got;
now = sys_clock_gettime_monotonic_nt();
if (timespec_cmp(now, deadline) >= 0)
if (!(waitms = sys_poll_nt_waitms(deadline)))
return 0;
next = timespec_add(now, timespec_frommillis(POLL_INTERVAL_MS));
if (timespec_cmp(next, deadline) >= 0) {
target = deadline;
} else {
target = next;
}
if (_park_norestart(target, waitmask) == -1)
if (_park_norestart(waitms, waitmask) == -1)
return -1;
}
}
textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds,
const struct timespec *relative,
textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
const sigset_t *sigmask) {
int rc;
struct timespec now, timeout, deadline;
BLOCK_SIGNALS;
now = relative ? sys_clock_gettime_monotonic_nt() : timespec_zero;
timeout = relative ? *relative : timespec_max;
now = ms ? sys_clock_gettime_monotonic_nt() : timespec_zero;
timeout = ms ? timespec_frommillis(*ms) : timespec_max;
deadline = timespec_add(now, timeout);
rc = sys_poll_nt_impl(fds, nfds, deadline, sigmask ? *sigmask : _SigMask);
ALLOW_SIGNALS;

View file

@ -25,7 +25,6 @@
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/strace.h"
#include "libc/limits.h"
#include "libc/runtime/stack.h"
#include "libc/sock/struct/pollfd.h"
#include "libc/sock/struct/pollfd.internal.h"
@ -77,13 +76,10 @@ static int ppoll_impl(struct pollfd *fds, size_t nfds,
}
fdcount = sys_ppoll(fds, nfds, tsp, sigmask, 8);
if (fdcount == -1 && errno == ENOSYS) {
int64_t ms;
int ms;
errno = e;
if (timeout) {
ms = timespec_tomillis(*timeout);
if (ms > INT_MAX)
ms = -1;
} else {
if (!timeout || ckd_add(&ms, timeout->tv_sec,
(timeout->tv_nsec + 999999) / 1000000)) {
ms = -1;
}
if (sigmask)
@ -93,7 +89,15 @@ static int ppoll_impl(struct pollfd *fds, size_t nfds,
sys_sigprocmask(SIG_SETMASK, &oldmask, 0);
}
} else {
fdcount = sys_poll_nt(fds, nfds, timeout, sigmask);
uint32_t ms;
uint32_t *msp;
if (timeout &&
!ckd_add(&ms, timeout->tv_sec, (timeout->tv_nsec + 999999) / 1000000)) {
msp = &ms;
} else {
msp = 0;
}
fdcount = sys_poll_nt(fds, nfds, msp, sigmask);
}
if (IsOpenbsd() && fdcount != -1) {

View file

@ -67,6 +67,7 @@
int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
const struct timespec *timeout, const sigset_t *sigmask) {
int rc;
struct timeval tv, *tvp;
struct timespec ts, *tsp;
struct {
const sigset_t *s;
@ -110,17 +111,24 @@ int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
rc = sys_pselect(nfds, readfds, writefds, exceptfds,
(struct timespec *)timeout, sigmask);
} else {
rc = sys_select_nt(nfds, readfds, writefds, exceptfds, timeout, sigmask);
if (timeout) {
tv.tv_sec = timeout->tv_sec;
tv.tv_usec = timeout->tv_nsec / 1000;
tvp = &tv;
} else {
tvp = 0;
}
rc = sys_select_nt(nfds, readfds, writefds, exceptfds, tvp, sigmask);
}
}
END_CANCELATION_POINT;
STRACE("pselect(%d, %s → [%s], %s → [%s], %s → [%s], %s, %s) → %d% m", nfds,
DescribeFdSet(0, nfds, old_readfds_ptr),
DescribeFdSet(rc, nfds, old_readfds_ptr),
DescribeFdSet(rc, nfds, readfds),
DescribeFdSet(0, nfds, old_writefds_ptr),
DescribeFdSet(rc, nfds, old_writefds_ptr),
DescribeFdSet(rc, nfds, writefds),
DescribeFdSet(0, nfds, old_exceptfds_ptr),
DescribeFdSet(rc, nfds, old_exceptfds_ptr),
DescribeFdSet(rc, nfds, exceptfds), //
DescribeTimespec(0, timeout), //
DescribeSigset(0, sigmask), rc);

View file

@ -48,7 +48,6 @@
#include "libc/nt/enum/wait.h"
#include "libc/nt/errors.h"
#include "libc/nt/events.h"
#include "libc/nt/memory.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/inputrecord.h"
#include "libc/nt/synchronization.h"
@ -128,46 +127,33 @@ struct Keystrokes {
bool ohno_decckm;
bool bypass_mode;
uint16_t utf16hs;
size_t free_keys;
int16_t freekeys;
int64_t cin, cot;
struct Dll *list;
struct Dll *line;
struct Dll *free;
pthread_mutex_t lock;
struct Keystroke pool[512];
};
static struct Keystrokes __keystroke;
static pthread_mutex_t __keystroke_lock = PTHREAD_MUTEX_INITIALIZER;
textwindows void sys_read_nt_wipe_keystrokes(void) {
textwindows void WipeKeystrokes(void) {
bzero(&__keystroke, sizeof(__keystroke));
_pthread_mutex_wipe_np(&__keystroke_lock);
}
textwindows static void FreeKeystrokeImpl(struct Dll *key) {
dll_make_first(&__keystroke.free, key);
++__keystroke.free_keys;
}
textwindows static struct Keystroke *AllocKeystroke(void) {
struct Keystroke *k;
if (!(k = HeapAlloc(GetProcessHeap(), 0, sizeof(struct Keystroke))))
return 0;
dll_init(&k->elem);
return k;
++__keystroke.freekeys;
}
textwindows static struct Keystroke *NewKeystroke(void) {
struct Dll *e;
struct Keystroke *k;
if ((e = dll_first(__keystroke.free))) {
dll_remove(&__keystroke.free, e);
k = KEYSTROKE_CONTAINER(e);
--__keystroke.free_keys;
} else {
// PopulateKeystrokes() should make this branch impossible
if (!(k = AllocKeystroke()))
return 0;
}
struct Dll *e = dll_first(__keystroke.free);
if (!e) // See MIN(freekeys) before ReadConsoleInput()
__builtin_trap();
struct Keystroke *k = KEYSTROKE_CONTAINER(e);
dll_remove(&__keystroke.free, &k->elem);
--__keystroke.freekeys;
k->buflen = 0;
return k;
}
@ -183,22 +169,15 @@ textwindows static void FreeKeystrokes(struct Dll **list) {
FreeKeystroke(list, key);
}
textwindows static void PopulateKeystrokes(size_t want) {
struct Keystroke *k;
while (__keystroke.free_keys < want) {
if ((k = AllocKeystroke())) {
FreeKeystrokeImpl(&k->elem);
} else {
break;
}
}
}
textwindows static void OpenConsole(void) {
__keystroke.cin = CreateFile(u"CONIN$", kNtGenericRead | kNtGenericWrite,
kNtFileShareRead, 0, kNtOpenExisting, 0, 0);
__keystroke.cot = CreateFile(u"CONOUT$", kNtGenericRead | kNtGenericWrite,
kNtFileShareWrite, 0, kNtOpenExisting, 0, 0);
for (int i = 0; i < ARRAYLEN(__keystroke.pool); ++i) {
dll_init(&__keystroke.pool[i].elem);
FreeKeystrokeImpl(&__keystroke.pool[i].elem);
}
}
textwindows static int AddSignal(int sig) {
@ -212,11 +191,11 @@ textwindows static void InitConsole(void) {
}
textwindows static void LockKeystrokes(void) {
_pthread_mutex_lock(&__keystroke_lock);
pthread_mutex_lock(&__keystroke.lock);
}
textwindows static void UnlockKeystrokes(void) {
_pthread_mutex_unlock(&__keystroke_lock);
pthread_mutex_unlock(&__keystroke.lock);
}
textwindows int64_t GetConsoleInputHandle(void) {
@ -341,12 +320,9 @@ textwindows static int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
// note we define _POSIX_VDISABLE as zero
// tcsetattr() lets anyone reconfigure these keybindings
if (c && !(__ttyconf.magic & kTtyNoIsigs) && !__keystroke.bypass_mode) {
char b[] = {c};
if (c == __ttyconf.vintr) {
EchoConsoleNt(b, 1, false);
return AddSignal(SIGINT);
} else if (c == __ttyconf.vquit) {
EchoConsoleNt(b, 1, false);
return AddSignal(SIGQUIT);
}
}
@ -481,8 +457,7 @@ textwindows static void WriteCtl(const char *p, size_t n, bool escape_harder) {
}
}
textwindows void EchoConsoleNt(const char *p, size_t n, bool escape_harder) {
InitConsole();
textwindows static void EchoTty(const char *p, size_t n, bool escape_harder) {
if (!(__ttyconf.magic & kTtySilence)) {
if (__ttyconf.magic & kTtyEchoRaw) {
WriteTty(p, n);
@ -539,12 +514,14 @@ textwindows static void IngestConsoleInputRecord(struct NtInputRecord *r) {
!(__ttyconf.magic & kTtyNoIexten)) { // IEXTEN
if (__keystroke.bypass_mode) {
struct Keystroke *k = NewKeystroke();
if (!k)
return;
memcpy(k->buf, buf, sizeof(k->buf));
k->buflen = len;
dll_make_last(&__keystroke.line, &k->elem);
EchoConsoleNt(buf, len, true);
EchoTty(buf, len, true);
if (!__keystroke.freekeys) {
dll_make_last(&__keystroke.list, __keystroke.line);
__keystroke.line = 0;
}
__keystroke.bypass_mode = false;
return;
} else if (len == 1 && buf[0] && //
@ -634,14 +611,12 @@ textwindows static void IngestConsoleInputRecord(struct NtInputRecord *r) {
// allocate object to hold keystroke
struct Keystroke *k = NewKeystroke();
if (!k)
return;
memcpy(k->buf, buf, sizeof(k->buf));
k->buflen = len;
// echo input if it was successfully recorded
// assuming the win32 console isn't doing it already
EchoConsoleNt(buf, len, false);
EchoTty(buf, len, false);
// save keystroke to appropriate list
if (__ttyconf.magic & kTtyUncanon) {
@ -649,12 +624,12 @@ textwindows static void IngestConsoleInputRecord(struct NtInputRecord *r) {
} else {
dll_make_last(&__keystroke.line, &k->elem);
// flush canonical mode line on enter
if (len == 1 && buf[0] &&
((buf[0] & 255) == '\n' || //
(buf[0] & 255) == __ttyconf.veol || //
((buf[0] & 255) == __ttyconf.veol2 &&
!(__ttyconf.magic & kTtyNoIexten)))) {
// flush canonical mode line if oom or enter
if (!__keystroke.freekeys || (len == 1 && buf[0] &&
((buf[0] & 255) == '\n' || //
(buf[0] & 255) == __ttyconf.veol || //
((buf[0] & 255) == __ttyconf.veol2 &&
!(__ttyconf.magic & kTtyNoIexten))))) {
dll_make_last(&__keystroke.list, __keystroke.line);
__keystroke.line = 0;
}
@ -665,17 +640,15 @@ textwindows static void IngestConsoleInput(void) {
uint32_t i, n;
struct NtInputRecord records[16];
for (;;) {
if (!__keystroke.freekeys)
return;
if (__keystroke.end_of_file)
return;
if (!GetNumberOfConsoleInputEvents(__keystroke.cin, &n))
goto UnexpectedEof;
if (n > ARRAYLEN(records))
n = ARRAYLEN(records);
PopulateKeystrokes(n + 1);
if (n > __keystroke.free_keys)
n = __keystroke.free_keys;
if (!n)
if (!n || !__keystroke.freekeys)
return;
n = MIN(__keystroke.freekeys, MIN(ARRAYLEN(records), n));
if (!ReadConsoleInput(__keystroke.cin, records, n, &n))
goto UnexpectedEof;
for (i = 0; i < n && !__keystroke.end_of_file; ++i)
@ -997,10 +970,8 @@ textwindows ssize_t ReadBuffer(int fd, void *data, size_t size, int64_t offset,
if (f->kind == kFdDevNull)
return 0;
if (f->kind == kFdDevRandom) {
ProcessPrng(data, size);
return size;
}
if (f->kind == kFdDevRandom)
return ProcessPrng(data, size) ? size : __winerr();
if (f->kind == kFdConsole)
return ReadFromConsole(f, data, size, waitmask);

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/createfileflags.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
@ -169,11 +170,14 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset,
}
// the i/o operation was successfully canceled
if (got_eagain)
if (got_eagain) {
unassert(!got_sig);
return eagain();
}
// it's now reasonable to report semaphore creation error
if (other_error) {
unassert(!got_sig);
errno = __dos2errno(other_error);
return -1;
}

View file

@ -25,6 +25,7 @@
#include "libc/sock/sock.h"
#include "libc/sock/struct/pollfd.h"
#include "libc/sock/struct/pollfd.internal.h"
#include "libc/stdckdint.h"
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/errfuns.h"
#ifdef __x86_64__
@ -43,7 +44,7 @@
// </sync libc/sysv/consts.sh>
int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, const struct timespec *timeout,
fd_set *exceptfds, struct timeval *timeout,
const sigset_t *sigmask) {
int pfds = 0;
@ -67,8 +68,21 @@ int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds,
}
}
// convert the wait time to a word
uint32_t millis;
if (!timeout) {
millis = -1u;
} else {
int64_t ms = timeval_tomillis(*timeout);
if (ms < 0 || ms > UINT32_MAX) {
millis = -1u;
} else {
millis = ms;
}
}
// call our nt poll implementation
int fdcount = sys_poll_nt(fds, pfds, timeout, sigmask);
int fdcount = sys_poll_nt(fds, pfds, &millis, sigmask);
if (fdcount == -1)
return -1;
@ -101,6 +115,10 @@ int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds,
}
}
// store remaining time back in caller's timeval
if (timeout)
*timeout = timeval_frommillis(millis);
return bits;
}

View file

@ -31,7 +31,10 @@
* such as out-of-band data on a socket; it is equivalent to POLLPRI
* in the revents of poll()
* @param timeout may be null which means to block indefinitely; cosmo's
* implementation of select() never modifies this parameter
* implementation of select() never modifies this parameter which is
* how most platforms except Linux work which modifies it to reflect
* elapsed time, noting that POSIX permits either behavior therefore
* portable code should assume that timeout memory becomes undefined
* @raise E2BIG if we exceeded the 64 socket limit on Windows
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINTR if signal was delivered

View file

@ -21,6 +21,7 @@
#include "libc/dce.h"
#include "libc/intrin/describeflags.h"
#include "libc/intrin/strace.h"
#include "libc/stdckdint.h"
#include "libc/sysv/errfuns.h"
/**

View file

@ -23,7 +23,6 @@
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/describeflags.h"
#include "libc/intrin/rlimit.h"
#include "libc/intrin/strace.h"
#include "libc/macros.h"
#include "libc/runtime/runtime.h"
@ -89,12 +88,10 @@ int setrlimit(int resource, const struct rlimit *rlim) {
} else if (!IsWindows() && !(IsNetbsd() && resource == RLIMIT_AS)) {
rc = sys_setrlimit(resource, rlim);
} else if (resource == RLIMIT_STACK) {
rc = 0;
rc = enotsup();
} else {
rc = einval();
}
if (!rc && resource == RLIMIT_STACK)
__rlimit_stack_set(*rlim); // so __rlimit_stack_get() works on all OSes
if (resource == RLIMIT_AS) {
__virtualmax = rlim->rlim_cur;
errno = olde;

View file

@ -35,8 +35,9 @@ void shm_path_np(const char *name, char buf[hasatleast 78]) {
const char *a;
uint8_t digest[BLAKE2B256_DIGEST_LENGTH];
a = "/tmp/", n = 5;
if (IsLinux() && isdirectory("/dev/shm"))
if (IsLinux() && isdirectory("/dev/shm")) {
a = "/dev/shm/", n = 9;
}
BLAKE2B256(name, strlen(name), digest);
p = mempcpy(buf, a, n);
p = hexpcpy(p, digest, BLAKE2B256_DIGEST_LENGTH);

621
libc/calls/sig.c Normal file
View file

@ -0,0 +1,621 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et 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/sysv/consts/sig.h"
#include "ape/sections.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/ucontext.internal.h"
#include "libc/calls/ucontext.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/bsf.h"
#include "libc/intrin/describebacktrace.h"
#include "libc/intrin/dll.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.h"
#include "libc/intrin/weaken.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/context.h"
#include "libc/nt/enum/exceptionhandleractions.h"
#include "libc/nt/enum/signal.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/events.h"
#include "libc/nt/runtime.h"
#include "libc/nt/signals.h"
#include "libc/nt/struct/ntexceptionpointers.h"
#include "libc/nt/synchronization.h"
#include "libc/nt/thread.h"
#include "libc/runtime/symbols.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/ss.h"
#include "libc/thread/posixthread.internal.h"
#ifdef __x86_64__
/**
* @fileoverview Cosmopolitan Signals for Windows.
*/
struct SignalFrame {
unsigned rva;
unsigned flags;
siginfo_t si;
ucontext_t ctx;
};
static textwindows bool __sig_ignored_by_default(int sig) {
return sig == SIGURG || //
sig == SIGCONT || //
sig == SIGCHLD || //
sig == SIGWINCH;
}
textwindows bool __sig_ignored(int sig) {
return __sighandrvas[sig] == (intptr_t)SIG_IGN ||
(__sighandrvas[sig] == (intptr_t)SIG_DFL &&
__sig_ignored_by_default(sig));
}
textwindows void __sig_delete(int sig) {
struct Dll *e;
atomic_fetch_and_explicit(&__sig.pending, ~(1ull << (sig - 1)),
memory_order_relaxed);
_pthread_lock();
for (e = dll_last(_pthread_list); e; e = dll_prev(_pthread_list, e))
atomic_fetch_and_explicit(&POSIXTHREAD_CONTAINER(e)->tib->tib_sigpending,
~(1ull << (sig - 1)), memory_order_relaxed);
_pthread_unlock();
}
static textwindows int __sig_getter(atomic_ulong *sigs, sigset_t masked) {
int sig;
sigset_t bit, pending, deliverable;
for (;;) {
pending = atomic_load_explicit(sigs, memory_order_acquire);
if ((deliverable = pending & ~masked)) {
sig = bsfl(deliverable) + 1;
bit = 1ull << (sig - 1);
if (atomic_fetch_and_explicit(sigs, ~bit, memory_order_acq_rel) & bit)
return sig;
} else {
return 0;
}
}
}
textwindows int __sig_get(sigset_t masked) {
int sig;
if (!(sig = __sig_getter(&__get_tls()->tib_sigpending, masked)))
sig = __sig_getter(&__sig.pending, masked);
return sig;
}
static textwindows bool __sig_should_use_altstack(unsigned flags,
struct CosmoTib *tib) {
if (!(flags & SA_ONSTACK))
return false; // signal handler didn't enable it
if (!tib->tib_sigstack_size)
return false; // sigaltstack() wasn't installed on this thread
if (tib->tib_sigstack_flags & SS_DISABLE)
return false; // sigaltstack() on this thread was disabled by user
char *bp = __builtin_frame_address(0);
if (tib->tib_sigstack_addr <= bp &&
bp <= tib->tib_sigstack_addr + tib->tib_sigstack_size)
return false; // we're already on the alternate stack
return true;
}
static textwindows wontreturn void __sig_terminate(int sig) {
TerminateThisProcess(sig);
}
static textwindows bool __sig_start(struct PosixThread *pt, int sig,
unsigned *rva, unsigned *flags) {
*rva = __sighandrvas[sig];
*flags = __sighandflags[sig];
if (*rva == (intptr_t)SIG_IGN ||
(*rva == (intptr_t)SIG_DFL && __sig_ignored_by_default(sig))) {
STRACE("ignoring %G", sig);
return false;
}
if (atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
(1ull << (sig - 1))) {
STRACE("enqueing %G on %d", sig, _pthread_tid(pt));
atomic_fetch_or_explicit(&pt->tib->tib_sigpending, 1ull << (sig - 1),
memory_order_relaxed);
return false;
}
if (*rva == (intptr_t)SIG_DFL) {
STRACE("terminating on %G due to no handler", sig);
__sig_terminate(sig);
}
return true;
}
static textwindows sigaction_f __sig_handler(unsigned rva) {
atomic_fetch_add_explicit(&__sig.count, 1, memory_order_relaxed);
return (sigaction_f)(__executable_start + rva);
}
textwindows int __sig_raise(volatile int sig, int sic) {
// bitset of kinds of handlers called
volatile int handler_was_called = 0;
// loop over pending signals
ucontext_t ctx;
getcontext(&ctx);
if (!sig) {
if ((sig = __sig_get(ctx.uc_sigmask))) {
sic = SI_KERNEL;
} else {
return handler_was_called;
}
}
// process signal(s)
unsigned rva, flags;
struct PosixThread *pt = _pthread_self();
if (__sig_start(pt, sig, &rva, &flags)) {
if (flags & SA_RESETHAND) {
STRACE("resetting %G handler", sig);
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
}
// update the signal mask in preparation for signal handller
sigset_t blocksigs = __sighandmask[sig];
if (!(flags & SA_NODEFER))
blocksigs |= 1ull << (sig - 1);
ctx.uc_sigmask = atomic_fetch_or_explicit(&pt->tib->tib_sigmask, blocksigs,
memory_order_acquire);
// call the user's signal handler
char ssbuf[128];
siginfo_t si = {.si_signo = sig, .si_code = sic};
STRACE("__sig_raise(%G, %t) mask %s", sig, __sig_handler(rva),
_DescribeSigset(ssbuf, 0, (sigset_t *)&pt->tib->tib_sigmask));
__sig_handler(rva)(sig, &si, &ctx);
// record this handler
if (flags & SA_RESTART) {
handler_was_called |= SIG_HANDLED_SA_RESTART;
} else {
handler_was_called |= SIG_HANDLED_NO_RESTART;
}
}
// restore sigmask
// loop back to top
// jump where handler says
sig = 0;
return setcontext(&ctx);
}
textwindows int __sig_relay(int sig, int sic, sigset_t waitmask) {
sigset_t m;
int handler_was_called;
m = atomic_exchange_explicit(&__get_tls()->tib_sigmask, waitmask,
memory_order_acquire);
handler_was_called = __sig_raise(sig, SI_KERNEL);
atomic_store_explicit(&__get_tls()->tib_sigmask, m, memory_order_release);
return handler_was_called;
}
// cancels blocking operations being performed by signaled thread
textwindows void __sig_cancel(struct PosixThread *pt, int sig, unsigned flags) {
atomic_int *blocker;
blocker = atomic_load_explicit(&pt->pt_blocker, memory_order_acquire);
if (!blocker) {
STRACE("%G sent to %d asynchronously", sig, _pthread_tid(pt));
return;
}
// threads can create semaphores on an as-needed basis
if (blocker == PT_BLOCKER_EVENT) {
STRACE("%G set %d's event object", sig, _pthread_tid(pt));
SetEvent(pt->pt_event);
return;
}
// all other blocking ops that aren't overlap should use futexes
// we force restartable futexes to churn by waking w/o releasing
STRACE("%G waking %d's futex", sig, _pthread_tid(pt));
WakeByAddressSingle(blocker);
}
// the user's signal handler callback is wrapped with this trampoline
static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) {
int sig = sf->si.si_signo;
struct CosmoTib *tib = __get_tls();
struct PosixThread *pt = (struct PosixThread *)tib->tib_pthread;
for (;;) {
// update the signal mask in preparation for signal handller
sigset_t blocksigs = __sighandmask[sig];
if (!(sf->flags & SA_NODEFER))
blocksigs |= 1ull << (sig - 1);
sf->ctx.uc_sigmask = atomic_fetch_or_explicit(&tib->tib_sigmask, blocksigs,
memory_order_acquire);
// call the user's signal handler
char ssbuf[2][128];
STRACE("__sig_tramp(%G, %t) mask %s → %s", sig, __sig_handler(sf->rva),
_DescribeSigset(ssbuf[0], 0, &sf->ctx.uc_sigmask),
_DescribeSigset(ssbuf[1], 0, (sigset_t *)&tib->tib_sigmask));
__sig_handler(sf->rva)(sig, &sf->si, &sf->ctx);
// restore the signal mask that was used by the interrupted code
// this may have been modified by the signal handler in the callback
atomic_store_explicit(&tib->tib_sigmask, sf->ctx.uc_sigmask,
memory_order_release);
// jump back into original code if there aren't any pending signals
do {
if (!(sig = __sig_get(sf->ctx.uc_sigmask)))
__sig_restore(&sf->ctx);
} while (!__sig_start(pt, sig, &sf->rva, &sf->flags));
// tail recurse into another signal handler
sf->si.si_signo = sig;
sf->si.si_code = SI_KERNEL;
if (sf->flags & SA_RESETHAND) {
STRACE("resetting %G handler", sig);
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
}
}
}
// sends signal to another specific thread which is ref'd
static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) {
unsigned rva = __sighandrvas[sig];
unsigned flags = __sighandflags[sig];
// do nothing if signal is ignored
if (rva == (intptr_t)SIG_IGN ||
(rva == (intptr_t)SIG_DFL && __sig_ignored_by_default(sig))) {
STRACE("ignoring %G", sig);
return 0;
}
// we can't preempt threads that masked sig or are blocked
if (atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
(1ull << (sig - 1))) {
atomic_fetch_or_explicit(&pt->tib->tib_sigpending, 1ull << (sig - 1),
memory_order_relaxed);
__sig_cancel(pt, sig, flags);
return 0;
}
// if there's no handler then killing a thread kills the process
if (rva == (intptr_t)SIG_DFL) {
STRACE("terminating on %G due to no handler", sig);
__sig_terminate(sig);
}
// ignore signals already pending
uintptr_t th = _pthread_syshand(pt);
if (atomic_load_explicit(&pt->tib->tib_sigpending, memory_order_acquire) &
(1ull << (sig - 1))) {
return 0;
}
// take control of thread
// suspending the thread happens asynchronously
// however getting the context blocks until it's frozen
static pthread_spinlock_t killer_lock;
pthread_spin_lock(&killer_lock);
if (SuspendThread(th) == -1u) {
STRACE("SuspendThread failed w/ %d", GetLastError());
pthread_spin_unlock(&killer_lock);
return ESRCH;
}
struct NtContext nc;
nc.ContextFlags = kNtContextFull;
if (!GetThreadContext(th, &nc)) {
STRACE("GetThreadContext failed w/ %d", GetLastError());
ResumeThread(th);
pthread_spin_unlock(&killer_lock);
return ESRCH;
}
pthread_spin_unlock(&killer_lock);
// we can't preempt threads that masked sig or are blocked
// we can't preempt threads that are running in win32 code
// so we shall unblock the thread and let it signal itself
if ((atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
(1ull << (sig - 1))) ||
!((uintptr_t)__executable_start <= nc.Rip &&
nc.Rip < (uintptr_t)__privileged_start)) {
atomic_fetch_or_explicit(&pt->tib->tib_sigpending, 1ull << (sig - 1),
memory_order_relaxed);
ResumeThread(th);
__sig_cancel(pt, sig, flags);
return 0;
}
// preferring to live dangerously
// the thread will be signaled asynchronously
if (flags & SA_RESETHAND) {
STRACE("resetting %G handler", sig);
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
}
// inject call to trampoline function into thread
uintptr_t sp;
if (__sig_should_use_altstack(flags, pt->tib)) {
sp = (uintptr_t)pt->tib->tib_sigstack_addr + pt->tib->tib_sigstack_size;
} else {
sp = nc.Rsp;
}
sp -= sizeof(struct SignalFrame);
sp &= -16;
struct SignalFrame *sf = (struct SignalFrame *)sp;
_ntcontext2linux(&sf->ctx, &nc);
bzero(&sf->si, sizeof(sf->si));
sf->rva = rva;
sf->flags = flags;
sf->si.si_code = sic;
sf->si.si_signo = sig;
*(uintptr_t *)(sp -= sizeof(uintptr_t)) = nc.Rip;
nc.Rip = (intptr_t)__sig_tramp;
nc.Rdi = (intptr_t)sf;
nc.Rsp = sp;
if (!SetThreadContext(th, &nc)) {
STRACE("SetThreadContext failed w/ %d", GetLastError());
return ESRCH;
}
ResumeThread(th);
__sig_cancel(pt, sig, flags);
return 0;
}
// sends signal to another specific thread
textwindows int __sig_kill(struct PosixThread *pt, int sig, int sic) {
int rc;
BLOCK_SIGNALS;
rc = __sig_killer(pt, sig, sic);
ALLOW_SIGNALS;
return rc;
}
// sends signal to any other thread
textwindows void __sig_generate(int sig, int sic) {
struct Dll *e;
struct PosixThread *pt, *mark = 0;
if (__sig_ignored(sig)) {
STRACE("ignoring %G", sig);
return;
}
if (__sighandrvas[sig] == (intptr_t)SIG_DFL) {
STRACE("terminating on %G due to no handler", sig);
__sig_terminate(sig);
}
if (atomic_load_explicit(&__sig.pending, memory_order_acquire) &
(1ull << (sig - 1))) {
return;
}
BLOCK_SIGNALS;
_pthread_lock();
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
pt = POSIXTHREAD_CONTAINER(e);
// we don't want to signal ourself
if (pt == _pthread_self())
continue;
// we don't want to signal a thread that isn't running
if (atomic_load_explicit(&pt->pt_status, memory_order_acquire) >=
kPosixThreadTerminated) {
continue;
}
// choose this thread if it isn't masking sig
if (!(atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
(1ull << (sig - 1)))) {
_pthread_ref(pt);
mark = pt;
break;
}
// if a thread is blocking then we check to see if it's planning
// to unblock our sig once the wait operation is completed; when
// that's the case we can cancel the thread's i/o to deliver sig
if (atomic_load_explicit(&pt->pt_blocker, memory_order_acquire) &&
!(pt->pt_blkmask & (1ull << (sig - 1)))) {
_pthread_ref(pt);
mark = pt;
break;
}
}
_pthread_unlock();
if (mark) {
__sig_killer(mark, sig, sic);
_pthread_unref(mark);
} else {
atomic_fetch_or_explicit(&__sig.pending, 1ull << (sig - 1),
memory_order_relaxed);
}
ALLOW_SIGNALS;
}
static textwindows char *__sig_stpcpy(char *d, const char *s) {
size_t i;
for (i = 0;; ++i)
if (!(d[i] = s[i]))
return d + i;
}
static textwindows wontreturn void __sig_death(int sig, const char *thing) {
#ifndef TINY
intptr_t hStderr;
char sigbuf[21], s[128], *p;
hStderr = GetStdHandle(kNtStdErrorHandle);
p = __sig_stpcpy(s, "Terminating on ");
p = __sig_stpcpy(p, thing);
p = __sig_stpcpy(p, strsignal_r(sig, sigbuf));
p = __sig_stpcpy(p,
". Pass --strace and/or ShowCrashReports() for details.\n");
WriteFile(hStderr, s, p - s, 0, 0);
#endif
__sig_terminate(sig);
}
static textwindows void __sig_unmaskable(struct NtExceptionPointers *ep,
int code, int sig,
struct CosmoTib *tib) {
// log vital crash information reliably for --strace before doing much
// we don't print this without the flag since raw numbers scare people
// this needs at least one page of stack memory in order to get logged
// otherwise it'll print a warning message about the lack of stack mem
STRACE("win32 vectored exception 0x%08Xu raising %G "
"cosmoaddr2line %s %lx %s",
ep->ExceptionRecord->ExceptionCode, sig,
_weaken(FindDebugBinary) ? _weaken(FindDebugBinary)()
: program_invocation_name,
ep->ContextRecord->Rip,
DescribeBacktrace((struct StackFrame *)ep->ContextRecord->Rbp));
// if the user didn't install a signal handler for this unmaskable
// exception, then print a friendly helpful hint message to stderr
unsigned rva = __sighandrvas[sig];
if (rva == (intptr_t)SIG_DFL || rva == (intptr_t)SIG_IGN)
__sig_death(sig, "uncaught ");
// if this signal handler is configured to auto-reset to the default
// then that reset needs to happen before the user handler is called
unsigned flags = __sighandflags[sig];
if (flags & SA_RESETHAND) {
STRACE("resetting %G handler", sig);
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
}
// determine the true memory address at which fault occurred
// if this is a stack overflow then reapply guard protection
void *si_addr;
if (ep->ExceptionRecord->ExceptionCode == kNtSignalGuardPage) {
si_addr = (void *)ep->ExceptionRecord->ExceptionInformation[1];
} else {
si_addr = ep->ExceptionRecord->ExceptionAddress;
}
// call the user signal handler
// and a modifiable view of the faulting code's cpu state
// temporarily replace signal mask while calling crash handler
// abort process if sig is already blocked to avoid crash loop
// note ucontext_t is a hefty data structures on top of NtContext
ucontext_t ctx = {0};
siginfo_t si = {.si_signo = sig, .si_code = code, .si_addr = si_addr};
_ntcontext2linux(&ctx, ep->ContextRecord);
sigset_t blocksigs = __sighandmask[sig];
if (!(flags & SA_NODEFER))
blocksigs |= 1ull << (sig - 1);
ctx.uc_sigmask = atomic_fetch_or_explicit(&tib->tib_sigmask, blocksigs,
memory_order_acquire);
if (ctx.uc_sigmask & (1ull << (sig - 1))) {
__sig_death(sig, "masked ");
__sig_terminate(sig);
}
__sig_handler(rva)(sig, &si, &ctx);
atomic_store_explicit(&tib->tib_sigmask, ctx.uc_sigmask,
memory_order_release);
_ntlinux2context(ep->ContextRecord, &ctx);
}
void __stack_call(struct NtExceptionPointers *, int, int, struct CosmoTib *,
void (*)(struct NtExceptionPointers *, int, int,
struct CosmoTib *),
void *);
// abashed the devil stood
// and felt how awful goodness is
__msabi dontinstrument unsigned __sig_crash(struct NtExceptionPointers *ep) {
// translate win32 to unix si_signo and si_code
int code, sig = __sig_crash_sig(ep->ExceptionRecord->ExceptionCode, &code);
// advance the instruction pointer to skip over debugger breakpoints
// this behavior is consistent with how unix kernels are implemented
if (sig == SIGTRAP) {
ep->ContextRecord->Rip++;
if (__sig_ignored(sig))
return kNtExceptionContinueExecution;
}
// win32 stack overflow detection executes INSIDE the guard page
// thus switch to the alternate signal stack as soon as possible
struct CosmoTib *tib = __get_tls();
unsigned flags = __sighandflags[sig];
if (__sig_should_use_altstack(flags, tib)) {
__stack_call(ep, code, sig, tib, __sig_unmaskable,
tib->tib_sigstack_addr + tib->tib_sigstack_size);
} else {
__sig_unmaskable(ep, code, sig, tib);
}
// resume running user program
// hopefully the user fixed the cpu state
// otherwise the crash will keep happening
return kNtExceptionContinueExecution;
}
static textwindows int __sig_console_sig(uint32_t dwCtrlType) {
switch (dwCtrlType) {
case kNtCtrlCEvent:
return SIGINT;
case kNtCtrlBreakEvent:
return SIGQUIT;
case kNtCtrlCloseEvent:
case kNtCtrlLogoffEvent: // only received by services
case kNtCtrlShutdownEvent: // only received by services
return SIGHUP;
default:
return SIGSTKFLT;
}
}
__msabi textwindows dontinstrument bool32 __sig_console(uint32_t dwCtrlType) {
struct CosmoTib tls;
__bootstrap_tls(&tls, __builtin_frame_address(0));
__sig_generate(__sig_console_sig(dwCtrlType), SI_KERNEL);
return true;
}
// returns 0 if no signal handlers were called, otherwise a bitmask
// consisting of `1` which means a signal handler was invoked which
// didn't have the SA_RESTART flag, and `2`, which means SA_RESTART
// handlers were called (or `3` if both were the case).
textwindows int __sig_check(void) {
int sig;
if ((sig = __sig_get(atomic_load_explicit(&__get_tls()->tib_sigmask,
memory_order_acquire)))) {
return __sig_raise(sig, SI_KERNEL);
} else {
return 0;
}
}
__attribute__((__constructor__(10))) textstartup void __sig_init(void) {
if (!IsWindows())
return;
AddVectoredExceptionHandler(true, (void *)__sig_crash);
SetConsoleCtrlHandler((void *)__sig_console, true);
}
#endif /* __x86_64__ */

View file

@ -1,8 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_
#include "libc/atomic.h"
#include "libc/calls/struct/sigset.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/thread/posixthread.internal.h"
#define SIG_HANDLED_NO_RESTART 1
@ -11,8 +9,8 @@
COSMOPOLITAN_C_START_
struct Signals {
atomic_ulong *process;
atomic_ulong count;
_Atomic(uint64_t) pending;
_Atomic(uint64_t) count;
};
extern struct Signals __sig;
@ -29,8 +27,5 @@ void __sig_delete(int);
void __sig_generate(int, int);
void __sig_init(void);
__msabi char16_t *__sig_process_path(char16_t *, uint32_t, int);
__msabi atomic_ulong *__sig_map_process(int, int);
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_ */

View file

@ -423,7 +423,7 @@ static int __sigaction(int sig, const struct sigaction *act,
* }
*
* void ContinueOnCrash(void) {
* struct sigaction sa = {.sa_sigaction = OnCrash,
* struct sigaction sa = {.sa_handler = OnSigSegv,
* .sa_flags = SA_SIGINFO | SA_RESETHAND};
* sigaction(SIGSEGV, &sa, 0);
* sigaction(SIGFPE, &sa, 0);

View file

@ -113,7 +113,7 @@ static int sigaltstack_bsd(const struct sigaltstack *neu,
* struct sigaction sa;
* struct sigaltstack ss;
* ss.ss_flags = 0;
* ss.ss_size = sysconf(_SC_SIGSTKSZ);
* ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 8192;
* ss.ss_sp = malloc(ss.ss_size);
* sigaltstack(&ss, 0);
* sigemptyset(&sa.ss_mask);
@ -121,16 +121,11 @@ static int sigaltstack_bsd(const struct sigaltstack *neu,
* sa.sa_handler = OnStackOverflow;
* sigaction(SIGSEGV, &sa, 0);
*
* Your stack size should be `sysconf(_SC_SIGSTKSZ)` which should be
* somewhere in the ballpark of 32kb to 64kb. You should go no lower
* than `sysconf(_SC_MINSIGSTKSZ) + 2048` which could be 4kb - 34kb.
* Cosmo also defines `SIGSTKSZ` as 32kb, which should also be safe.
*
* @param neu if non-null will install new signal alt stack
* @param old if non-null will receive current signal alt stack
* @return 0 on success, or -1 w/ errno
* @raise EFAULT if bad memory was supplied
* @raise ENOMEM if `neu->ss_size` is beneath `sysconf(_SC_MINSIGSTKSZ)`
* @raise ENOMEM if `neu->ss_size` is less than `MINSIGSTKSZ`
*/
int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) {
int rc;

View file

@ -23,18 +23,18 @@
#include "libc/sysv/errfuns.h"
textwindows int __sigcheck(sigset_t waitmask, bool restartable) {
int sig, handler_was_called = 0;
int sig, handler_was_called;
if (_check_cancel() == -1)
return -1;
while (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))) {
handler_was_called |= _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))) {
handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
if (_check_cancel() == -1)
return -1;
}
if (handler_was_called & SIG_HANDLED_NO_RESTART)
return eintr();
if (handler_was_called & SIG_HANDLED_SA_RESTART)
if (!restartable)
if (handler_was_called & SIG_HANDLED_NO_RESTART)
return eintr();
if (handler_was_called & SIG_HANDLED_SA_RESTART)
if (!restartable)
return eintr();
}
return 0;
}

View file

@ -33,7 +33,6 @@
#include "libc/runtime/syslib.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h"
/**
* @fileoverview XNU kernel callback normalization.
@ -514,7 +513,6 @@ privileged void __sigenter_xnu(int sig, struct siginfo_xnu *xnuinfo,
flags = __sighandflags[sig];
#ifdef __aarch64__
// xnu silicon claims to support sa_resethand but it does nothing
// this can be tested, since it clears the bit from flags as well
if (flags & SA_RESETHAND) {
@ -523,13 +521,6 @@ privileged void __sigenter_xnu(int sig, struct siginfo_xnu *xnuinfo,
__sighandflags[sig] = 0;
__sighandrvas[sig] = 0;
}
// unlike amd64, the instruction pointer on arm64 isn't advanced
// past the debugger breakpoint instruction automatically. we need
// this so execution can resume after __builtin_trap().
if (xnuctx && sig == SIGTRAP)
xnuctx->uc_mcontext->__ss.__pc += 4;
#endif
if (~flags & SA_SIGINFO) {

View file

@ -53,7 +53,7 @@ int sigpending(sigset_t *pending) {
}
rc = 0;
} else if (IsWindows()) {
*pending = atomic_load_explicit(__sig.process, memory_order_acquire) |
*pending = atomic_load_explicit(&__sig.pending, memory_order_acquire) |
atomic_load_explicit(&__get_tls()->tib_sigpending,
memory_order_acquire);
rc = 0;

View file

@ -21,7 +21,6 @@
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
@ -54,14 +53,8 @@ int sigsuspend(const sigset_t *ignore) {
} else {
sigset_t waitmask = ignore ? *ignore : 0;
if (IsWindows() || IsMetal()) {
// we don't strictly need to block signals, but it reduces signal
// delivery latency, by preventing other threads from delivering a
// signal asynchronously. it takes about ~5us to deliver a signal
// using SetEvent() whereas it takes ~30us to use SuspendThread(),
// GetThreadContext(), SetThreadContext(), and ResumeThread().
BLOCK_SIGNALS;
rc = _park_norestart(timespec_max, waitmask);
ALLOW_SIGNALS;
while (!(rc = _park_norestart(-1u, waitmask)))
donothing;
} else {
rc = sys_sigsuspend((uint64_t[2]){waitmask}, 8);
}

View file

@ -13,6 +13,7 @@ extern unsigned __sighandflags[NSIG + 1];
extern uint64_t __sighandmask[NSIG + 1];
extern const struct NtSecurityAttributes kNtIsInheritable;
void __fds_wipe(void);
void __fds_lock(void);
void __fds_unlock(void);

View file

@ -4,21 +4,19 @@ COSMOPOLITAN_C_START_
typedef uint64_t sigset_t;
/* clang-format off */
int sigaddset(sigset_t *, int) libcesque paramsnonnull();
int sigdelset(sigset_t *, int) libcesque paramsnonnull();
int sigemptyset(sigset_t *) libcesque paramsnonnull();
int sigfillset(sigset_t *) libcesque paramsnonnull();
int sigandset(sigset_t *, const sigset_t *, const sigset_t *) libcesque paramsnonnull();
int sigorset(sigset_t *, const sigset_t *, const sigset_t *) libcesque paramsnonnull();
int sigisemptyset(const sigset_t *) libcesque paramsnonnull() nosideeffect;
int sigismember(const sigset_t *, int) libcesque paramsnonnull() nosideeffect;
int sigcountset(const sigset_t *) libcesque paramsnonnull() nosideeffect;
int sigprocmask(int, const sigset_t *, sigset_t *) dontthrow;
int sigsuspend(const sigset_t *) dontthrow;
int sigpending(sigset_t *) libcesque;
int pthread_sigmask(int, const sigset_t *, sigset_t *) dontthrow;
/* clang-format on */
int sigaddset(sigset_t *, int) paramsnonnull();
int sigdelset(sigset_t *, int) paramsnonnull();
int sigemptyset(sigset_t *) paramsnonnull();
int sigfillset(sigset_t *) paramsnonnull();
int sigandset(sigset_t *, const sigset_t *, const sigset_t *) paramsnonnull();
int sigorset(sigset_t *, const sigset_t *, const sigset_t *) paramsnonnull();
int sigisemptyset(const sigset_t *) paramsnonnull() nosideeffect;
int sigismember(const sigset_t *, int) paramsnonnull() nosideeffect;
int sigcountset(const sigset_t *) paramsnonnull() nosideeffect;
int sigprocmask(int, const sigset_t *, sigset_t *);
int sigsuspend(const sigset_t *);
int sigpending(sigset_t *);
int pthread_sigmask(int, const sigset_t *, sigset_t *);
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_SIGSET_H_ */

View file

@ -5,15 +5,27 @@
#include "libc/sysv/consts/sig.h"
COSMOPOLITAN_C_START_
#ifndef MODE_DBG
/* block sigs because theoretical edge cases */
#define BLOCK_SIGNALS \
do { \
sigset_t _SigMask; \
_SigMask = __sig_block()
#define ALLOW_SIGNALS \
__sig_unblock(_SigMask); \
} \
while (0)
#else
/* doesn't block signals so we can get a crash
report, when a core runtime library crashes */
#define BLOCK_SIGNALS \
do { \
sigset_t _SigMask; \
sigprocmask(SIG_SETMASK, 0, &_SigMask)
#define ALLOW_SIGNALS \
} \
while (0)
#endif
sigset_t __sig_block(void);
void __sig_unblock(sigset_t);

View file

@ -1,14 +1,13 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_INTERNAL_H_
#include "libc/calls/ucontext.h"
#include "libc/nt/struct/context.h"
COSMOPOLITAN_C_START_
#ifdef __x86_64__
#define PC rip
#define SP rsp
#define BP rbp
#define RES0 rax
#define RES1 rdx
#define ARG0 rdi
#define ARG1 rsi
#define ARG2 rdx
@ -19,8 +18,6 @@ COSMOPOLITAN_C_START_
#define PC pc
#define SP sp
#define BP regs[29]
#define RES0 regs[0]
#define RES1 regs[1]
#define ARG0 regs[0]
#define ARG1 regs[1]
#define ARG2 regs[2]
@ -31,5 +28,8 @@ COSMOPOLITAN_C_START_
#error "unsupported architecture"
#endif
void _ntcontext2linux(struct ucontext *, const struct NtContext *);
void _ntlinux2context(struct NtContext *, const ucontext_t *);
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_INTERNAL_H_ */

View file

@ -0,0 +1,71 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_USER_REGS_STRUCT_H_
#define COSMOPOLITAN_LIBC_CALLS_STRUCT_USER_REGS_STRUCT_H_
COSMOPOLITAN_C_START_
/**
* Linux Kernel user registers.
*
* @note superset of struct pt_regs
* @see ptrace() w/ PTRACE_SYSCALL
*/
struct user_regs_struct {
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t rbp;
uint64_t rbx;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t rax;
uint64_t rcx;
uint64_t rdx;
uint64_t rsi;
uint64_t rdi;
uint64_t orig_rax;
uint64_t rip;
uint64_t cs;
uint64_t eflags;
uint64_t rsp;
uint64_t ss;
uint64_t fs_base;
uint64_t gs_base;
uint64_t ds;
uint64_t es;
uint64_t fs;
uint64_t gs;
};
struct useregs_struct_freebsd {
int64_t r15;
int64_t r14;
int64_t r13;
int64_t r12;
int64_t r11;
int64_t r10;
int64_t r9;
int64_t r8;
int64_t rdi;
int64_t rsi;
int64_t rbp;
int64_t rbx;
int64_t rdx;
int64_t rcx;
int64_t rax;
uint32_t trapno;
uint16_t fs;
uint16_t gs;
uint32_t err;
uint16_t es;
uint16_t ds;
int64_t rip;
int64_t cs;
int64_t rflags;
int64_t rsp;
int64_t ss;
};
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_USER_REGS_STRUCT_H_ */

View file

@ -31,17 +31,17 @@
// @returnstwice
.ftrace1
swapcontext:
beg
.ftrace2
#include "libc/intrin/getcontext.inc"
#include "libc/calls/getcontext.inc"
#ifdef __x86_64__
pro
cpush %rsi
cpush %rsi
push %rbp
mov %rsp,%rbp
push %rsi
push %rsi
call __swapcontextsig
cpop %rdi
cpop %rdi
epi
pop %rdi
pop %rdi
pop %rbp
test %eax,%eax
jnz 1f
#elif defined(__aarch64__)
@ -56,5 +56,4 @@ swapcontext:
#endif
jmp __tailcontext
1: ret
end
.endfn swapcontext,globl

View file

@ -2,9 +2,6 @@
#define COSMOPOLITAN_LIBC_CALLS_SYSCALL_NT_INTERNAL_H_
COSMOPOLITAN_C_START_
extern int sys_getppid_nt_cosmo;
extern int sys_getppid_nt_win32;
bool32 sys_isatty(int);
int sys_chdir_nt(const char *);
int sys_dup_nt(int, int, int, int);
@ -40,7 +37,6 @@ int sys_unlinkat_nt(int, const char *, int);
int64_t sys_lseek_nt(int, int64_t, int);
ssize_t sys_read_nt_impl(int, void *, size_t, int64_t);
ssize_t sys_readlinkat_nt(int, const char *, char *, size_t);
void sys_getppid_nt_wipe(int, int);
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_CALLS_SYSCALL_NT_INTERNAL_H_ */

View file

@ -57,7 +57,8 @@ __tailcontext:
mov 80(%rax),%rsp
push 88(%rax)
mov 24(%rax),%rdi
mov 64(%rax),%rax
xor %eax,%eax
ret
#elif defined(__aarch64__)

View file

@ -16,9 +16,10 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/timespec.h"
#include "libc/sysv/consts/clock.h"
#include "libc/time.h"
#include "libc/calls/struct/timeval.h"
#include "libc/dce.h"
#include "libc/sysv/errfuns.h"
/**
* Returns time as seconds from UNIX epoch.
@ -28,11 +29,15 @@
* @asyncsignalsafe
*/
int64_t time(int64_t *opt_out_ret) {
int64_t secs = -1;
struct timespec ts;
if (!clock_gettime(CLOCK_REALTIME, &ts))
secs = ts.tv_sec;
if (opt_out_ret)
*opt_out_ret = secs;
int64_t secs;
struct timeval tv;
if (gettimeofday(&tv, 0) != -1) {
secs = tv.tv_sec;
if (opt_out_ret) {
*opt_out_ret = secs;
}
} else {
secs = -1;
}
return secs;
}

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