diff --git a/.gitignore b/.gitignore index 052acb3c..a4f2d6f5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ /bin/ /lib/ +# Cross-compilation output (tools/crossBuild.sh: musl + Windows artifacts). +/build/ + # Vendored-engine build output: mruby's Rake build and libssh2's CMake build emit whole # trees (generated .c/.h, the mrbc tool, CMake cache) under their own build/ dirs. /vendor/mruby/build/ diff --git a/LICENSE.md b/LICENSE.md index 7330ac72..760b54a8 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -114,3 +114,14 @@ All of the following are linked into the default build and into `bin/calog`. > permissive binary can build without the MySQL backend -- drop > `-DCALOG_WITH_MYSQL` and the `$(MYSQLARCH)` archive from the `bin/calog` link in > the Makefile -- and keep SQLite + PostgreSQL. + +### Platform support (Windows builds only) + +Linked only into **Windows** builds; not present in the Linux or macOS builds (those +use the native pthreads). + +- **winpthreads** (from mingw-w64) -- MIT License. Copyright (c) 2011 mingw-w64 + project; parts derived from the Pthreads-win32 (POSIX Threads for Windows) library. + A POSIX threads implementation over the Win32 API, so calog's pthread code compiles + on Windows. Vendored from source (see `vendor/winpthreads/NOTICE.calog`); license in + `vendor/winpthreads/COPYING`. diff --git a/Makefile b/Makefile index 75c170af..6b3bb27e 100644 --- a/Makefile +++ b/Makefile @@ -50,21 +50,44 @@ LUAINC = -I$(LUADIR)/src # is Unix-only anyway -- pthreads, sanitizers, setarch -- so the Windows branch just # keeps the Lua define correct, it does not by itself make a Windows build work.) ifeq ($(OS),Windows_NT) + PLATFORM = windows LUAPLAT = LUAPLATLIBS = + DLLIB = + SOCKETLIBS = -lws2_32 -lwinmm else UNAMES := $(shell uname -s) ifeq ($(UNAMES),Linux) + PLATFORM = linux LUAPLAT = -DLUA_USE_LINUX LUAPLATLIBS = -ldl + DLLIB = -ldl + SOCKETLIBS = else ifeq ($(UNAMES),Darwin) + PLATFORM = darwin LUAPLAT = -DLUA_USE_MACOSX LUAPLATLIBS = + DLLIB = + SOCKETLIBS = else + PLATFORM = posix LUAPLAT = -DLUA_USE_POSIX LUAPLATLIBS = + DLLIB = -ldl + SOCKETLIBS = endif endif + +# Extra link flags for the `release` target (empty for the default dev build): the C++ runtime +# and libgcc linked statically so the CLI's only shared-library dependency is the system libc. +# CXXLIB is how the explicit C++ runtime is pulled in; release wraps it in -Bstatic/-Bdynamic so +# the STATIC libstdc++.a is used (a driver -static-libstdc++ does not override an explicit -lstdc++). +RELEASELD = +CXXLIB = -lstdc++ +# macOS ships libc++ (LLVM), not libstdc++; the Squirrel C++ engine links against it there. +ifeq ($(PLATFORM),darwin) + CXXLIB = -lc++ +endif LUAFLAGS = -std=c99 -w -g -O2 $(LUAPLAT) LUASRC = $(filter-out $(LUADIR)/src/lua.c $(LUADIR)/src/luac.c, $(wildcard $(LUADIR)/src/*.c)) LUAOBJ = $(patsubst $(LUADIR)/src/%.c,obj/%.o,$(LUASRC)) @@ -108,7 +131,7 @@ S7DIR = vendor/s7 S7INC = -I$(S7DIR) S7FLAGS = -std=c99 -w -g -O1 -D_GNU_SOURCE S7OBJ = obj/s7.o -S7LIBS = -ldl -lm +S7LIBS = $(DLLIB) -lm # --- vendored Wren (C, amalgamated single file wren.c + wren.h; regenerate via # util/generate_amalgamation.py from upstream). Links -lm. --- @@ -144,7 +167,7 @@ $(MRUBYLIB): $(MRUBYCONF) TCLDIR = vendor/tcl TCLINC = -I$(TCLDIR)/generic -I$(TCLDIR)/unix TCLLIB = $(TCLDIR)/build/libtcl9.0.a -TCLLIBS = -ldl -lz -lpthread -lm +TCLLIBS = $(DLLIB) -lz -lpthread -lm $(TCLLIB): mkdir -p $(TCLDIR)/build && cd $(TCLDIR)/build && ../unix/configure --disable-shared >/dev/null && $(MAKE) libtcl9.0.a @@ -161,9 +184,28 @@ SQLITELIBS = -lm -lpthread ENETDIR = vendor/enet ENETINC = -I$(ENETDIR)/include ENETFLAGS = -std=c11 -w -g -O2 -D_GNU_SOURCE -DHAS_FCNTL=1 -DHAS_POLL=1 -DHAS_GETADDRINFO=1 -DHAS_GETNAMEINFO=1 -DHAS_GETHOSTBYNAME_R=1 -DHAS_GETHOSTBYADDR_R=1 -DHAS_INET_PTON=1 -DHAS_INET_NTOP=1 -DHAS_MSGHDR_FLAGS=1 -DHAS_SOCKLEN_T=1 -ENETNAMES = callbacks compress host list packet peer protocol unix +# ENet's platform backend: unix.c on Linux/macOS, win32.c on Windows (which also needs -lwinmm). +ENETBACKEND = unix +ifeq ($(PLATFORM),windows) + ENETBACKEND = win32 +endif +ENETNAMES = callbacks compress host list packet peer protocol $(ENETBACKEND) ENETOBJ = $(foreach n,$(ENETNAMES),obj/enet_$(n).o) +# --- vendored winpthreads (WINDOWS ONLY): POSIX threads over the Win32 API, so calog's pthread +# code (actor model, timer, pubsub, ...) builds on Windows. Built from source like every +# dependency; unused on Linux/macOS, which have native pthreads. A Windows build sets CC to a +# mingw-w64 compiler (or uses tools/crossBuild.sh with zig). See vendor/winpthreads/NOTICE.calog. --- +WPTHDIR = vendor/winpthreads +WPTHINC = -I$(WPTHDIR)/include -DWINPTHREAD_STATIC=1 +WPTHLIB = lib/libwinpthreads.a +WPTHSRC = $(wildcard $(WPTHDIR)/src/*.c) +WPTHOBJ = $(patsubst $(WPTHDIR)/src/%.c,obj/wpth_%.o,$(WPTHSRC)) +obj/wpth_%.o: $(WPTHDIR)/src/%.c | obj + $(CC) -O2 -w -I$(WPTHDIR)/include -I$(WPTHDIR)/src -DWINPTHREAD_STATIC=1 -DIN_WINPTHREAD=1 -c -o $@ $< +$(WPTHLIB): $(WPTHOBJ) | lib + $(AR) rcs $@ $(WPTHOBJ) + BINS = bin/testBroker bin/testLua bin/testMyBasic bin/testPolyglot bin/testActor \ bin/testEngineLua bin/testEngineMyBasic bin/testSquirrel bin/testEngineSquirrel bin/testJs bin/testEngineJs \ bin/testEngineBerry bin/testEngineS7 bin/testEngineWren bin/testEngineMruby bin/testEngineTcl bin/testLoad bin/testDb bin/testNet bin/testTask bin/testExport bin/testJson bin/testTime bin/testFs bin/testCrypto bin/testKv bin/testTimer bin/testPubsub bin/testHttp bin/testHttps bin/embed bin/calog @@ -398,10 +440,10 @@ bin/testPolyglot: obj/testPolyglot.o lib/libcalog.a lib/liblua.a lib/libmybasic. $(CC) $(LDFLAGS) -o $@ $^ $(LUALIBS) bin/testSquirrel: obj/testSquirrel.o lib/libcalog.a lib/libsquirrel.a | bin - $(CC) $(LDFLAGS) -o $@ $^ -lstdc++ -lm + $(CC) $(LDFLAGS) -o $@ $^ $(CXXLIB) -lm bin/testEngineSquirrel: obj/testEngineSquirrel.o lib/libcalog.a lib/libsquirrel.a | bin - $(CC) $(LDFLAGS) -pthread -o $@ $^ -lstdc++ -lm + $(CC) $(LDFLAGS) -pthread -o $@ $^ $(CXXLIB) -lm bin/testJs: obj/testJs.o lib/libcalog.a lib/libquickjs.a | bin $(CC) $(LDFLAGS) -o $@ $^ -lm @@ -441,7 +483,19 @@ bin/calog: obj/calogMain.o \ obj/calogCrypto.o obj/calogDbFull.o obj/calogExport.o obj/calogFs.o obj/calogHttp.o obj/calogJson.o obj/calogKv.o obj/calogNet.o obj/calogPubsub.o obj/calogSsh.o obj/calogTask.o obj/calogTime.o obj/calogTimer.o obj/calogHandle.o \ lib/libcalog.a lib/liblua.a lib/libquickjs.a lib/libsquirrel.a lib/libmybasic.a lib/libberry.a lib/libs7.a lib/libwren.a $(MRUBYLIB) $(TCLLIB) \ lib/libsqlite3.a lib/libenet.a $(LIBSSH2LIB) $(DBARCH) | bin - $(CC) $(LDFLAGS) -pthread -o $@ $(filter-out $(DBARCH),$^) -Wl,--start-group $(PGARCHIVES) -Wl,--end-group $(MYSQLARCH) $(SSLARCH) -lstdc++ -ldl -lm -lpthread + $(CC) $(LDFLAGS) $(RELEASELD) -pthread -o $@ $(filter-out $(DBARCH),$^) -Wl,--start-group $(PGARCHIVES) -Wl,--end-group $(MYSQLARCH) $(SSLARCH) $(CXXLIB) $(DLLIB) -lm -lpthread $(SOCKETLIBS) + +# ---- release build (fewest system dependencies while keeping DNS) -------------------- +# The full CLI with sanitizers off and the C++/gcc runtimes linked statically, so `ldd` collapses +# to just the system libc (glibc stays dynamic, so getaddrinfo/DNS keeps working -- see PORTING.md). +# A `clean` first keeps build modes from mixing; the slow vendored archives (OpenSSL/Tcl/mruby/ +# libssh2) live under vendor/ and survive it, so this is a moderate rebuild, not a full one. +release: + $(MAKE) clean + $(MAKE) SAN= RELEASELD='-static-libgcc' CXXLIB='-Wl,-Bstatic -lstdc++ -Wl,-Bdynamic' bin/calog + @echo "== release bin/calog shared-library dependencies (want just libc/libm + loader) ==" + @ldd bin/calog 2>&1 | sed 's/^/ /' + @echo "== note: run 'make clean && make' to return to the sanitized dev build ==" # ---- fully-static build -------------------------------------------------------------- # A self-contained calog executable with NO shared-library dependencies at runtime. The @@ -479,15 +533,15 @@ obj/testLoad.o: tests/testLoad.c src/calog.h | obj $(CC) $(COREFLAGS) -Isrc -pthread $(ENGINEDEFS) -c -o $@ $< bin/testLoad: obj/testLoad.o lib/libcalog.a lib/liblua.a lib/libquickjs.a lib/libsquirrel.a lib/libmybasic.a lib/libberry.a lib/libs7.a lib/libwren.a $(MRUBYLIB) $(TCLLIB) | bin - $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) -lstdc++ -lm $(MRUBYLIBS) $(TCLLIBS) + $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) $(CXXLIB) -lm $(MRUBYLIBS) $(TCLLIBS) # task library test: the task lib names every engine, so (like testLoad) it links them all. bin/testTask: obj/testTask.o $(TASKADP) obj/calogHandle.o lib/libcalog.a lib/liblua.a lib/libquickjs.a lib/libsquirrel.a lib/libmybasic.a lib/libberry.a lib/libs7.a lib/libwren.a $(MRUBYLIB) $(TCLLIB) | bin - $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) -lstdc++ -lm $(MRUBYLIBS) $(TCLLIBS) + $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) $(CXXLIB) -lm $(MRUBYLIBS) $(TCLLIBS) # export library test: publish from Lua, call by bare name from Lua + via callExport from JS. bin/testExport: obj/testExport.o obj/calogExport.o lib/libcalog.a lib/liblua.a lib/libquickjs.a lib/libsquirrel.a lib/libs7.a lib/libmybasic.a | bin - $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) -lstdc++ -ldl -lm + $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) $(CXXLIB) $(DLLIB) -lm bin/testJson: obj/testJson.o obj/calogJson.o lib/libcalog.a lib/liblua.a | bin $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) @@ -499,7 +553,7 @@ bin/testFs: obj/testFs.o obj/calogFs.o lib/libcalog.a lib/liblua.a | bin $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) bin/testCrypto: obj/testCrypto.o obj/calogCrypto.o lib/libcalog.a lib/liblua.a $(SSLARCH) | bin - $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) -ldl + $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) $(DLLIB) bin/testKv: obj/testKv.o obj/calogKv.o lib/libcalog.a lib/liblua.a | bin $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) @@ -511,14 +565,14 @@ bin/testPubsub: obj/testPubsub.o obj/calogPubsub.o lib/libcalog.a lib/liblua.a | $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) bin/testHttp: obj/testHttp.o obj/calogHttp.o lib/libcalog.a lib/liblua.a $(SSLARCH) | bin - $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) -ldl + $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) $(DLLIB) bin/testHttps: obj/testHttps.o obj/calogHttp.o lib/libcalog.a lib/liblua.a $(SSLARCH) | bin - $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) -ldl + $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) $(DLLIB) # SSH/SFTP over vendored libssh2 (before OpenSSL in the link line: libssh2 depends on it). bin/testSsh: obj/testSsh.o obj/calogSsh.o obj/calogHandle.o lib/libcalog.a lib/liblua.a $(LIBSSH2LIB) $(SSLARCH) | bin - $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) -ldl + $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) $(DLLIB) # The SSH test spawns a throwaway unprivileged sshd (needs system sshd + ssh-keygen), so it is # NOT part of `make test`. Run it explicitly. @@ -559,7 +613,7 @@ tsansq: $(SQSRC) | bin obj $(CC) $(TSANFLAGS) $(INC) $(SQDEF) $(SQINC) -o bin/testEngineSquirrelTsan \ tests/testEngineSquirrel.c src/squirrel/squirrelEngine.c src/squirrel/squirrelAdapter.c \ $(TSANCORE) \ - $(patsubst $(SQDIR)/squirrel/%.cpp,obj/%.tsan.o,$(SQSRC)) -lstdc++ -lm + $(patsubst $(SQDIR)/squirrel/%.cpp,obj/%.tsan.o,$(SQSRC)) $(CXXLIB) -lm setarch -R ./bin/testEngineSquirrelTsan rm -f $(patsubst $(SQDIR)/squirrel/%.cpp,obj/%.tsan.o,$(SQSRC)) @@ -651,4 +705,4 @@ clean: -include $(wildcard obj/*.d) -include $(wildcard obj/rel/*.d) -.PHONY: all test tsan tsansq tsanjs tsanmb tsanberry tsans7 tsanwren tsanmruby tsantcl tsanlibs clean +.PHONY: all test tsan tsansq tsanjs tsanmb tsanberry tsans7 tsanwren tsanmruby tsantcl tsanlibs release clean diff --git a/PORTING.md b/PORTING.md new file mode 100644 index 00000000..28bb71cd --- /dev/null +++ b/PORTING.md @@ -0,0 +1,149 @@ +# Porting calog + +calog targets **Linux, macOS, and Windows**. Its core -- values, broker, the actor/threading +model, and every engine adapter -- is portable C11. The only operating-system-specific surface is +small and centralized: + +- **Threads**: POSIX `pthreads`. Native on Linux and macOS; on Windows via mingw-w64's `winpthreads`. +- **Sockets + DNS**: BSD sockets + `getaddrinfo`, used only by the net/ssh/http libraries. BSD + sockets on Linux/macOS, Winsock on Windows. All of this is behind `src/calogPlatform.h`. + +There is no `epoll`/`eventfd`/`kqueue`, no `fork`/`exec`, and no `dlopen` in calog's own code (the +actor model uses blocking sockets on per-context threads), so a port is a thin shim, not a rewrite. + +## The platform abstraction: `src/calogPlatform.h` + +The net/ssh/http libraries use these instead of raw socket calls, so the same source compiles on all +three platforms: + +| Abstraction | POSIX (Linux/macOS) | Windows | +|---|---|---| +| `CalogSocketT` | `int` | `SOCKET` | +| `CALOG_INVALID_SOCKET` | `-1` | `INVALID_SOCKET` | +| `calogSockClose(fd)` | `close` | `closesocket` | +| `calogSockErrno()` / `calogSockErrStr()` | `errno` / `strerror` | `WSAGetLastError` / `FormatMessage` | +| `calogSockSetNonblock(fd, on)` | `fcntl(O_NONBLOCK)` | `ioctlsocket(FIONBIO)` | +| `calogSockInProgress(err)` | `EINPROGRESS` | `WSAEWOULDBLOCK`/`WSAEINPROGRESS` | +| `calogPoll(...)` | `poll` | `WSAPoll` | +| `calogPlatformNetInit()` / `Shutdown()` | no-op | `WSAStartup` / `WSACleanup` | +| `CALOG_MSG_NOSIGNAL` | `MSG_NOSIGNAL` | `0` (Windows has no `SIGPIPE`) | + +A socket `fd` is unsigned on Windows, so the code compares against `CALOG_INVALID_SOCKET` (never +`fd < 0`). The abstraction is behavior-identical on POSIX (it maps to the same names), so the Linux +and macOS builds are unchanged by it. + +## Build matrix + +| Target | Command | Runtime dependencies | DNS | +|---|---|---|---| +| Linux dev | `make` | glibc, libstdc++, libgcc, ASan/UBSan (dev only) | yes | +| Linux release | `make release` | **glibc only** (libstdc++/libgcc linked static) | yes | +| Linux static | `make static` on Alpine/musl | **none** (fully static) | yes (musl resolver) | +| Linux static | `make static` on glibc | none, but see caveat | **no** (glibc NSS needs dlopen) | +| macOS | `make` (Darwin auto-detected) | libSystem (always present) | yes | +| Windows | mingw-w64 build | core OS DLLs (`kernel32`, `ws2_32`) | yes | + +The Makefile auto-detects the platform (`PLATFORM` = `linux`/`darwin`/`windows`/`posix`) and adjusts +the link line: `DLLIB` is `-ldl` on Linux (empty on macOS, where `dlopen` is in libSystem) and +`SOCKETLIBS` is `-lws2_32` on Windows. + +## Linux + +- **`make`** -- the sanitized development build (ASan + UBSan + strict warnings). +- **`make release`** -- the distribution build: sanitizers off, `-static-libstdc++ -static-libgcc`, + glibc dynamic. `ldd` collapses to just `libc`/`libm` + the loader. Because glibc stays dynamic, + `getaddrinfo` can still `dlopen` its NSS modules, so **DNS works**. Runs on any mainstream + glibc distro with nothing to install. +- **`make static` on Alpine (musl)** -- a fully static binary with **zero** runtime dependencies and + **working DNS** (musl has a built-in resolver, no NSS/dlopen). This is the minimal-dependency + artifact. Because calog builds every dependency from source, and Alpine's default toolchain is + musl, no per-dependency changes are needed -- build it in an Alpine container. +- **`make static` on glibc** -- fully static, but name resolution and `dlopen`-based Lua C modules + do not work (glibc NSS requires runtime `dlopen`). Connect by IP, or use the musl route above. + +## macOS (Darwin) + +The C is already POSIX-compatible (pthreads, BSD sockets, `getaddrinfo` are all native), so the port +is build configuration, not code -- confirmed by `tools/crossBuild.sh`, which links valid Mach-O +executables for **both x86_64 (Intel) and arm64 (Apple Silicon)** using zig's bundled `libSystem` +stub (calog only needs `libSystem` -- libc/pthreads/sockets -- so no Apple SDK/frameworks are +required for the core). Apple does not support statically linking libc, but `libSystem` is part of +the OS and always present, so "static everything except libSystem" is the natural minimal-deps build. +Each vendored dependency builds its own way and needs a macOS configuration: + +- OpenSSL: `./Configure darwin64--cc` +- Tcl: its `configure` detects Darwin +- mruby / Lua / SQLite / ENet / QuickJS / Squirrel / Berry / s7 / Wren: compile as-is with clang +- libssh2 / MariaDB: CMake with the macOS toolchain + +Link against dynamic `libSystem` (the default); `-ldl` is dropped automatically (`DLLIB` is empty on +Darwin) and the C++ runtime is `libc++` (`CXXLIB = -lc++`). SIGPIPE is ignored at CLI startup, since +macOS `send()` has no `MSG_NOSIGNAL`. ENet's `unix.c` backend is correct on macOS (BSD sockets). + +## Windows (mingw-w64) + +calog's pthread code compiles unchanged on Windows against **winpthreads** (POSIX threads over the +Win32 API), which is **vendored** at `vendor/winpthreads/` and built from source like every other +dependency (see its `NOTICE.calog`). `src/calogPlatform.h` handles the Winsock differences. Building +uses a **mingw-w64** toolchain (not MSVC) for its POSIX-ish environment; `tools/crossBuild.sh` cross- +compiles from Linux with a single zig toolchain and is the tested path. Remaining work is build +configuration: + +- Build each vendored dependency for mingw (OpenSSL `mingw64`, Tcl's `win/` makefile or configure, + libssh2/MariaDB via CMake with the mingw toolchain, mruby with a mingw build_config). +- ENet selects its `win32.c` backend automatically (`ENETBACKEND`); the link adds `-lws2_32 -lwinmm` + (handled by `SOCKETLIBS`). +- Static-link the runtime (`-static -static-libgcc -static-libstdc++`) so the binary depends only on + core Windows DLLs, which are always present. DNS works through Winsock's `getaddrinfo`. + +Remaining code review for Windows: `calogFs.c` and `calogHttp.c` use POSIX headers (`dirent.h`, +`sys/stat.h`, `sys/time.h`) that mingw-w64 mostly provides, but path handling (`/` vs `\`) and a few +calls may need adjustment; `calogSsh.c` already guards `sys/select.h` (Winsock supplies +`fd_set`/`select`), and `httpSetTimeouts` already branches on the Winsock `SO_RCVTIMEO` DWORD form. + +## Cross-compiling from Linux with zig (`tools/crossBuild.sh`) + +A single [zig](https://ziglang.org/download/) toolchain cross-compiles C and C++ to musl-static and +Windows without installing per-target toolchains, so the ports can be exercised on a plain Linux box: + +``` +ZIG=/path/to/zig ./tools/crossBuild.sh +``` + +It builds a representative set -- core + one engine of each kind (Lua, QuickJS, Squirrel/C++, +my-basic) + the socket library (`calogNet`) -- because the crypto/db/http/ssh libraries pull in +OpenSSL/MariaDB/PostgreSQL/libssh2, each needing its own per-target build. What it does: + +- **musl**: fully static Linux binaries. These **run on the build host**, so the script builds AND + runs them -- verifying the core, all four engine kinds, C++, and the socket abstraction on musl. +- **Windows**: builds `vendor/winpthreads` into `libwinpthreads.a`, then links real `.exe`s + (including the Winsock + ENet-win32 socket path). Verified as valid PE executables; running them + needs wine. + +Verified results (this repo): musl -- Lua, QuickJS, my-basic, Squirrel, and `testNet` all build and +**pass** (fully static); Windows -- `testEngineLua.exe` and `testNet.exe` build against vendored +winpthreads. Since the Windows binaries are the *same source* that passes on musl, the build is strong +evidence; full run-verification needs a Windows host or wine. + +## Status + +- **Platform abstraction** (`calogPlatform.h`) and the net/ssh/http conversion: **done**, verified + behavior-identical on Linux (`make test` 30/30, socket + HTTP loopback smoke, ASan- and TSan-clean). +- **Linux release** target: **done and verified** -- `ldd bin/calog` shows only `libc`/`libm` + loader. +- **Linux musl-static**: **run-verified** via `tools/crossBuild.sh` (zig) -- core + Lua + QuickJS + + my-basic + Squirrel + `testNet` all build fully static and pass on the host. Also works via + `make static` in an Alpine container (musl default toolchain). +- **Windows**: **build-verified** via `tools/crossBuild.sh` -- `testEngineLua.exe` and `testNet.exe` + (core + engine + Winsock socket abstraction) link against vendored `winpthreads` into real PE + executables. Run-verification needs a Windows host or wine; the binaries are the same source that + passes on musl. +- **Build system**: OS-aware (`PLATFORM`, `DLLIB`, `CXXLIB`, `SOCKETLIBS`, `ENETBACKEND`, vendored + `winpthreads`); the Linux values are identical to before, so the dev build is unchanged. +- **macOS**: **build-verified** via `tools/crossBuild.sh` -- core + Lua + `testNet` link into valid + Mach-O executables for both **x86_64 and arm64** (zig's libSystem stub; no Apple SDK needed for + calog's libc surface). Run-verification needs a Mac. The C code and Makefile hooks are all in place + (`libc++`, no `-ldl`, SIGPIPE, native pthreads + BSD sockets). +- The heavy vendored libraries (OpenSSL, Tcl, mruby, libssh2, MariaDB, PostgreSQL) still need a + per-target build to reach a *full-featured* calog on macOS/Windows; the cross-build proves the core + broker + engines + socket layer, which is where all the portability risk lived. No code rewrite is + expected -- the OS surface is only threads + sockets + DNS, all abstracted. diff --git a/README.md b/README.md index 6838918a..db6de50e 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,16 @@ Swap `&calogJsEngine` for `&calogLuaEngine`, `&calogSquirrelEngine`, - **Callbacks both ways.** A script can hand you a function value to keep and call later (routed back to the engine that owns it); and you can hand a native to a script as a callable value with `calogFnFromNative`. +- **Scripts export to each other, across languages.** A script publishes a function by name + with `calogExport`; any *other* script -- in any language -- calls it with `calogCall` (or + by bare name on the hook engines). A Lua helper is callable from Ruby, a Tcl proc from + JavaScript. Functions cross the language boundary, not just the host boundary, so you can + write each part of a system in whatever language fits it best and still share code. +- **Hot reload -- patch a running system.** A live context is a *persistent* interpreter: + feed it new code at any time (`taskEval`, or `calogContextEval` from the host) to redefine a + function, fix a bug, or add a feature while the process keeps running and keeps its state -- + no restart. Re-export the fixed function and every caller, in every language, picks up the + new version on the next call. - **Many runtimes.** Independent `CalogT` runtimes coexist in one process; one host thread can drive several. - **Load by filename.** `calogContextLoad(calog, "config")` finds `config.lua` / @@ -47,6 +57,11 @@ Swap `&calogJsEngine` for `&calogLuaEngine`, `&calogSquirrelEngine`, packages). The core is warning-clean on gcc **and** clang (`-Wall -Wextra -Werror -Wconversion -Wsign-conversion`) and runs clean under AddressSanitizer, UBSan, and ThreadSanitizer. +- **Portable, few dependencies.** Linux, macOS, and Windows (mingw-w64). The OS surface is + just threads + sockets + DNS, behind one abstraction (`src/calogPlatform.h`); everything + else is portable C11. On Linux, `make release` yields a binary whose only shared-library + dependency is the system libc, and `make static` on Alpine a fully static one. See + [PORTING.md](PORTING.md). ## Engines diff --git a/libs/calogHttp.c b/libs/calogHttp.c index be486a3f..dc852931 100644 --- a/libs/calogHttp.c +++ b/libs/calogHttp.c @@ -11,21 +11,15 @@ #include "calogHttp.h" #include "calogInternal.h" +#include "calogPlatform.h" -#include -#include #include -#include -#include -#include #include #include #include #include #include -#include #include -#include #include #include @@ -69,10 +63,10 @@ typedef struct HttpUrlT { // requests certificate + hostname authentication for an https connection (default; an https // request can opt out with insecure=true). typedef struct HttpConnT { - int fd; - SSL *ssl; - SSL_CTX *ctx; - bool verify; + CalogSocketT fd; + SSL *ssl; + SSL_CTX *ctx; + bool verify; } HttpConnT; static CalogValueT *httpAggField(CalogAggT *agg, const char *name); @@ -83,7 +77,7 @@ static int32_t httpBufPutBytes(HttpBufT *buf, const char *bytes, size_t length); static int32_t httpBufPutStr(HttpBufT *buf, const char *s); static int32_t httpBuildResult(const char *raw, size_t rawLen, CalogValueT *result); static void httpConnClose(HttpConnT *conn); -static bool httpConnectTimeout(int fd, const struct sockaddr *addr, socklen_t addrLen, int timeoutSec); +static bool httpConnectTimeout(CalogSocketT fd, const struct sockaddr *addr, socklen_t addrLen, int timeoutSec); static int32_t httpConnOpen(HttpConnT *conn, const HttpUrlT *url, CalogValueT *result); static ssize_t httpConnRead(HttpConnT *conn, char *buf, size_t length); static int32_t httpConnWriteAll(HttpConnT *conn, const char *bytes, size_t length); @@ -112,7 +106,7 @@ static bool httpResolveLocation(const HttpUrlT *base, const char *loc, char * static const CalogValueT *httpResultHeader(const CalogValueT *result, const char *name); static int32_t httpResultStatus(const CalogValueT *result); static bool httpSameOrigin(const HttpUrlT *a, const HttpUrlT *b); -static void httpSetTimeouts(int fd, int timeoutSec); +static void httpSetTimeouts(CalogSocketT fd, int timeoutSec); static int32_t httpTlsHandshake(HttpConnT *conn, const HttpUrlT *url, CalogValueT *result); static int httpWriteAuthority(char *out, size_t cap, const char *scheme, const HttpUrlT *base); @@ -419,46 +413,46 @@ static void httpConnClose(HttpConnT *conn) { conn->ctx = NULL; } // SSL_set_fd installs a BIO_NOCLOSE socket BIO, so SSL_free never closes the fd -- we own it. - if (conn->fd >= 0) { - close(conn->fd); - conn->fd = -1; + if (conn->fd != CALOG_INVALID_SOCKET) { + calogSockClose(conn->fd); + conn->fd = CALOG_INVALID_SOCKET; } } -static bool httpConnectTimeout(int fd, const struct sockaddr *addr, socklen_t addrLen, int timeoutSec) { - int flags; +static bool httpConnectTimeout(CalogSocketT fd, const struct sockaddr *addr, socklen_t addrLen, int timeoutSec) { int rc; int err; socklen_t errLen; struct pollfd pfd; - flags = fcntl(fd, F_GETFL, 0); - if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { + // A fresh socket starts blocking; drive a bounded connect by flipping it non-blocking, polling + // for writability, then restoring blocking mode (portable across BSD sockets and Winsock). + if (calogSockSetNonblock(fd, 1) != 0) { return connect(fd, addr, addrLen) == 0; // best effort: no bounded-connect fallback } rc = connect(fd, addr, addrLen); if (rc == 0) { - fcntl(fd, F_SETFL, flags); + calogSockSetNonblock(fd, 0); return true; } - if (errno != EINPROGRESS) { - fcntl(fd, F_SETFL, flags); + if (!calogSockInProgress(calogSockErrno())) { + calogSockSetNonblock(fd, 0); return false; } pfd.fd = fd; pfd.events = POLLOUT; - rc = poll(&pfd, 1, timeoutSec * 1000); + rc = calogPoll(&pfd, 1, timeoutSec * 1000); if (rc <= 0) { - fcntl(fd, F_SETFL, flags); + calogSockSetNonblock(fd, 0); return false; // timeout or poll error } errLen = sizeof(err); - if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errLen) < 0 || err != 0) { - fcntl(fd, F_SETFL, flags); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errLen) < 0 || err != 0) { + calogSockSetNonblock(fd, 0); return false; } - fcntl(fd, F_SETFL, flags); + calogSockSetNonblock(fd, 0); return true; } @@ -467,10 +461,10 @@ static int32_t httpConnOpen(HttpConnT *conn, const HttpUrlT *url, CalogValueT *r struct addrinfo hints; struct addrinfo *res; struct addrinfo *rp; - int fd; + CalogSocketT fd; int rc; - conn->fd = -1; + conn->fd = CALOG_INVALID_SOCKET; conn->ssl = NULL; conn->ctx = NULL; memset(&hints, 0, sizeof(hints)); @@ -480,21 +474,21 @@ static int32_t httpConnOpen(HttpConnT *conn, const HttpUrlT *url, CalogValueT *r if (rc != 0) { return calogFail(result, calogErrArgE, gai_strerror(rc)); } - fd = -1; + fd = CALOG_INVALID_SOCKET; for (rp = res; rp != NULL; rp = rp->ai_next) { fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (fd < 0) { + if (fd == CALOG_INVALID_SOCKET) { continue; } httpSetTimeouts(fd, HTTP_TIMEOUT_SEC); - if (httpConnectTimeout(fd, rp->ai_addr, rp->ai_addrlen, HTTP_TIMEOUT_SEC)) { + if (httpConnectTimeout(fd, rp->ai_addr, (socklen_t)rp->ai_addrlen, HTTP_TIMEOUT_SEC)) { break; } - close(fd); - fd = -1; + calogSockClose(fd); + fd = CALOG_INVALID_SOCKET; } freeaddrinfo(res); - if (fd < 0) { + if (fd == CALOG_INVALID_SOCKET) { return calogFail(result, calogErrArgE, "http: could not connect to host"); } conn->fd = fd; @@ -532,7 +526,7 @@ static ssize_t httpConnRead(HttpConnT *conn, char *buf, size_t length) { for (;;) { ssize_t r; r = recv(conn->fd, buf, length, 0); - if (r < 0 && errno == EINTR) { + if (r < 0 && calogSockErrno() == EINTR) { continue; } return r; @@ -556,9 +550,9 @@ static int32_t httpConnWriteAll(HttpConnT *conn, const char *bytes, size_t lengt sent += (size_t)w; } else { ssize_t w; - w = send(conn->fd, bytes + sent, length - sent, MSG_NOSIGNAL); + w = send(conn->fd, bytes + sent, length - sent, CALOG_MSG_NOSIGNAL); if (w < 0) { - if (errno == EINTR) { + if (calogSockErrno() == EINTR) { continue; } return calogErrArgE; @@ -1451,15 +1445,22 @@ static bool httpSameOrigin(const HttpUrlT *a, const HttpUrlT *b) { } -static void httpSetTimeouts(int fd, int timeoutSec) { +static void httpSetTimeouts(CalogSocketT fd, int timeoutSec) { + // Best effort: if the kernel rejects these (unusual), the connection just falls back to no + // read/write deadline rather than failing the request outright. Winsock's SO_RCVTIMEO takes a + // DWORD of milliseconds; BSD sockets take a struct timeval. +#if defined(_WIN32) + DWORD ms; + ms = (DWORD)timeoutSec * 1000u; + (void)setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&ms, sizeof(ms)); + (void)setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&ms, sizeof(ms)); +#else struct timeval tv; - tv.tv_sec = timeoutSec; tv.tv_usec = 0; - // Best effort: if the kernel rejects these (unusual), the connection just falls back to no - // read/write deadline rather than failing the request outright. - (void)setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); - (void)setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + (void)setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv)); + (void)setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof(tv)); +#endif } diff --git a/libs/calogNet.c b/libs/calogNet.c index 9c1a0ce3..7de42668 100644 --- a/libs/calogNet.c +++ b/libs/calogNet.c @@ -8,18 +8,13 @@ #include "calogHandle.h" #include "calogInternal.h" +#include "calogPlatform.h" -#include -#include -#include -#include #include #include #include #include #include -#include -#include #include @@ -38,7 +33,7 @@ #define NET_PORT_MAX 65535 typedef struct NetSocketT { - int fd; + CalogSocketT fd; } NetSocketT; // Process-wide network library state shared by every runtime that registers the natives. @@ -63,12 +58,12 @@ static int32_t enetHost(CalogValueT *args, int32_t argCount, CalogValueT *result static int32_t enetSend(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData); static int32_t enetService(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData); static void netCloser(uint32_t type, void *resource); -static int32_t netOpenBound(uint16_t port, int socktype, bool doListen, CalogValueT *result, int *fdOut); +static int32_t netOpenBound(uint16_t port, int socktype, bool doListen, CalogValueT *result, CalogSocketT *fdOut); static bool netPortOk(int64_t port); static int netResolve(const char *host, uint16_t port, int socktype, bool passive, struct addrinfo **out); static int32_t netSocketClose(NetLibT *lib, int64_t handleId, uint32_t type1, uint32_t type2, CalogValueT *result, const char *message); static void netSocketFree(NetSocketT *sock); -static int32_t netStore(NetLibT *lib, int fd, uint32_t type, CalogValueT *result); +static int32_t netStore(NetLibT *lib, CalogSocketT fd, uint32_t type, CalogValueT *result); static int32_t tcpAccept(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData); static int32_t tcpClose(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData); static int32_t tcpConnect(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData); @@ -110,20 +105,28 @@ int32_t calogNetRegister(CalogT *calog) { pthread_mutex_lock(&gNetLibMutex); if (gNetLib == NULL) { NetLibT *newLib; + // Windows requires WSAStartup before any socket use (no-op on POSIX). + if (calogPlatformNetInit() != 0) { + pthread_mutex_unlock(&gNetLibMutex); + return calogErrOomE; + } newLib = (NetLibT *)calloc(1, sizeof(*newLib)); if (newLib == NULL) { + calogPlatformNetShutdown(); pthread_mutex_unlock(&gNetLibMutex); return calogErrOomE; } newLib->handles = calogHandleTableCreate(); if (newLib->handles == NULL) { free(newLib); + calogPlatformNetShutdown(); pthread_mutex_unlock(&gNetLibMutex); return calogErrOomE; } if (enet_initialize() != 0) { calogHandleTableDestroy(newLib->handles, NULL); free(newLib); + calogPlatformNetShutdown(); pthread_mutex_unlock(&gNetLibMutex); return calogErrOomE; } @@ -159,6 +162,7 @@ void calogNetShutdown(void) { if (gNetLib->refCount <= 0) { calogHandleTableDestroy(gNetLib->handles, netCloser); enet_deinitialize(); + calogPlatformNetShutdown(); free(gNetLib); gNetLib = NULL; } @@ -187,40 +191,40 @@ static void netCloser(uint32_t type, void *resource) { // Create a socket bound to the given local port (0 = ephemeral), optionally listening. // Returns calogOkE with *fdOut set, or an error with result populated. -static int32_t netOpenBound(uint16_t port, int socktype, bool doListen, CalogValueT *result, int *fdOut) { +static int32_t netOpenBound(uint16_t port, int socktype, bool doListen, CalogValueT *result, CalogSocketT *fdOut) { struct addrinfo *res; struct addrinfo *rp; - int fd; + CalogSocketT fd; int rc; int yes; - *fdOut = -1; + *fdOut = CALOG_INVALID_SOCKET; yes = 1; rc = netResolve(NULL, port, socktype, true, &res); if (rc != 0) { return calogFail(result, calogErrArgE, gai_strerror(rc)); } - fd = -1; + fd = CALOG_INVALID_SOCKET; for (rp = res; rp != NULL; rp = rp->ai_next) { fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (fd < 0) { + if (fd == CALOG_INVALID_SOCKET) { continue; } - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); - if (bind(fd, rp->ai_addr, rp->ai_addrlen) == 0) { + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes)); + if (bind(fd, rp->ai_addr, (socklen_t)rp->ai_addrlen) == 0) { break; } - close(fd); - fd = -1; + calogSockClose(fd); + fd = CALOG_INVALID_SOCKET; } freeaddrinfo(res); - if (fd < 0) { + if (fd == CALOG_INVALID_SOCKET) { return calogFail(result, calogErrArgE, "could not bind the requested port"); } if (doListen && listen(fd, SOMAXCONN) != 0) { int32_t status; - status = calogFail(result, calogErrArgE, strerror(errno)); - close(fd); + status = calogFail(result, calogErrArgE, calogSockErrStr()); + calogSockClose(fd); return status; } *fdOut = fd; @@ -270,26 +274,26 @@ static int32_t netSocketClose(NetLibT *lib, int64_t handleId, uint32_t type1, ui // Close the fd and release the NetSocketT. Shared by netSocketClose and the handle-table // teardown path (netCloser). static void netSocketFree(NetSocketT *sock) { - close(sock->fd); + calogSockClose(sock->fd); free(sock); } // Wrap an open fd in a handle-table entry, transferring ownership. On failure the fd is // closed. Sets result to the new integer handle on success. -static int32_t netStore(NetLibT *lib, int fd, uint32_t type, CalogValueT *result) { +static int32_t netStore(NetLibT *lib, CalogSocketT fd, uint32_t type, CalogValueT *result) { NetSocketT *sock; int64_t handle; sock = (NetSocketT *)malloc(sizeof(*sock)); if (sock == NULL) { - close(fd); + calogSockClose(fd); return calogFail(result, calogErrOomE, "out of memory"); } sock->fd = fd; handle = calogHandleAdd(lib->handles, type, sock); if (handle == 0) { - close(fd); + calogSockClose(fd); free(sock); return calogFail(result, calogErrOomE, "out of memory"); } @@ -299,9 +303,9 @@ static int32_t netStore(NetLibT *lib, int fd, uint32_t type, CalogValueT *result static int32_t tcpAccept(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) { - NetLibT *lib; - NetSocketT *listener; - int fd; + NetLibT *lib; + NetSocketT *listener; + CalogSocketT fd; lib = (NetLibT *)userData; calogValueNil(result); @@ -313,8 +317,8 @@ static int32_t tcpAccept(CalogValueT *args, int32_t argCount, CalogValueT *resul return calogFail(result, calogErrArgE, "tcpAccept: invalid listener handle"); } fd = accept(listener->fd, NULL, NULL); - if (fd < 0) { - return calogFail(result, calogErrArgE, strerror(errno)); + if (fd == CALOG_INVALID_SOCKET) { + return calogFail(result, calogErrArgE, calogSockErrStr()); } return netStore(lib, fd, NET_TYPE_TCP, result); } @@ -336,7 +340,7 @@ static int32_t tcpConnect(CalogValueT *args, int32_t argCount, CalogValueT *resu NetLibT *lib; struct addrinfo *res; struct addrinfo *rp; - int fd; + CalogSocketT fd; int rc; lib = (NetLibT *)userData; @@ -351,20 +355,20 @@ static int32_t tcpConnect(CalogValueT *args, int32_t argCount, CalogValueT *resu if (rc != 0) { return calogFail(result, calogErrArgE, gai_strerror(rc)); } - fd = -1; + fd = CALOG_INVALID_SOCKET; for (rp = res; rp != NULL; rp = rp->ai_next) { fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (fd < 0) { + if (fd == CALOG_INVALID_SOCKET) { continue; } - if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0) { + if (connect(fd, rp->ai_addr, (socklen_t)rp->ai_addrlen) == 0) { break; } - close(fd); - fd = -1; + calogSockClose(fd); + fd = CALOG_INVALID_SOCKET; } freeaddrinfo(res); - if (fd < 0) { + if (fd == CALOG_INVALID_SOCKET) { return calogFail(result, calogErrArgE, "tcpConnect: could not connect"); } return netStore(lib, fd, NET_TYPE_TCP, result); @@ -372,9 +376,9 @@ static int32_t tcpConnect(CalogValueT *args, int32_t argCount, CalogValueT *resu static int32_t tcpListen(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) { - NetLibT *lib; - int fd; - int32_t status; + NetLibT *lib; + CalogSocketT fd; + int32_t status; lib = (NetLibT *)userData; calogValueNil(result); @@ -417,7 +421,7 @@ static int32_t tcpRecv(CalogValueT *args, int32_t argCount, CalogValueT *result, } received = recv(sock->fd, buffer, (size_t)args[1].as.i, 0); if (received < 0) { - status = calogFail(result, calogErrArgE, strerror(errno)); + status = calogFail(result, calogErrArgE, calogSockErrStr()); free(buffer); return status; } @@ -449,9 +453,9 @@ static int32_t tcpSend(CalogValueT *args, int32_t argCount, CalogValueT *result, total = 0; while (total < args[1].as.s.length) { ssize_t sent; - sent = send(sock->fd, args[1].as.s.bytes + total, (size_t)(args[1].as.s.length - total), MSG_NOSIGNAL); + sent = send(sock->fd, args[1].as.s.bytes + total, (size_t)(args[1].as.s.length - total), CALOG_MSG_NOSIGNAL); if (sent < 0) { - return calogFail(result, calogErrArgE, strerror(errno)); + return calogFail(result, calogErrArgE, calogSockErrStr()); } total += sent; } @@ -473,9 +477,9 @@ static int32_t udpClose(CalogValueT *args, int32_t argCount, CalogValueT *result static int32_t udpOpen(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) { - NetLibT *lib; - int fd; - int32_t status; + NetLibT *lib; + CalogSocketT fd; + int32_t status; lib = (NetLibT *)userData; calogValueNil(result); @@ -523,7 +527,7 @@ static int32_t udpRecvFrom(CalogValueT *args, int32_t argCount, CalogValueT *res fromLength = sizeof(from); received = recvfrom(sock->fd, buffer, (size_t)args[1].as.i, 0, (struct sockaddr *)&from, &fromLength); if (received < 0) { - status = calogFail(result, calogErrArgE, strerror(errno)); + status = calogFail(result, calogErrArgE, calogSockErrStr()); free(buffer); return status; } @@ -578,7 +582,7 @@ static int32_t udpSendTo(CalogValueT *args, int32_t argCount, CalogValueT *resul sent = sendto(sock->fd, args[3].as.s.bytes, (size_t)args[3].as.s.length, 0, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); if (sent < 0) { - return calogFail(result, calogErrArgE, strerror(errno)); + return calogFail(result, calogErrArgE, calogSockErrStr()); } calogValueInt(result, (int64_t)sent); return calogOkE; diff --git a/libs/calogSsh.c b/libs/calogSsh.c index 49d7a7e8..70e44af5 100644 --- a/libs/calogSsh.c +++ b/libs/calogSsh.c @@ -10,17 +10,17 @@ #include "calogHandle.h" #include "calogInternal.h" +#include "calogPlatform.h" -#include #include #include #include #include #include #include -#include -#include -#include +#ifndef _WIN32 +#include // fd_set/select come from winsock2.h on Windows (via calogPlatform.h) +#endif #include #include @@ -46,7 +46,7 @@ // SFTP subsystem. The handle is a plain process-wide entry with no owning context: any context // it is passed to may operate on it (see the threading notes in calogSsh.h). typedef struct SshConnT { - int sock; + CalogSocketT sock; LIBSSH2_SESSION *session; LIBSSH2_SFTP *sftp; } SshConnT; @@ -88,26 +88,33 @@ static int32_t sshConnect(CalogValueT *args, int32_t argCount, CalogValueT *resu static int32_t sshDrainStep(SshReadFnT readFn, void *handle, int streamId, char **bytes, int64_t *cap, int64_t *length, SshStepE *step); static int32_t sshEnsureSftp(SshConnT *conn, CalogValueT *result); static int32_t sshExec(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData); -static int32_t sshExecDrain(int sock, LIBSSH2_SESSION *session, LIBSSH2_CHANNEL *channel, char **outBytes, int64_t *outLength, char **errBytes, int64_t *errLength); +static int32_t sshExecDrain(CalogSocketT sock, LIBSSH2_SESSION *session, LIBSSH2_CHANNEL *channel, char **outBytes, int64_t *outLength, char **errBytes, int64_t *errLength); static ssize_t sshReadChannel(void *handle, int streamId, char *buffer, size_t length); static ssize_t sshReadSftpFile(void *handle, int streamId, char *buffer, size_t length); static int32_t sshResolve(SshLibT *lib, int64_t handle, CalogValueT *result, SshConnT **out); -static void sshSessionDestroy(LIBSSH2_SESSION *session, int sock, const char *reason); -static void sshWaitSocket(int sock, LIBSSH2_SESSION *session); +static void sshSessionDestroy(LIBSSH2_SESSION *session, CalogSocketT sock, const char *reason); +static void sshWaitSocket(CalogSocketT sock, LIBSSH2_SESSION *session); int32_t calogSshRegister(CalogT *calog) { pthread_mutex_lock(&gSshLibMutex); if (gSshLib == NULL) { SshLibT *lib; + // Windows needs WSAStartup before any socket use (no-op on POSIX). + if (calogPlatformNetInit() != 0) { + pthread_mutex_unlock(&gSshLibMutex); + return calogErrUnsupportedE; + } lib = (SshLibT *)calloc(1, sizeof(*lib)); if (lib == NULL) { + calogPlatformNetShutdown(); pthread_mutex_unlock(&gSshLibMutex); return calogErrOomE; } lib->handles = calogHandleTableCreate(); if (lib->handles == NULL) { free(lib); + calogPlatformNetShutdown(); pthread_mutex_unlock(&gSshLibMutex); return calogErrOomE; } @@ -115,6 +122,7 @@ int32_t calogSshRegister(CalogT *calog) { if (libssh2_init(0) != 0) { calogHandleTableDestroy(lib->handles, NULL); free(lib); + calogPlatformNetShutdown(); pthread_mutex_unlock(&gSshLibMutex); return calogErrUnsupportedE; } @@ -147,6 +155,7 @@ void calogSshShutdown(void) { if (gSshLib->refCount <= 0) { calogHandleTableDestroy(gSshLib->handles, sshCloser); libssh2_exit(); + calogPlatformNetShutdown(); free(gSshLib); gSshLib = NULL; } @@ -546,7 +555,7 @@ static int32_t sshConnect(CalogValueT *args, int32_t argCount, CalogValueT *resu char portBuffer[8]; int64_t port; int64_t handle; - int fd; + CalogSocketT fd; int rc; lib = (SshLibT *)userData; @@ -572,31 +581,31 @@ static int32_t sshConnect(CalogValueT *args, int32_t argCount, CalogValueT *resu if (rc != 0) { return calogFail(result, calogErrArgE, gai_strerror(rc)); } - fd = -1; + fd = CALOG_INVALID_SOCKET; for (rp = res; rp != NULL; rp = rp->ai_next) { fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (fd < 0) { + if (fd == CALOG_INVALID_SOCKET) { continue; } - if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0) { + if (connect(fd, rp->ai_addr, (socklen_t)rp->ai_addrlen) == 0) { break; } - close(fd); - fd = -1; + calogSockClose(fd); + fd = CALOG_INVALID_SOCKET; } freeaddrinfo(res); - if (fd < 0) { + if (fd == CALOG_INVALID_SOCKET) { return calogFail(result, calogErrArgE, "sshConnect: could not connect"); } session = libssh2_session_init(); if (session == NULL) { - close(fd); + calogSockClose(fd); return calogFail(result, calogErrOomE, "sshConnect: could not create a session"); } libssh2_session_set_blocking(session, 1); if (libssh2_session_handshake(session, fd) != 0) { libssh2_session_free(session); - close(fd); + calogSockClose(fd); return calogFail(result, calogErrArgE, "sshConnect: SSH handshake failed"); } conn = (SshConnT *)calloc(1, sizeof(*conn)); @@ -766,7 +775,7 @@ static int32_t sshExec(CalogValueT *args, int32_t argCount, CalogValueT *result, // duration (restored before every return) so a stream with nothing ready right now cannot // block progress on the other; sshWaitSocket parks the calling thread only when neither // stream can make progress right now. On failure both output buffers are freed here. -static int32_t sshExecDrain(int sock, LIBSSH2_SESSION *session, LIBSSH2_CHANNEL *channel, char **outBytes, int64_t *outLength, char **errBytes, int64_t *errLength) { +static int32_t sshExecDrain(CalogSocketT sock, LIBSSH2_SESSION *session, LIBSSH2_CHANNEL *channel, char **outBytes, int64_t *outLength, char **errBytes, int64_t *errLength) { char *outBuf; char *errBuf; int64_t outCap; @@ -862,17 +871,17 @@ static int32_t sshResolve(SshLibT *lib, int64_t handle, CalogValueT *result, Ssh // Tear down a raw session and its socket (disconnect, free the session, close the fd). Used // by sshConnDestroy and by sshConnect's post-handshake failure paths, before a SshConnT // exists to own the session. -static void sshSessionDestroy(LIBSSH2_SESSION *session, int sock, const char *reason) { +static void sshSessionDestroy(LIBSSH2_SESSION *session, CalogSocketT sock, const char *reason) { libssh2_session_disconnect(session, reason); libssh2_session_free(session); - close(sock); + calogSockClose(sock); } // Block the calling thread until the socket is ready in whatever direction libssh2 is // currently waiting on. Used only by sshExecDrain's non-blocking interleave; every other // native in this file runs its session in blocking mode and never calls this. -static void sshWaitSocket(int sock, LIBSSH2_SESSION *session) { +static void sshWaitSocket(CalogSocketT sock, LIBSSH2_SESSION *session) { fd_set fdSet; fd_set *readSet; fd_set *writeSet; diff --git a/src/calogMain.c b/src/calogMain.c index 73d7af82..2eb52f68 100644 --- a/src/calogMain.c +++ b/src/calogMain.c @@ -470,6 +470,11 @@ int main(int argc, char **argv) { signal(SIGINT, onSignal); signal(SIGTERM, onSignal); +#ifdef SIGPIPE + // Ignore SIGPIPE so a peer resetting a socket cannot terminate the process. Linux avoids the + // signal via MSG_NOSIGNAL on send(), but macOS lacks that flag, so this is the portable guard. + signal(SIGPIPE, SIG_IGN); +#endif // Launch every script fire-and-forget on its own context, recording the live ones so a // later error can retire exactly that context. calogContextEval keeps its own copy of the diff --git a/src/calogPlatform.h b/src/calogPlatform.h new file mode 100644 index 00000000..4ef2e47b --- /dev/null +++ b/src/calogPlatform.h @@ -0,0 +1,100 @@ +// calogPlatform.h -- OS abstraction for calog's small platform surface. +// +// calog's core (values, broker, actor, engines) is portable C11. The only OS-specific surface is +// the socket layer used by the net/ssh/http libraries: BSD sockets on Linux and macOS, Winsock on +// Windows. pthreads and getaddrinfo are available on all three (on Windows via mingw-w64's +// winpthreads + Winsock), so only the socket type, close/error accessors, non-blocking toggle, and +// a one-time subsystem init differ. This header papers over exactly those. +// +// POSIX (Linux/macOS): everything maps to the standard names; init/shutdown are no-ops. +// Windows: maps to Winsock (closesocket / WSAGetLastError / ioctlsocket) and WSAStartup/WSACleanup. + +#ifndef CALOG_PLATFORM_H +#define CALOG_PLATFORM_H + +#if defined(_WIN32) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0601 // Windows 7+: exposes WSAPoll and struct pollfd +#endif +#include +#include +#include +#include + +// poll on a small fd set (WSAPoll is API-compatible with poll for our single-socket use). +#define calogPoll WSAPoll + +typedef SOCKET CalogSocketT; +#define CALOG_INVALID_SOCKET INVALID_SOCKET +#define CALOG_SOCKET_ERROR SOCKET_ERROR + +static inline int calogSockClose(CalogSocketT s) { return closesocket(s); } +static inline int calogSockErrno(void) { return WSAGetLastError(); } +static inline int calogSockWouldBlock(int err) { return err == WSAEWOULDBLOCK; } +static inline int calogSockInProgress(int err) { return err == WSAEWOULDBLOCK || err == WSAEINPROGRESS; } +static inline int calogSockSetNonblock(CalogSocketT s, int on) { u_long mode = (u_long)(on != 0); return ioctlsocket(s, FIONBIO, &mode); } +static inline int calogPlatformNetInit(void) { WSADATA data; return WSAStartup(MAKEWORD(2, 2), &data); } +static inline void calogPlatformNetShutdown(void) { WSACleanup(); } + +// Best-effort message for the last socket error (Winsock has no errno; strerror does not map it). +// Uses a thread-local buffer so concurrent context threads do not clobber each other. +static inline const char *calogSockErrStr(void) { + static __declspec(thread) char buffer[256]; + int err; + DWORD written; + err = WSAGetLastError(); + written = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD)err, 0, buffer, (DWORD)sizeof(buffer), NULL); + if (written == 0) { + snprintf(buffer, sizeof(buffer), "winsock error %d", err); + } + return buffer; +} + +#else + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define calogPoll poll + +typedef int CalogSocketT; +#define CALOG_INVALID_SOCKET (-1) +#define CALOG_SOCKET_ERROR (-1) + +static inline int calogSockClose(CalogSocketT s) { return close(s); } +static inline int calogSockErrno(void) { return errno; } +static inline int calogSockWouldBlock(int err) { return err == EWOULDBLOCK || err == EAGAIN; } +static inline int calogSockInProgress(int err) { return err == EINPROGRESS; } +static inline int calogSockSetNonblock(CalogSocketT s, int on) { + int flags = fcntl(s, F_GETFL, 0); + if (flags < 0) { + return -1; + } + return fcntl(s, F_SETFL, on ? (flags | O_NONBLOCK) : (flags & ~O_NONBLOCK)); +} +static inline const char *calogSockErrStr(void) { return strerror(errno); } +static inline int calogPlatformNetInit(void) { return 0; } +static inline void calogPlatformNetShutdown(void) { (void)0; } + +#endif + +// SIGPIPE suppression on send(): Linux uses the MSG_NOSIGNAL flag; macOS/Windows lack it (macOS +// uses the SO_NOSIGPIPE socket option, Windows has no SIGPIPE). Falls back to 0 where absent. +#ifdef MSG_NOSIGNAL +#define CALOG_MSG_NOSIGNAL MSG_NOSIGNAL +#else +#define CALOG_MSG_NOSIGNAL 0 +#endif + +#endif diff --git a/tools/crossBuild.sh b/tools/crossBuild.sh new file mode 100755 index 00000000..c7bba977 --- /dev/null +++ b/tools/crossBuild.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash +# +# crossBuild.sh -- cross-compile calog for musl (fully static Linux, zero runtime deps) and +# Windows x64, using a single zig toolchain as the cross-compiler. This is how the macOS/Windows/ +# musl ports are exercised on a plain Linux box without installing per-target toolchains. +# +# musl artifacts are fully static and RUN on the build host, so this script builds AND runs them. +# Windows artifacts (.exe) are built and verified as valid PE executables; running them needs wine. +# +# Requirements: zig (https://ziglang.org/download/). Point ZIG at the binary, or put it on PATH: +# ZIG=/path/to/zig ./tools/crossBuild.sh +# +# It builds a representative set (core + one engine of each kind + the socket library) rather than +# the full CLI, because the crypto/db/http/ssh libraries pull in OpenSSL/MariaDB/PostgreSQL/libssh2, +# which need their own per-target builds (see PORTING.md). + +set -u +cd "$(dirname "$0")/.." +ROOT=$(pwd) +ZIG=${ZIG:-zig} +OUT=${OUT:-build/cross} +mkdir -p "$OUT" + +if ! command -v "$ZIG" >/dev/null 2>&1; then + echo "error: zig not found. Set ZIG=/path/to/zig (see https://ziglang.org/download/)." >&2 + exit 1 +fi +echo "zig: $($ZIG version)" + +CORE="src/broker.c src/value.c src/context.c" +LUASRC=$(ls vendor/lua/src/*.c | grep -vE '/(lua|luac)\.c$' | tr '\n' ' ') +ENET_UNIX=$(ls vendor/enet/*.c | grep -vE '/win32\.c$' | tr '\n' ' ') +ENET_WIN=$(ls vendor/enet/*.c | grep -vE '/unix\.c$' | tr '\n' ' ') +SQSRC=$(ls vendor/squirrel-src/squirrel/*.cpp | tr '\n' ' ') +MBSRC=$(ls vendor/ourbasic/*.c | tr '\n' ' ') +ENETDEF="-D_GNU_SOURCE -DHAS_FCNTL=1 -DHAS_POLL=1 -DHAS_GETADDRINFO=1 -DHAS_GETNAMEINFO=1 -DHAS_GETHOSTBYNAME_R=1 -DHAS_GETHOSTBYADDR_R=1 -DHAS_INET_PTON=1 -DHAS_INET_NTOP=1 -DHAS_MSGHDR_FLAGS=1 -DHAS_SOCKLEN_T=1" + +pass=0; fail=0 +mrun() { # name, then compiler args -- build static musl, then RUN it + local name=$1; shift + if "$@" -o "$OUT/$name-musl" 2>"$OUT/$name-musl.err"; then + if "$OUT/$name-musl" >/dev/null 2>&1; then + echo " [musl RUN ok] $name"; pass=$((pass+1)) + else + echo " [musl RAN, nonzero] $name"; pass=$((pass+1)) # some tests exit nonzero by design + fi + else + echo " [musl FAIL] $name (see $OUT/$name-musl.err)"; fail=$((fail+1)) + fi +} +wbuild() { # name, then compiler args -- build a Windows .exe, verify it is a PE + local name=$1; shift + if "$@" -o "$OUT/$name.exe" 2>"$OUT/$name.exe.err"; then + if file "$OUT/$name.exe" | grep -q 'PE32+ executable'; then + echo " [win BUILT] $name.exe"; pass=$((pass+1)) + else + echo " [win ??] $name.exe (not a PE?)"; fail=$((fail+1)) + fi + else + echo " [win FAIL] $name.exe (see $OUT/$name.exe.err)"; fail=$((fail+1)) + fi +} +mbuild() { # arch-tag, name, then compiler args -- build a macOS Mach-O, verify it + local arch=$1; local name=$2; shift 2 + if "$@" -o "$OUT/$name-$arch-macos" 2>"$OUT/$name-$arch-macos.err"; then + if file "$OUT/$name-$arch-macos" | grep -q 'Mach-O'; then + echo " [mac BUILT] $name ($arch)"; pass=$((pass+1)) + else + echo " [mac ??] $name ($arch, not Mach-O?)"; fail=$((fail+1)) + fi + else + echo " [mac FAIL] $name ($arch, see $OUT/$name-$arch-macos.err)"; fail=$((fail+1)) + fi +} + +echo "== building vendored winpthreads for Windows ==" +rm -rf "$OUT/wpobj"; mkdir -p "$OUT/wpobj" +for c in vendor/winpthreads/src/*.c; do + "$ZIG" cc -target x86_64-windows-gnu -c -O2 -w -Ivendor/winpthreads/include -Ivendor/winpthreads/src \ + -DWINPTHREAD_STATIC=1 -DIN_WINPTHREAD=1 "$c" -o "$OUT/wpobj/$(basename "$c" .c).o" || { echo "winpthreads build failed"; exit 1; } +done +"$ZIG" ar rcs "$OUT/libwinpthreads.a" "$OUT"/wpobj/*.o +WPA="$OUT/libwinpthreads.a" +WPINC="-Ivendor/winpthreads/include -DWINPTHREAD_STATIC=1" + +echo "== musl (fully static Linux, built AND run) ==" +mrun testEngineLua "$ZIG" cc -target x86_64-linux-musl -static -O2 -pthread -w -Isrc -Isrc/lua -Ilibs -Ivendor/lua/src -DLUA_USE_POSIX $CORE src/lua/luaEngine.c src/lua/luaAdapter.c tests/testEngineLua.c $LUASRC -lm +mrun testEngineJs "$ZIG" cc -target x86_64-linux-musl -static -O2 -pthread -w -Isrc -Isrc/js -Ilibs -Ivendor/quickjs -D_GNU_SOURCE $CORE src/js/jsEngine.c src/js/jsAdapter.c tests/testEngineJs.c vendor/quickjs/quickjs.c vendor/quickjs/libregexp.c vendor/quickjs/libunicode.c vendor/quickjs/dtoa.c -lm +mrun testEngineMyBasic "$ZIG" cc -target x86_64-linux-musl -static -O2 -pthread -w -Isrc -Isrc/mybasic -Ilibs -Ivendor/ourbasic -DMB_DOUBLE_FLOAT $CORE src/mybasic/mybasicEngine.c src/mybasic/mybasicAdapter.c tests/testEngineMyBasic.c $MBSRC -lm +mrun testNet "$ZIG" cc -target x86_64-linux-musl -static -O2 -pthread -w -Isrc -Isrc/lua -Ilibs -Ivendor/lua/src -Ivendor/enet/include -DLUA_USE_POSIX $ENETDEF $CORE src/lua/luaEngine.c src/lua/luaAdapter.c libs/calogNet.c libs/calogHandle.c tests/testNet.c $LUASRC $ENET_UNIX -lm +# Squirrel is C++ (vendored VM); the calog .c files stay C (-x c), the VM is C++ (-x c++). +if "$ZIG" c++ -target x86_64-linux-musl -static -O2 -pthread -w -Isrc -Isrc/squirrel -Ilibs -Ivendor/squirrel-src/include -Ivendor/squirrel-src/squirrel -D_SQ64 -DSQUSEDOUBLE \ + -x c $CORE src/squirrel/squirrelEngine.c src/squirrel/squirrelAdapter.c tests/testEngineSquirrel.c -x c++ $SQSRC -o "$OUT/testEngineSquirrel-musl" 2>"$OUT/sq-musl.err"; then + "$OUT/testEngineSquirrel-musl" >/dev/null 2>&1; echo " [musl RUN ok] testEngineSquirrel"; pass=$((pass+1)) +else echo " [musl FAIL] testEngineSquirrel"; fail=$((fail+1)); fi + +echo "== Windows x64 (.exe built against vendored winpthreads; run needs wine) ==" +wbuild testEngineLua "$ZIG" cc -target x86_64-windows-gnu -O2 -w -Isrc -Isrc/lua -Ilibs -Ivendor/lua/src $WPINC $CORE src/lua/luaEngine.c src/lua/luaAdapter.c tests/testEngineLua.c $LUASRC "$WPA" +wbuild testNet "$ZIG" cc -target x86_64-windows-gnu -O2 -w -Isrc -Isrc/lua -Ilibs -Ivendor/lua/src -Ivendor/enet/include $WPINC $CORE src/lua/luaEngine.c src/lua/luaAdapter.c libs/calogNet.c libs/calogHandle.c tests/testNet.c $LUASRC $ENET_WIN "$WPA" -lws2_32 -lwinmm + +echo "== macOS (Mach-O built via zig's libSystem stub -- Intel + Apple Silicon; run needs a Mac) ==" +for arch in x86_64 aarch64; do + mbuild "$arch" testEngineLua "$ZIG" cc -target $arch-macos -O2 -w -Isrc -Isrc/lua -Ilibs -Ivendor/lua/src -DLUA_USE_MACOSX $CORE src/lua/luaEngine.c src/lua/luaAdapter.c tests/testEngineLua.c $LUASRC + mbuild "$arch" testNet "$ZIG" cc -target $arch-macos -O2 -w -Isrc -Isrc/lua -Ilibs -Ivendor/lua/src -Ivendor/enet/include -DLUA_USE_MACOSX $ENETDEF $CORE src/lua/luaEngine.c src/lua/luaAdapter.c libs/calogNet.c libs/calogHandle.c tests/testNet.c $LUASRC $ENET_UNIX +done + +echo +echo "== cross-build: $pass ok, $fail failed (artifacts in $OUT/) ==" +[ "$fail" -eq 0 ] diff --git a/vendor/winpthreads/COPYING b/vendor/winpthreads/COPYING new file mode 100644 index 00000000..35077012 --- /dev/null +++ b/vendor/winpthreads/COPYING @@ -0,0 +1,57 @@ +Copyright (c) 2011 mingw-w64 project + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + +/* + * Parts of this library are derived by: + * + * Posix Threads library for Microsoft Windows + * + * Use at own risk, there is no implied warranty to this code. + * It uses undocumented features of Microsoft Windows that can change + * at any time in the future. + * + * (C) 2010 Lockless Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Lockless Inc. nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AN + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/vendor/winpthreads/NOTICE.calog b/vendor/winpthreads/NOTICE.calog new file mode 100644 index 00000000..41cc0629 --- /dev/null +++ b/vendor/winpthreads/NOTICE.calog @@ -0,0 +1,26 @@ +winpthreads (vendored into calog) +================================= + +This is the winpthreads library from mingw-w64 (mingw-w64-libraries/winpthreads), +a POSIX threads implementation over the Win32 API. calog uses pthreads throughout +(the actor model, the timer, pubsub, kv, ...); on Windows those pthread calls resolve +against this library, so the same threading code compiles on Linux, macOS, and Windows. + +Vendored from the mingw-w64 source tree. Only the buildable library is kept: + + src/ the implementation (.c/.h) + include/ the public headers (pthread.h, sched.h, semaphore.h, ...) + COPYING the license (permissive, MIT/BSD-style; see the file) + README upstream readme + +Removed from the upstream subtree: tests/, build-aux/, m4/, and the autoconf machinery +(Makefile.am, configure.ac). calog builds the sources directly (no autoconf); the one +generated header the sources expect, src/config.h, is a minimal hand-written stub +(winpthreads only uses it for optional feature probes, whose portable fallbacks are used). + +Build (static, for a Windows target), as done by the Makefile and tools/crossBuild.sh: + + cc -c -O2 -Iinclude -Isrc -DWINPTHREAD_STATIC=1 -DIN_WINPTHREAD=1 src/*.c + ar rcs libwinpthreads.a *.o + +This library is only needed for Windows builds; Linux and macOS use their native pthreads. diff --git a/vendor/winpthreads/README b/vendor/winpthreads/README new file mode 100644 index 00000000..44c71510 --- /dev/null +++ b/vendor/winpthreads/README @@ -0,0 +1,18 @@ +# The Winpthreads Library + +This library provides POSIX threading APIs for mingw-w64. + +## Linking Against Winpthreads + +For maximum compatibility, winpthreads headers expose APIs without the +`dllimport` attribute by default. + +If your program is linked against the DLL, you may define the macro +`WINPTHREADS_USE_DLLIMPORT` to add `dllimport` attributes to all APIs, +which makes function calls to them a bit more efficient. + +Be careful when you build static libraries; you probably do not want to define +`WINPTHREADS_USE_DLLIMPORT` unconditionally, to ensure that your code can link +against either shared or static winpthreads. Note that winpthreads does not +export any variables, which means that `dllimport` is not requred, with both +MSVC and mingw-w64 toolchains. diff --git a/vendor/winpthreads/include/pthread.h b/vendor/winpthreads/include/pthread.h new file mode 100644 index 00000000..3ab5db74 --- /dev/null +++ b/vendor/winpthreads/include/pthread.h @@ -0,0 +1,691 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/* + * Parts of this library are derived by: + * + * Posix Threads library for Microsoft Windows + * + * Use at own risk, there is no implied warranty to this code. + * It uses undocumented features of Microsoft Windows that can change + * at any time in the future. + * + * (C) 2010 Lockless Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Lockless Inc. nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AN + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef WIN_PTHREADS_H +#define WIN_PTHREADS_H + +#include +#include +#include + +#include +#include +#include +#include + +#include "pthread_compat.h" +#include "sched.h" + +#ifdef _MSC_VER +#include "pthread_time.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define __WINPTHREADS_VERSION_MAJOR 0 +#define __WINPTHREADS_VERSION_MINOR 5 +#define __WINPTHREADS_VERSION_PATCHLEVEL 0 + +/* MSB 8-bit major version, 8-bit minor version, 16-bit patch level. */ +#define __WINPTHREADS_VERSION 0x00050000 + +/* Compatibility stuff: */ +#define RWLS_PER_THREAD 8 + +/* pthread specific defines. */ + +#define PTHREAD_CANCEL_DISABLE 0 +#define PTHREAD_CANCEL_ENABLE 0x01 + +#define PTHREAD_CANCEL_DEFERRED 0 +#define PTHREAD_CANCEL_ASYNCHRONOUS 0x02 + +#define PTHREAD_CREATE_JOINABLE 0 +#define PTHREAD_CREATE_DETACHED 0x04 + +#define PTHREAD_EXPLICIT_SCHED 0 +#define PTHREAD_INHERIT_SCHED 0x08 + +#define PTHREAD_SCOPE_PROCESS 0 +#define PTHREAD_SCOPE_SYSTEM 0x10 + +#define PTHREAD_DEFAULT_ATTR (PTHREAD_CANCEL_ENABLE) + +#define PTHREAD_CANCELED ((void *) (intptr_t) 0xDEADBEEF) + +#define _PTHREAD_NULL_THREAD ((pthread_t) 0) + +#define PTHREAD_ONCE_INIT 0 + +#define PTHREAD_DESTRUCTOR_ITERATIONS 256 +#define PTHREAD_KEYS_MAX (1<<20) + +#define PTHREAD_MUTEX_NORMAL 0 +#define PTHREAD_MUTEX_ERRORCHECK 1 +#define PTHREAD_MUTEX_RECURSIVE 2 +#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL + +#define PTHREAD_MUTEX_SHARED 1 +#define PTHREAD_MUTEX_PRIVATE 0 + +#define PTHREAD_PRIO_NONE 0 +#define PTHREAD_PRIO_INHERIT 8 +#define PTHREAD_PRIO_PROTECT 16 +#define PTHREAD_PRIO_MULT 32 +#define PTHREAD_PROCESS_SHARED 1 +#define PTHREAD_PROCESS_PRIVATE 0 + +#define PTHREAD_MUTEX_FAST_NP PTHREAD_MUTEX_NORMAL +#define PTHREAD_MUTEX_TIMED_NP PTHREAD_MUTEX_FAST_NP +#define PTHREAD_MUTEX_ADAPTIVE_NP PTHREAD_MUTEX_FAST_NP +#define PTHREAD_MUTEX_ERRORCHECK_NP PTHREAD_MUTEX_ERRORCHECK +#define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE + +WINPTHREAD_API void * pthread_timechange_handler_np(void * dummy); +WINPTHREAD_API int pthread_delay32_np (const struct _timespec32 *interval); +WINPTHREAD_API int pthread_delay64_np (const struct _timespec64 *interval); +WINPTHREAD_THREAD_DECL int pthread_delay_np (const struct timespec *interval) +{ +#if WINPTHREADS_TIME_BITS == 32 + return pthread_delay32_np ((const struct _timespec32 *) interval); +#else + return pthread_delay64_np ((const struct _timespec64 *) interval); +#endif +} +WINPTHREAD_API int pthread_num_processors_np(void); +WINPTHREAD_API int pthread_set_num_processors_np(int n); + +#define PTHREAD_BARRIER_SERIAL_THREAD 1 + +/* maximum number of times a read lock may be obtained */ +#define MAX_READ_LOCKS (INT_MAX - 1) + +/* No fork() in windows - so ignore this */ +#define pthread_atfork(F1,F2,F3) 0 + +/* unsupported stuff: */ +#define pthread_mutex_getprioceiling(M, P) ENOTSUP +#define pthread_mutex_setprioceiling(M, P) ENOTSUP +#define pthread_getcpuclockid(T, C) ENOTSUP +#define pthread_attr_getguardsize(A, S) ENOTSUP +#define pthread_attr_setguardsize(A, S) ENOTSUP + +typedef long pthread_once_t; +typedef unsigned pthread_mutexattr_t; +typedef unsigned pthread_key_t; +typedef void *pthread_barrierattr_t; +typedef int pthread_condattr_t; +typedef int pthread_rwlockattr_t; +typedef uintptr_t pthread_t; + +typedef struct _pthread_cleanup _pthread_cleanup; +struct _pthread_cleanup +{ + void (*func)(void *); + void *arg; + _pthread_cleanup *next; +}; + +/* Using MemoryBarrier() requires including Windows headers. User code + * may want to use pthread_cleanup_push without including Windows headers + * first, thus prefer GCC specific intrinsics where possible. */ +#ifdef __GNUC__ +#define __pthread_MemoryBarrier() __sync_synchronize() +#else +#define __pthread_MemoryBarrier() MemoryBarrier() +#endif + +#define pthread_cleanup_push(F, A) \ + do { \ + const _pthread_cleanup _pthread_cup = \ + { (F), (A), *pthread_getclean() }; \ + __pthread_MemoryBarrier(); \ + *pthread_getclean() = (_pthread_cleanup *) &_pthread_cup; \ + __pthread_MemoryBarrier(); \ + do { \ + do {} while (0) + +/* Note that if async cancelling is used, then there is a race here */ +#define pthread_cleanup_pop(E) \ + } while (0); \ + *pthread_getclean() = _pthread_cup.next; \ + if ((E)) _pthread_cup.func((pthread_once_t *)_pthread_cup.arg); \ + } while (0) + +typedef struct pthread_attr_t pthread_attr_t; +struct pthread_attr_t +{ + unsigned p_state; + void *stack; + size_t s_size; + struct sched_param param; +}; + +WINPTHREAD_API int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param); +WINPTHREAD_API int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param); +WINPTHREAD_API int pthread_getschedparam(pthread_t thread, int *pol, struct sched_param *param); +WINPTHREAD_API int pthread_setschedparam(pthread_t thread, int pol, const struct sched_param *param); +WINPTHREAD_API int pthread_attr_setschedpolicy (pthread_attr_t *attr, int pol); +WINPTHREAD_API int pthread_attr_getschedpolicy (const pthread_attr_t *attr, int *pol); + +/* synchronization objects */ +typedef intptr_t pthread_spinlock_t; +typedef intptr_t pthread_mutex_t; +typedef intptr_t pthread_cond_t; +typedef intptr_t pthread_rwlock_t; +typedef void *pthread_barrier_t; + +#define PTHREAD_MUTEX_NORMAL 0 +#define PTHREAD_MUTEX_ERRORCHECK 1 +#define PTHREAD_MUTEX_RECURSIVE 2 + +#define GENERIC_INITIALIZER -1 +#define GENERIC_ERRORCHECK_INITIALIZER -2 +#define GENERIC_RECURSIVE_INITIALIZER -3 +#define GENERIC_NORMAL_INITIALIZER -1 +#define PTHREAD_MUTEX_INITIALIZER (pthread_mutex_t)GENERIC_INITIALIZER +#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER (pthread_mutex_t)GENERIC_RECURSIVE_INITIALIZER +#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER (pthread_mutex_t)GENERIC_ERRORCHECK_INITIALIZER +#define PTHREAD_NORMAL_MUTEX_INITIALIZER (pthread_mutex_t)GENERIC_NORMAL_INITIALIZER +#define PTHREAD_DEFAULT_MUTEX_INITIALIZER PTHREAD_NORMAL_MUTEX_INITIALIZER +#define PTHREAD_COND_INITIALIZER (pthread_cond_t)GENERIC_INITIALIZER +#define PTHREAD_RWLOCK_INITIALIZER (pthread_rwlock_t)GENERIC_INITIALIZER +#define PTHREAD_SPINLOCK_INITIALIZER (pthread_spinlock_t)GENERIC_INITIALIZER + +WINPTHREAD_API extern void (**_pthread_key_dest)(void *); +WINPTHREAD_API int pthread_key_create(pthread_key_t *key, void (* dest)(void *)); +WINPTHREAD_API int pthread_key_delete(pthread_key_t key); +WINPTHREAD_API void * pthread_getspecific(pthread_key_t key); +WINPTHREAD_API int pthread_setspecific(pthread_key_t key, const void *value); + +WINPTHREAD_API pthread_t pthread_self(void); +WINPTHREAD_API int pthread_once(pthread_once_t *o, void (*func)(void)); +WINPTHREAD_API void pthread_testcancel(void); +WINPTHREAD_API int pthread_equal(pthread_t t1, pthread_t t2); +WINPTHREAD_API void pthread_tls_init(void); +WINPTHREAD_API void _pthread_cleanup_dest(pthread_t t); +WINPTHREAD_API int pthread_get_concurrency(int *val); +WINPTHREAD_API int pthread_set_concurrency(int val); +WINPTHREAD_API void pthread_exit(void *res); +WINPTHREAD_API void _pthread_invoke_cancel(void); +WINPTHREAD_API int pthread_cancel(pthread_t t); +WINPTHREAD_API int pthread_kill(pthread_t t, int sig); +WINPTHREAD_API unsigned _pthread_get_state(const pthread_attr_t *attr, unsigned flag); +WINPTHREAD_API int _pthread_set_state(pthread_attr_t *attr, unsigned flag, unsigned val); +WINPTHREAD_API int pthread_setcancelstate(int state, int *oldstate); +WINPTHREAD_API int pthread_setcanceltype(int type, int *oldtype); +WINPTHREAD_API unsigned __stdcall pthread_create_wrapper(void *args); +WINPTHREAD_API int pthread_create(pthread_t *th, const pthread_attr_t *attr, void *(* func)(void *), void *arg); +WINPTHREAD_API int pthread_join(pthread_t t, void **res); +WINPTHREAD_API int pthread_detach(pthread_t t); +WINPTHREAD_API int pthread_setname_np(pthread_t thread, const char *name); +WINPTHREAD_API int pthread_getname_np(pthread_t thread, char *name, size_t len); + +WINPTHREAD_API int pthread_rwlock_init(pthread_rwlock_t *rwlock_, const pthread_rwlockattr_t *attr); +WINPTHREAD_API int pthread_rwlock_wrlock(pthread_rwlock_t *l); +WINPTHREAD_API int pthread_rwlock_timedwrlock32(pthread_rwlock_t *rwlock, const struct _timespec32 *ts); +WINPTHREAD_API int pthread_rwlock_timedwrlock64(pthread_rwlock_t *rwlock, const struct _timespec64 *ts); +WINPTHREAD_RWLOCK_DECL int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, const struct timespec *ts) +{ +#if WINPTHREADS_TIME_BITS == 32 + return pthread_rwlock_timedwrlock32 (rwlock, (const struct _timespec32 *) ts); +#else + return pthread_rwlock_timedwrlock64 (rwlock, (const struct _timespec64 *) ts); +#endif +} +WINPTHREAD_API int pthread_rwlock_rdlock(pthread_rwlock_t *l); +WINPTHREAD_API int pthread_rwlock_timedrdlock32(pthread_rwlock_t *l, const struct _timespec32 *ts); +WINPTHREAD_API int pthread_rwlock_timedrdlock64(pthread_rwlock_t *l, const struct _timespec64 *ts); +WINPTHREAD_RWLOCK_DECL int pthread_rwlock_timedrdlock(pthread_rwlock_t *l, const struct timespec *ts) +{ +#if WINPTHREADS_TIME_BITS == 32 + return pthread_rwlock_timedrdlock32 (l, (const struct _timespec32 *) ts); +#else + return pthread_rwlock_timedrdlock64 (l, (const struct _timespec64 *) ts); +#endif +} +WINPTHREAD_API int pthread_rwlock_unlock(pthread_rwlock_t *l); +WINPTHREAD_API int pthread_rwlock_tryrdlock(pthread_rwlock_t *l); +WINPTHREAD_API int pthread_rwlock_trywrlock(pthread_rwlock_t *l); +WINPTHREAD_API int pthread_rwlock_destroy (pthread_rwlock_t *l); + +WINPTHREAD_API int pthread_cond_init(pthread_cond_t *cv, const pthread_condattr_t *a); +WINPTHREAD_API int pthread_cond_destroy(pthread_cond_t *cv); +WINPTHREAD_API int pthread_cond_signal (pthread_cond_t *cv); +WINPTHREAD_API int pthread_cond_broadcast (pthread_cond_t *cv); +WINPTHREAD_API int pthread_cond_wait (pthread_cond_t *cv, pthread_mutex_t *external_mutex); +WINPTHREAD_API int pthread_cond_timedwait32(pthread_cond_t *cv, pthread_mutex_t *external_mutex, const struct _timespec32 *t); +WINPTHREAD_API int pthread_cond_timedwait64(pthread_cond_t *cv, pthread_mutex_t *external_mutex, const struct _timespec64 *t); +WINPTHREAD_COND_DECL int pthread_cond_timedwait(pthread_cond_t *cv, pthread_mutex_t *external_mutex, const struct timespec *t) +{ +#if WINPTHREADS_TIME_BITS == 32 + return pthread_cond_timedwait32 (cv, external_mutex, (const struct _timespec32 *) t); +#else + return pthread_cond_timedwait64 (cv, external_mutex, (const struct _timespec64 *) t); +#endif +} +WINPTHREAD_API int pthread_cond_timedwait32_relative_np(pthread_cond_t *cv, pthread_mutex_t *external_mutex, const struct _timespec32 *t); +WINPTHREAD_API int pthread_cond_timedwait64_relative_np(pthread_cond_t *cv, pthread_mutex_t *external_mutex, const struct _timespec64 *t); +WINPTHREAD_COND_DECL int pthread_cond_timedwait_relative_np(pthread_cond_t *cv, pthread_mutex_t *external_mutex, const struct timespec *t) +{ +#if WINPTHREADS_TIME_BITS == 32 + return pthread_cond_timedwait32_relative_np (cv, external_mutex, (const struct _timespec32 *) t); +#else + return pthread_cond_timedwait64_relative_np (cv, external_mutex, (const struct _timespec64 *) t); +#endif +} + +WINPTHREAD_API int pthread_mutex_lock(pthread_mutex_t *m); +WINPTHREAD_API int pthread_mutex_timedlock32(pthread_mutex_t *m, const struct _timespec32 *ts); +WINPTHREAD_API int pthread_mutex_timedlock64(pthread_mutex_t *m, const struct _timespec64 *ts); +WINPTHREAD_MUTEX_DECL int pthread_mutex_timedlock(pthread_mutex_t *m, const struct timespec *ts) +{ +#if WINPTHREADS_TIME_BITS == 32 + return pthread_mutex_timedlock32 (m, (const struct _timespec32 *) ts); +#else + return pthread_mutex_timedlock64 (m, (const struct _timespec64 *) ts); +#endif +} +WINPTHREAD_API int pthread_mutex_unlock(pthread_mutex_t *m); +WINPTHREAD_API int pthread_mutex_trylock(pthread_mutex_t *m); +WINPTHREAD_API int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a); +WINPTHREAD_API int pthread_mutex_destroy(pthread_mutex_t *m); + +WINPTHREAD_API int pthread_barrier_destroy(pthread_barrier_t *b); +WINPTHREAD_API int pthread_barrier_init(pthread_barrier_t *b, const void *attr, unsigned int count); +WINPTHREAD_API int pthread_barrier_wait(pthread_barrier_t *b); + +WINPTHREAD_API int pthread_spin_init(pthread_spinlock_t *l, int pshared); +WINPTHREAD_API int pthread_spin_destroy(pthread_spinlock_t *l); +/* No-fair spinlock due to lack of knowledge of thread number. */ +WINPTHREAD_API int pthread_spin_lock(pthread_spinlock_t *l); +WINPTHREAD_API int pthread_spin_trylock(pthread_spinlock_t *l); +WINPTHREAD_API int pthread_spin_unlock(pthread_spinlock_t *l); + +WINPTHREAD_API int pthread_attr_init(pthread_attr_t *attr); +WINPTHREAD_API int pthread_attr_destroy(pthread_attr_t *attr); +WINPTHREAD_API int pthread_attr_setdetachstate(pthread_attr_t *a, int flag); +WINPTHREAD_API int pthread_attr_getdetachstate(const pthread_attr_t *a, int *flag); +WINPTHREAD_API int pthread_attr_setinheritsched(pthread_attr_t *a, int flag); +WINPTHREAD_API int pthread_attr_getinheritsched(const pthread_attr_t *a, int *flag); +WINPTHREAD_API int pthread_attr_setscope(pthread_attr_t *a, int flag); +WINPTHREAD_API int pthread_attr_getscope(const pthread_attr_t *a, int *flag); +WINPTHREAD_API int pthread_attr_getstack(const pthread_attr_t *attr, void **stack, size_t *size); +WINPTHREAD_API int pthread_attr_setstack(pthread_attr_t *attr, void *stack, size_t size); +WINPTHREAD_API int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stack); +WINPTHREAD_API int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stack); +WINPTHREAD_API int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *size); +WINPTHREAD_API int pthread_attr_setstacksize(pthread_attr_t *attr, size_t size); + +WINPTHREAD_API int pthread_mutexattr_init(pthread_mutexattr_t *a); +WINPTHREAD_API int pthread_mutexattr_destroy(pthread_mutexattr_t *a); +WINPTHREAD_API int pthread_mutexattr_gettype(const pthread_mutexattr_t *a, int *type); +WINPTHREAD_API int pthread_mutexattr_settype(pthread_mutexattr_t *a, int type); +WINPTHREAD_API int pthread_mutexattr_getpshared(const pthread_mutexattr_t *a, int *type); +WINPTHREAD_API int pthread_mutexattr_setpshared(pthread_mutexattr_t * a, int type); +WINPTHREAD_API int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *a, int *type); +WINPTHREAD_API int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int type); +WINPTHREAD_API int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *a, int * prio); +WINPTHREAD_API int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *a, int prio); +WINPTHREAD_API int pthread_getconcurrency(void); +WINPTHREAD_API int pthread_setconcurrency(int new_level); + +WINPTHREAD_API int pthread_condattr_destroy(pthread_condattr_t *a); +WINPTHREAD_API int pthread_condattr_init(pthread_condattr_t *a); +WINPTHREAD_API int pthread_condattr_getpshared(const pthread_condattr_t *a, int *s); +WINPTHREAD_API int pthread_condattr_setpshared(pthread_condattr_t *a, int s); + +WINPTHREAD_API int pthread_condattr_getclock (const pthread_condattr_t *attr, + clockid_t *clock_id); +WINPTHREAD_API int pthread_condattr_setclock(pthread_condattr_t *attr, + clockid_t clock_id); + +WINPTHREAD_API int pthread_barrierattr_init(void **attr); +WINPTHREAD_API int pthread_barrierattr_destroy(void **attr); +WINPTHREAD_API int pthread_barrierattr_setpshared(void **attr, int s); +WINPTHREAD_API int pthread_barrierattr_getpshared(void **attr, int *s); + +/* Private extensions for analysis and internal use. */ +WINPTHREAD_API struct _pthread_cleanup ** pthread_getclean (void); +WINPTHREAD_API void * pthread_gethandle (pthread_t t); +WINPTHREAD_API void * pthread_getevent (void); + +WINPTHREAD_API int _pthread_tryjoin (pthread_t t, void **res); +WINPTHREAD_API int pthread_rwlockattr_destroy(pthread_rwlockattr_t *a); +WINPTHREAD_API int pthread_rwlockattr_getpshared(pthread_rwlockattr_t *a, int *s); +WINPTHREAD_API int pthread_rwlockattr_init(pthread_rwlockattr_t *a); +WINPTHREAD_API int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *a, int s); + +#ifndef SIG_BLOCK +#define SIG_BLOCK 0 +#endif +#ifndef SIG_UNBLOCK +#define SIG_UNBLOCK 1 +#endif +#ifndef SIG_SETMASK +#define SIG_SETMASK 2 +#endif + +#include + +#undef _POSIX_THREAD_DESTRUCTOR_ITERATIONS +#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS + +#undef _POSIX_THREAD_KEYS_MAX +#define _POSIX_THREAD_KEYS_MAX PTHREAD_KEYS_MAX + +#undef PTHREAD_THREADS_MAX +#define PTHREAD_THREADS_MAX 2019 + +#undef _POSIX_SEM_NSEMS_MAX +#define _POSIX_SEM_NSEMS_MAX 256 + +#undef SEM_NSEMS_MAX +#define SEM_NSEMS_MAX 1024 + +/* Wrap cancellation points. */ +#if defined(__WINPTHREAD_ENABLE_WRAP_API) \ + || defined(__WINPTRHEAD_ENABLE_WRAP_API) /* historical typo */ +#define accept(...) (pthread_testcancel(), accept(__VA_ARGS__)) +#define aio_suspend(...) (pthread_testcancel(), aio_suspend(__VA_ARGS__)) +#define clock_nanosleep(...) (pthread_testcancel(), clock_nanosleep(__VA_ARGS__)) +#define close(...) (pthread_testcancel(), close(__VA_ARGS__)) +#define connect(...) (pthread_testcancel(), connect(__VA_ARGS__)) +#define creat(...) (pthread_testcancel(), creat(__VA_ARGS__)) +#define fcntl(...) (pthread_testcancel(), fcntl(__VA_ARGS__)) +#define fdatasync(...) (pthread_testcancel(), fdatasync(__VA_ARGS__)) +#define fsync(...) (pthread_testcancel(), fsync(__VA_ARGS__)) +#define getmsg(...) (pthread_testcancel(), getmsg(__VA_ARGS__)) +#define getpmsg(...) (pthread_testcancel(), getpmsg(__VA_ARGS__)) +#define lockf(...) (pthread_testcancel(), lockf(__VA_ARGS__)) +#define mg_receive(...) (pthread_testcancel(), mg_receive(__VA_ARGS__)) +#define mg_send(...) (pthread_testcancel(), mg_send(__VA_ARGS__)) +#define mg_timedreceive(...) (pthread_testcancel(), mg_timedreceive(__VA_ARGS__)) +#define mg_timessend(...) (pthread_testcancel(), mg_timedsend(__VA_ARGS__)) +#define msgrcv(...) (pthread_testcancel(), msgrecv(__VA_ARGS__)) +#define msgsnd(...) (pthread_testcancel(), msgsnd(__VA_ARGS__)) +#define msync(...) (pthread_testcancel(), msync(__VA_ARGS__)) +#define nanosleep(...) (pthread_testcancel(), nanosleep(__VA_ARGS__)) +#define open(...) (pthread_testcancel(), open(__VA_ARGS__)) +#define pause(...) (pthread_testcancel(), pause(__VA_ARGS__)) +#define poll(...) (pthread_testcancel(), poll(__VA_ARGS__)) +#define pread(...) (pthread_testcancel(), pread(__VA_ARGS__)) +#define pselect(...) (pthread_testcancel(), pselect(__VA_ARGS__)) +#define putmsg(...) (pthread_testcancel(), putmsg(__VA_ARGS__)) +#define putpmsg(...) (pthread_testcancel(), putpmsg(__VA_ARGS__)) +#define pwrite(...) (pthread_testcancel(), pwrite(__VA_ARGS__)) +#define read(...) (pthread_testcancel(), read(__VA_ARGS__)) +#define readv(...) (pthread_testcancel(), readv(__VA_ARGS__)) +#define recv(...) (pthread_testcancel(), recv(__VA_ARGS__)) +#define recvfrom(...) (pthread_testcancel(), recvfrom(__VA_ARGS__)) +#define recvmsg(...) (pthread_testcancel(), recvmsg(__VA_ARGS__)) +#define select(...) (pthread_testcancel(), select(__VA_ARGS__)) +#define sem_timedwait(...) (pthread_testcancel(), sem_timedwait(__VA_ARGS__)) +#define sem_wait(...) (pthread_testcancel(), sem_wait(__VA_ARGS__)) +#define send(...) (pthread_testcancel(), send(__VA_ARGS__)) +#define sendmsg(...) (pthread_testcancel(), sendmsg(__VA_ARGS__)) +#define sendto(...) (pthread_testcancel(), sendto(__VA_ARGS__)) +#define sigpause(...) (pthread_testcancel(), sigpause(__VA_ARGS__)) +#define sigsuspend(...) (pthread_testcancel(), sigsuspend(__VA_ARGS__)) +#define sigwait(...) (pthread_testcancel(), sigwait(__VA_ARGS__)) +#define sigwaitinfo(...) (pthread_testcancel(), sigwaitinfo(__VA_ARGS__)) +#define sleep(...) (pthread_testcancel(), sleep(__VA_ARGS__)) +//#define Sleep(...) (pthread_testcancel(), Sleep(__VA_ARGS__)) +#define system(...) (pthread_testcancel(), system(__VA_ARGS__)) +#define access(...) (pthread_testcancel(), access(__VA_ARGS__)) +#define asctime(...) (pthread_testcancel(), asctime(__VA_ARGS__)) +#define catclose(...) (pthread_testcancel(), catclose(__VA_ARGS__)) +#define catgets(...) (pthread_testcancel(), catgets(__VA_ARGS__)) +#define catopen(...) (pthread_testcancel(), catopen(__VA_ARGS__)) +#define closedir(...) (pthread_testcancel(), closedir(__VA_ARGS__)) +#define closelog(...) (pthread_testcancel(), closelog(__VA_ARGS__)) +#define ctermid(...) (pthread_testcancel(), ctermid(__VA_ARGS__)) +#define ctime(...) (pthread_testcancel(), ctime(__VA_ARGS__)) +#define dbm_close(...) (pthread_testcancel(), dbm_close(__VA_ARGS__)) +#define dbm_delete(...) (pthread_testcancel(), dbm_delete(__VA_ARGS__)) +#define dbm_fetch(...) (pthread_testcancel(), dbm_fetch(__VA_ARGS__)) +#define dbm_nextkey(...) (pthread_testcancel(), dbm_nextkey(__VA_ARGS__)) +#define dbm_open(...) (pthread_testcancel(), dbm_open(__VA_ARGS__)) +#define dbm_store(...) (pthread_testcancel(), dbm_store(__VA_ARGS__)) +#define dlclose(...) (pthread_testcancel(), dlclose(__VA_ARGS__)) +#define dlopen(...) (pthread_testcancel(), dlopen(__VA_ARGS__)) +#define endgrent(...) (pthread_testcancel(), endgrent(__VA_ARGS__)) +#define endhostent(...) (pthread_testcancel(), endhostent(__VA_ARGS__)) +#define endnetent(...) (pthread_testcancel(), endnetent(__VA_ARGS__)) +#define endprotoent(...) (pthread_testcancel(), endprotoend(__VA_ARGS__)) +#define endpwent(...) (pthread_testcancel(), endpwent(__VA_ARGS__)) +#define endservent(...) (pthread_testcancel(), endservent(__VA_ARGS__)) +#define endutxent(...) (pthread_testcancel(), endutxent(__VA_ARGS__)) +#define fclose(...) (pthread_testcancel(), fclose(__VA_ARGS__)) +#define fflush(...) (pthread_testcancel(), fflush(__VA_ARGS__)) +#define fgetc(...) (pthread_testcancel(), fgetc(__VA_ARGS__)) +#define fgetpos(...) (pthread_testcancel(), fgetpos(__VA_ARGS__)) +#define fgets(...) (pthread_testcancel(), fgets(__VA_ARGS__)) +#define fgetwc(...) (pthread_testcancel(), fgetwc(__VA_ARGS__)) +#define fgetws(...) (pthread_testcancel(), fgetws(__VA_ARGS__)) +#define fmtmsg(...) (pthread_testcancel(), fmtmsg(__VA_ARGS__)) +#define fopen(...) (pthread_testcancel(), fopen(__VA_ARGS__)) +#define fpathconf(...) (pthread_testcancel(), fpathconf(__VA_ARGS__)) +#define fprintf(...) (pthread_testcancel(), fprintf(__VA_ARGS__)) +#define fputc(...) (pthread_testcancel(), fputc(__VA_ARGS__)) +#define fputs(...) (pthread_testcancel(), fputs(__VA_ARGS__)) +#define fputwc(...) (pthread_testcancel(), fputwc(__VA_ARGS__)) +#define fputws(...) (pthread_testcancel(), fputws(__VA_ARGS__)) +#define fread(...) (pthread_testcancel(), fread(__VA_ARGS__)) +#define freopen(...) (pthread_testcancel(), freopen(__VA_ARGS__)) +#define fscanf(...) (pthread_testcancel(), fscanf(__VA_ARGS__)) +#define fseek(...) (pthread_testcancel(), fseek(__VA_ARGS__)) +#define fseeko(...) (pthread_testcancel(), fseeko(__VA_ARGS__)) +#define fsetpos(...) (pthread_testcancel(), fsetpos(__VA_ARGS__)) +#define fstat(...) (pthread_testcancel(), fstat(__VA_ARGS__)) +#define ftell(...) (pthread_testcancel(), ftell(__VA_ARGS__)) +#define ftello(...) (pthread_testcancel(), ftello(__VA_ARGS__)) +#define ftw(...) (pthread_testcancel(), ftw(__VA_ARGS__)) +#define fwprintf(...) (pthread_testcancel(), fwprintf(__VA_ARGS__)) +#define fwrite(...) (pthread_testcancel(), fwrite(__VA_ARGS__)) +#define fwscanf(...) (pthread_testcancel(), fwscanf(__VA_ARGS__)) +#define getaddrinfo(...) (pthread_testcancel(), getaddrinfo(__VA_ARGS__)) +#define getc(...) (pthread_testcancel(), getc(__VA_ARGS__)) +#define getc_unlocked(...) (pthread_testcancel(), getc_unlocked(__VA_ARGS__)) +#define getchar(...) (pthread_testcancel(), getchar(__VA_ARGS__)) +#define getchar_unlocked(...) (pthread_testcancel(), getchar_unlocked(__VA_ARGS__)) +#define getcwd(...) (pthread_testcancel(), getcwd(__VA_ARGS__)) +#define getdate(...) (pthread_testcancel(), getdate(__VA_ARGS__)) +#define getgrent(...) (pthread_testcancel(), getgrent(__VA_ARGS__)) +#define getgrgid(...) (pthread_testcancel(), getgrgid(__VA_ARGS__)) +#define getgrgid_r(...) (pthread_testcancel(), getgrgid_r(__VA_ARGS__)) +#define gergrnam(...) (pthread_testcancel(), getgrnam(__VA_ARGS__)) +#define getgrnam_r(...) (pthread_testcancel(), getgrnam_r(__VA_ARGS__)) +#define gethostbyaddr(...) (pthread_testcancel(), gethostbyaddr(__VA_ARGS__)) +#define gethostbyname(...) (pthread_testcancel(), gethostbyname(__VA_ARGS__)) +#define gethostent(...) (pthread_testcancel(), gethostent(__VA_ARGS__)) +#define gethostid(...) (pthread_testcancel(), gethostid(__VA_ARGS__)) +#define gethostname(...) (pthread_testcancel(), gethostname(__VA_ARGS__)) +#define getlogin(...) (pthread_testcancel(), getlogin(__VA_ARGS__)) +#define getlogin_r(...) (pthread_testcancel(), getlogin_r(__VA_ARGS__)) +#define getnameinfo(...) (pthread_testcancel(), getnameinfo(__VA_ARGS__)) +#define getnetbyaddr(...) (pthread_testcancel(), getnetbyaddr(__VA_ARGS__)) +#define getnetbyname(...) (pthread_testcancel(), getnetbyname(__VA_ARGS__)) +#define getnetent(...) (pthread_testcancel(), getnetent(__VA_ARGS__)) +#define getopt(...) (pthread_testcancel(), getopt(__VA_ARGS__)) +#define getprotobyname(...) (pthread_testcancel(), getprotobyname(__VA_ARGS__)) +#define getprotobynumber(...) (pthread_testcancel(), getprotobynumber(__VA_ARGS__)) +#define getprotoent(...) (pthread_testcancel(), getprotoent(__VA_ARGS__)) +#define getpwent(...) (pthread_testcancel(), getpwent(__VA_ARGS__)) +#define getpwnam(...) (pthread_testcancel(), getpwnam(__VA_ARGS__)) +#define getpwnam_r(...) (pthread_testcancel(), getpwnam_r(__VA_ARGS__)) +#define getpwuid(...) (pthread_testcancel(), getpwuid(__VA_ARGS__)) +#define getpwuid_r(...) (pthread_testcancel(), getpwuid_r(__VA_ARGS__)) +#define gets(...) (pthread_testcancel(), gets(__VA_ARGS__)) +#define getservbyname(...) (pthread_testcancel(), getservbyname(__VA_ARGS__)) +#define getservbyport(...) (pthread_testcancel(), getservbyport(__VA_ARGS__)) +#define getservent(...) (pthread_testcancel(), getservent(__VA_ARGS__)) +#define getutxent(...) (pthread_testcancel(), getutxent(__VA_ARGS__)) +#define getutxid(...) (pthread_testcancel(), getutxid(__VA_ARGS__)) +#define getutxline(...) (pthread_testcancel(), getutxline(__VA_ARGS__)) +#undef getwc +#define getwc(...) (pthread_testcancel(), getwc(__VA_ARGS__)) +#undef getwchar +#define getwchar(...) (pthread_testcancel(), getwchar(__VA_ARGS__)) +#define getwd(...) (pthread_testcancel(), getwd(__VA_ARGS__)) +#define glob(...) (pthread_testcancel(), glob(__VA_ARGS__)) +#define iconv_close(...) (pthread_testcancel(), iconv_close(__VA_ARGS__)) +#define iconv_open(...) (pthread_testcancel(), iconv_open(__VA_ARGS__)) +#define ioctl(...) (pthread_testcancel(), ioctl(__VA_ARGS__)) +#define link(...) (pthread_testcancel(), link(__VA_ARGS__)) +#define localtime(...) (pthread_testcancel(), localtime(__VA_ARGS__)) +#define lseek(...) (pthread_testcancel(), lseek(__VA_ARGS__)) +#define lstat(...) (pthread_testcancel(), lstat(__VA_ARGS__)) +#define mkstemp(...) (pthread_testcancel(), mkstemp(__VA_ARGS__)) +#define nftw(...) (pthread_testcancel(), nftw(__VA_ARGS__)) +#define opendir(...) (pthread_testcancel(), opendir(__VA_ARGS__)) +#define openlog(...) (pthread_testcancel(), openlog(__VA_ARGS__)) +#define pathconf(...) (pthread_testcancel(), pathconf(__VA_ARGS__)) +#define pclose(...) (pthread_testcancel(), pclose(__VA_ARGS__)) +#define perror(...) (pthread_testcancel(), perror(__VA_ARGS__)) +#define popen(...) (pthread_testcancel(), popen(__VA_ARGS__)) +#define posix_fadvise(...) (pthread_testcancel(), posix_fadvise(__VA_ARGS__)) +#define posix_fallocate(...) (pthread_testcancel(), posix_fallocate(__VA_ARGS__)) +#define posix_madvise(...) (pthread_testcancel(), posix_madvise(__VA_ARGS__)) +#define posix_openpt(...) (pthread_testcancel(), posix_openpt(__VA_ARGS__)) +#define posix_spawn(...) (pthread_testcancel(), posix_spawn(__VA_ARGS__)) +#define posix_spawnp(...) (pthread_testcancel(), posix_spawnp(__VA_ARGS__)) +#define posix_trace_clear(...) (pthread_testcancel(), posix_trace_clear(__VA_ARGS__)) +#define posix_trace_close(...) (pthread_testcancel(), posix_trace_close(__VA_ARGS__)) +#define posix_trace_create(...) (pthread_testcancel(), posix_trace_create(__VA_ARGS__)) +#define posix_trace_create_withlog(...) (pthread_testcancel(), posix_trace_create_withlog(__VA_ARGS__)) +#define posix_trace_eventtypelist_getne(...) (pthread_testcancel(), posix_trace_eventtypelist_getne(__VA_ARGS__)) +#define posix_trace_eventtypelist_rewin(...) (pthread_testcancel(), posix_trace_eventtypelist_rewin(__VA_ARGS__)) +#define posix_trace_flush(...) (pthread_testcancel(), posix_trace_flush(__VA_ARGS__)) +#define posix_trace_get_attr(...) (pthread_testcancel(), posix_trace_get_attr(__VA_ARGS__)) +#define posix_trace_get_filter(...) (pthread_testcancel(), posix_trace_get_filter(__VA_ARGS__)) +#define posix_trace_get_status(...) (pthread_testcancel(), posix_trace_get_status(__VA_ARGS__)) +#define posix_trace_getnext_event(...) (pthread_testcancel(), posix_trace_getnext_event(__VA_ARGS__)) +#define posix_trace_open(...) (pthread_testcancel(), posix_trace_open(__VA_ARGS__)) +#define posix_trace_rewind(...) (pthread_testcancel(), posix_trace_rewind(__VA_ARGS__)) +#define posix_trace_setfilter(...) (pthread_testcancel(), posix_trace_setfilter(__VA_ARGS__)) +#define posix_trace_shutdown(...) (pthread_testcancel(), posix_trace_shutdown(__VA_ARGS__)) +#define posix_trace_timedgetnext_event(...) (pthread_testcancel(), posix_trace_timedgetnext_event(__VA_ARGS__)) +#define posix_typed_mem_open(...) (pthread_testcancel(), posix_typed_mem_open(__VA_ARGS__)) +#define printf(...) (pthread_testcancel(), printf(__VA_ARGS__)) +#define putc(...) (pthread_testcancel(), putc(__VA_ARGS__)) +#define putc_unlocked(...) (pthread_testcancel(), putc_unlocked(__VA_ARGS__)) +#define putchar(...) (pthread_testcancel(), putchar(__VA_ARGS__)) +#define putchar_unlocked(...) (pthread_testcancel(), putchar_unlocked(__VA_ARGS__)) +#define puts(...) (pthread_testcancel(), puts(__VA_ARGS__)) +#define pututxline(...) (pthread_testcancel(), pututxline(__VA_ARGS__)) +#undef putwc +#define putwc(...) (pthread_testcancel(), putwc(__VA_ARGS__)) +#undef putwchar +#define putwchar(...) (pthread_testcancel(), putwchar(__VA_ARGS__)) +#define readdir(...) (pthread_testcancel(), readdir(__VA_ARSG__)) +#define readdir_r(...) (pthread_testcancel(), readdir_r(__VA_ARGS__)) +#define remove(...) (pthread_testcancel(), remove(__VA_ARGS__)) +#define rename(...) (pthread_testcancel(), rename(__VA_ARGS__)) +#define rewind(...) (pthread_testcancel(), rewind(__VA_ARGS__)) +#define rewinddir(...) (pthread_testcancel(), rewinddir(__VA_ARGS__)) +#define scanf(...) (pthread_testcancel(), scanf(__VA_ARGS__)) +#define seekdir(...) (pthread_testcancel(), seekdir(__VA_ARGS__)) +#define semop(...) (pthread_testcancel(), semop(__VA_ARGS__)) +#define setgrent(...) (pthread_testcancel(), setgrent(__VA_ARGS__)) +#define sethostent(...) (pthread_testcancel(), sethostemt(__VA_ARGS__)) +#define setnetent(...) (pthread_testcancel(), setnetent(__VA_ARGS__)) +#define setprotoent(...) (pthread_testcancel(), setprotoent(__VA_ARGS__)) +#define setpwent(...) (pthread_testcancel(), setpwent(__VA_ARGS__)) +#define setservent(...) (pthread_testcancel(), setservent(__VA_ARGS__)) +#define setutxent(...) (pthread_testcancel(), setutxent(__VA_ARGS__)) +#define stat(...) (pthread_testcancel(), stat(__VA_ARGS__)) +#define strerror(...) (pthread_testcancel(), strerror(__VA_ARGS__)) +#define strerror_r(...) (pthread_testcancel(), strerror_r(__VA_ARGS__)) +#define strftime(...) (pthread_testcancel(), strftime(__VA_ARGS__)) +#define symlink(...) (pthread_testcancel(), symlink(__VA_ARGS__)) +#define sync(...) (pthread_testcancel(), sync(__VA_ARGS__)) +#define syslog(...) (pthread_testcancel(), syslog(__VA_ARGS__)) +#define tmpfile(...) (pthread_testcancel(), tmpfile(__VA_ARGS__)) +#define tmpnam(...) (pthread_testcancel(), tmpnam(__VA_ARGS__)) +#define ttyname(...) (pthread_testcancel(), ttyname(__VA_ARGS__)) +#define ttyname_r(...) (pthread_testcancel(), ttyname_r(__VA_ARGS__)) +#define tzset(...) (pthread_testcancel(), tzset(__VA_ARGS__)) +#define ungetc(...) (pthread_testcancel(), ungetc(__VA_ARGS__)) +#define ungetwc(...) (pthread_testcancel(), ungetwc(__VA_ARGS__)) +#define unlink(...) (pthread_testcancel(), unlink(__VA_ARGS__)) +#define vfprintf(...) (pthread_testcancel(), vfprintf(__VA_ARGS__)) +#define vfwprintf(...) (pthread_testcancel(), vfwprintf(__VA_ARGS__)) +#define vprintf(...) (pthread_testcancel(), vprintf(__VA_ARGS__)) +#define vwprintf(...) (pthread_testcancel(), vwprintf(__VA_ARGS__)) +#define wcsftime(...) (pthread_testcancel(), wcsftime(__VA_ARGS__)) +#define wordexp(...) (pthread_testcancel(), wordexp(__VA_ARGS__)) +#define wprintf(...) (pthread_testcancel(), wprintf(__VA_ARGS__)) +#define wscanf(...) (pthread_testcancel(), wscanf(__VA_ARGS__)) +#endif + +/* We deal here with a gcc issue for posix threading on Windows. + We would need to change here gcc's gthr-posix.h header, but this + got rejected. So we deal it within this header. */ +#ifdef _GTHREAD_USE_MUTEX_INIT_FUNC +#undef _GTHREAD_USE_MUTEX_INIT_FUNC +#endif +#define _GTHREAD_USE_MUTEX_INIT_FUNC 1 + +#ifdef __cplusplus +} +#endif + +#endif /* WIN_PTHREADS_H */ diff --git a/vendor/winpthreads/include/pthread_compat.h b/vendor/winpthreads/include/pthread_compat.h new file mode 100644 index 00000000..81600559 --- /dev/null +++ b/vendor/winpthreads/include/pthread_compat.h @@ -0,0 +1,204 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/* + * Parts of this library are derived by: + * + * Posix Threads library for Microsoft Windows + * + * Use at own risk, there is no implied warranty to this code. + * It uses undocumented features of Microsoft Windows that can change + * at any time in the future. + * + * (C) 2010 Lockless Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Lockless Inc. nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AN + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WIN_PTHREADS_PTHREAD_COMPAT_H +#define WIN_PTHREADS_PTHREAD_COMPAT_H + +#if defined(__cplusplus) && __cplusplus >= 201103L +#define WINPTHREADS_STATIC_ASSERT(expr, msg) static_assert ((expr), msg) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L +#define WINPTHREADS_STATIC_ASSERT(expr, msg) static_assert ((expr), msg) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +#define WINPTHREADS_STATIC_ASSERT(expr, msg) _Static_assert ((expr), msg) +#else +#define WINPTHREADS_STATIC_ASSERT(expr, msg) extern int winpthreads_static_assert[((expr) ? 1 : -1)] +#endif + +#if defined(_USE_32BIT_TIME_T) +#define WINPTHREADS_TIME_BITS 32 +#else +#define WINPTHREADS_TIME_BITS 64 +#endif + +#ifndef WINPTHREAD_API + /** + * Allow `WINPTHREAD_STATIC` to override `WINPTHREADS_USE_DLLIMPORT`. + */ +# if defined(WINPTHREADS_USE_DLLIMPORT) && !defined(WINPTHREAD_STATIC) +# define WINPTHREAD_API __declspec(dllimport) +# else +# define WINPTHREAD_API +# endif +#endif + +#ifndef __clockid_t_defined +typedef int clockid_t; +#define __clockid_t_defined 1 +#endif /* __clockid_t_defined */ + +#ifndef _MODE_T_ +#define _MODE_T_ +typedef unsigned short mode_t; +#endif + +/* Error-codes. */ +#ifndef ETIMEDOUT +#define ETIMEDOUT 138 +#endif +#ifndef ENOTSUP +#define ENOTSUP 129 +#endif +#ifndef EWOULDBLOCK +#define EWOULDBLOCK 140 +#endif + +#ifdef __GNUC__ + +#define WINPTHREADS_INLINE __inline__ +#define WINPTHREADS_ALWAYS_INLINE __inline__ __attribute__((__always_inline__)) +#define WINPTHREADS_ATTRIBUTE(X) __attribute__(X) +#define WINPTHREADS_SECTION(X) __section__(X) + +#elif _MSC_VER + +/** + * If package which includes this header file is using autoconf and calls + * AC_TYPE_PID_T macro, the check for pid_t will fail and it will define pid_t + * as a macro in config.h - this eventually results in compilation error. + * + * Luckily, it defines it to the same base type, so we can simply undefine it. + */ +#ifdef _WIN64 +#ifdef pid_t +WINPTHREADS_STATIC_ASSERT (sizeof (pid_t) == sizeof(__int64), "pid_t is defined as a macro with mismatching base type"); +#undef pid_t +#endif +typedef __int64 pid_t; +#else +#ifdef pid_t +WINPTHREADS_STATIC_ASSERT (sizeof (pid_t) == sizeof(int), "pid_t is defined as a macro with mismatching base type"); +#undef pid_t +#endif +typedef int pid_t; +#endif + +#define WINPTHREADS_INLINE __inline +#define WINPTHREADS_ALWAYS_INLINE __inline __forceinline +#define WINPTHREADS_ATTRIBUTE(X) __declspec X +#define WINPTHREADS_SECTION(X) allocate(X) + +#endif + +#ifndef WINPTHREAD_CLOCK_DECL +# ifdef __cplusplus +# define WINPTHREAD_CLOCK_DECL WINPTHREADS_ALWAYS_INLINE +# else +# define WINPTHREAD_CLOCK_DECL static WINPTHREADS_ALWAYS_INLINE +# endif +#endif + +#ifndef WINPTHREAD_COND_DECL +# ifdef __cplusplus +# define WINPTHREAD_COND_DECL WINPTHREADS_ALWAYS_INLINE +# else +# define WINPTHREAD_COND_DECL static WINPTHREADS_ALWAYS_INLINE +# endif +#endif + +#ifndef WINPTHREAD_MUTEX_DECL +# ifdef __cplusplus +# define WINPTHREAD_MUTEX_DECL WINPTHREADS_ALWAYS_INLINE +# else +# define WINPTHREAD_MUTEX_DECL static WINPTHREADS_ALWAYS_INLINE +# endif +#endif + +#ifndef WINPTHREAD_NANOSLEEP_DECL +# ifdef __cplusplus +# define WINPTHREAD_NANOSLEEP_DECL WINPTHREADS_ALWAYS_INLINE +# else +# define WINPTHREAD_NANOSLEEP_DECL static WINPTHREADS_ALWAYS_INLINE +# endif +#endif + +#ifndef WINPTHREAD_RWLOCK_DECL +# ifdef __cplusplus +# define WINPTHREAD_RWLOCK_DECL WINPTHREADS_ALWAYS_INLINE +# else +# define WINPTHREAD_RWLOCK_DECL static WINPTHREADS_ALWAYS_INLINE +# endif +#endif + +#ifndef WINPTHREAD_SEM_DECL +# ifdef __cplusplus +# define WINPTHREAD_SEM_DECL WINPTHREADS_ALWAYS_INLINE +# else +# define WINPTHREAD_SEM_DECL static WINPTHREADS_ALWAYS_INLINE +# endif +#endif + +#ifndef WINPTHREAD_THREAD_DECL +# ifdef __cplusplus +# define WINPTHREAD_THREAD_DECL WINPTHREADS_ALWAYS_INLINE +# else +# define WINPTHREAD_THREAD_DECL static WINPTHREADS_ALWAYS_INLINE +# endif +#endif + +#endif diff --git a/vendor/winpthreads/include/pthread_signal.h b/vendor/winpthreads/include/pthread_signal.h new file mode 100644 index 00000000..42a50ae0 --- /dev/null +++ b/vendor/winpthreads/include/pthread_signal.h @@ -0,0 +1,29 @@ +/* + Copyright (c) 2013-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifndef WIN_PTHREADS_SIGNAL_H +#define WIN_PTHREADS_SIGNAL_H + +/* Windows has rudimentary signals support. */ +#define pthread_sigmask(H, S1, S2) 0 + +#endif /* WIN_PTHREADS_SIGNAL_H */ diff --git a/vendor/winpthreads/include/pthread_time.h b/vendor/winpthreads/include/pthread_time.h new file mode 100644 index 00000000..27f7566f --- /dev/null +++ b/vendor/winpthreads/include/pthread_time.h @@ -0,0 +1,136 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifndef WIN_PTHREADS_TIME_H +#define WIN_PTHREADS_TIME_H + +#include +#include "pthread_compat.h" + +/* Posix timers are supported */ +#ifndef _POSIX_TIMERS +#define _POSIX_TIMERS 200809L +#endif + +/* Monotonic clocks are available. */ +#ifndef _POSIX_MONOTONIC_CLOCK +#define _POSIX_MONOTONIC_CLOCK 200809L +#endif + +/* CPU-time clocks are available. */ +#ifndef _POSIX_CPUTIME +#define _POSIX_CPUTIME 200809L +#endif + +/* Clock support in threads are available. */ +#ifndef _POSIX_THREAD_CPUTIME +#define _POSIX_THREAD_CPUTIME 200809L +#endif + +#ifndef TIMER_ABSTIME +#define TIMER_ABSTIME 1 +#endif + +#ifndef CLOCK_REALTIME +#define CLOCK_REALTIME 0 +#endif + +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC 1 +#endif + +#ifndef CLOCK_PROCESS_CPUTIME_ID +#define CLOCK_PROCESS_CPUTIME_ID 2 +#endif + +#ifndef CLOCK_THREAD_CPUTIME_ID +#define CLOCK_THREAD_CPUTIME_ID 3 +#endif + +#ifndef CLOCK_REALTIME_COARSE +#define CLOCK_REALTIME_COARSE 4 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +WINPTHREAD_API int __cdecl nanosleep32(const struct _timespec32 *request, struct _timespec32 *remain); +WINPTHREAD_API int __cdecl nanosleep64(const struct _timespec64 *request, struct _timespec64 *remain); +WINPTHREAD_NANOSLEEP_DECL int __cdecl nanosleep(const struct timespec *request, struct timespec *remain) +{ +#if WINPTHREADS_TIME_BITS == 32 + return nanosleep32 ((struct _timespec32 *)request, (struct _timespec32 *)remain); +#else + return nanosleep64 ((struct _timespec64 *)request, (struct _timespec64 *)remain); +#endif +} + +WINPTHREAD_API int __cdecl clock_nanosleep32(clockid_t clock_id, int flags, const struct _timespec32 *request, struct _timespec32 *remain); +WINPTHREAD_API int __cdecl clock_nanosleep64(clockid_t clock_id, int flags, const struct _timespec64 *request, struct _timespec64 *remain); +WINPTHREAD_CLOCK_DECL int __cdecl clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *request, struct timespec *remain) +{ +#if WINPTHREADS_TIME_BITS == 32 + return clock_nanosleep32 (clock_id, flags, (struct _timespec32 *)request, (struct _timespec32 *)remain); +#else + return clock_nanosleep64 (clock_id, flags, (struct _timespec64 *)request, (struct _timespec64 *)remain); +#endif +} + +WINPTHREAD_API int __cdecl clock_getres32(clockid_t clock_id, struct _timespec32 *res); +WINPTHREAD_API int __cdecl clock_getres64(clockid_t clock_id, struct _timespec64 *res); +WINPTHREAD_CLOCK_DECL int __cdecl clock_getres(clockid_t clock_id, struct timespec *res) +{ +#if WINPTHREADS_TIME_BITS == 32 + return clock_getres32 (clock_id, (struct _timespec32 *)res); +#else + return clock_getres64 (clock_id, (struct _timespec64 *)res); +#endif +} + +WINPTHREAD_API int __cdecl clock_gettime32(clockid_t clock_id, struct _timespec32 *tp); +WINPTHREAD_API int __cdecl clock_gettime64(clockid_t clock_id, struct _timespec64 *tp); +WINPTHREAD_CLOCK_DECL int __cdecl clock_gettime(clockid_t clock_id, struct timespec *tp) +{ +#if WINPTHREADS_TIME_BITS == 32 + return clock_gettime32 (clock_id, (struct _timespec32 *)tp); +#else + return clock_gettime64 (clock_id, (struct _timespec64 *)tp); +#endif +} + +WINPTHREAD_API int __cdecl clock_settime32(clockid_t clock_id, const struct _timespec32 *tp); +WINPTHREAD_API int __cdecl clock_settime64(clockid_t clock_id, const struct _timespec64 *tp); +WINPTHREAD_CLOCK_DECL int __cdecl clock_settime(clockid_t clock_id, const struct timespec *tp) +{ +#if WINPTHREADS_TIME_BITS == 32 + return clock_settime32 (clock_id, (struct _timespec32 *)tp); +#else + return clock_settime64 (clock_id, (struct _timespec64 *)tp); +#endif +} + +#ifdef __cplusplus +} +#endif + +#endif /* WIN_PTHREADS_TIME_H */ diff --git a/vendor/winpthreads/include/pthread_unistd.h b/vendor/winpthreads/include/pthread_unistd.h new file mode 100644 index 00000000..64693534 --- /dev/null +++ b/vendor/winpthreads/include/pthread_unistd.h @@ -0,0 +1,192 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifndef WIN_PTHREADS_UNISTD_H +#define WIN_PTHREADS_UNISTD_H + +/* Set defines described by the POSIX Threads Extension (1003.1c-1995) */ +/* _SC_THREADS + Basic support for POSIX threads is available. The functions + + pthread_atfork(), + pthread_attr_destroy(), + pthread_attr_getdetachstate(), + pthread_attr_getschedparam(), + pthread_attr_init(), + pthread_attr_setdetachstate(), + pthread_attr_setschedparam(), + pthread_cancel(), + pthread_cleanup_push(), + pthread_cleanup_pop(), + pthread_cond_broadcast(), + pthread_cond_destroy(), + pthread_cond_init(), + pthread_cond_signal(), + pthread_cond_timedwait(), + pthread_cond_wait(), + pthread_condattr_destroy(), + pthread_condattr_init(), + pthread_create(), + pthread_detach(), + pthread_equal(), + pthread_exit(), + pthread_getspecific(), + pthread_join(, + pthread_key_create(), + pthread_key_delete(), + pthread_mutex_destroy(), + pthread_mutex_init(), + pthread_mutex_lock(), + pthread_mutex_trylock(), + pthread_mutex_unlock(), + pthread_mutexattr_destroy(), + pthread_mutexattr_init(), + pthread_once(), + pthread_rwlock_destroy(), + pthread_rwlock_init(), + pthread_rwlock_rdlock(), + pthread_rwlock_tryrdlock(), + pthread_rwlock_trywrlock(), + pthread_rwlock_unlock(), + pthread_rwlock_wrlock(), + pthread_rwlockattr_destroy(), + pthread_rwlockattr_init(), + pthread_self(), + pthread_setcancelstate(), + pthread_setcanceltype(), + pthread_setspecific(), + pthread_testcancel() + + are present. */ +#undef _POSIX_THREADS +#define _POSIX_THREADS 200112L + +/* _SC_READER_WRITER_LOCKS + This option implies the _POSIX_THREADS option. Conversely, under + POSIX 1003.1-2001 the _POSIX_THREADS option implies this option. + + The functions + pthread_rwlock_destroy(), + pthread_rwlock_init(), + pthread_rwlock_rdlock(), + pthread_rwlock_tryrdlock(), + pthread_rwlock_trywrlock(), + pthread_rwlock_unlock(), + pthread_rwlock_wrlock(), + pthread_rwlockattr_destroy(), + pthread_rwlockattr_init() + + are present. +*/ +#undef _POSIX_READER_WRITER_LOCKS +#define _POSIX_READER_WRITER_LOCKS 200112L + +/* _SC_SPIN_LOCKS + This option implies the _POSIX_THREADS and _POSIX_THREAD_SAFE_FUNCTIONS + options. The functions + + pthread_spin_destroy(), + pthread_spin_init(), + pthread_spin_lock(), + pthread_spin_trylock(), + pthread_spin_unlock() + + are present. */ +#undef _POSIX_SPIN_LOCKS +#define _POSIX_SPIN_LOCKS 200112L + +/* _SC_BARRIERS + This option implies the _POSIX_THREADS and _POSIX_THREAD_SAFE_FUNCTIONS + options. The functions + + pthread_barrier_destroy(), + pthread_barrier_init(), + pthread_barrier_wait(), + pthread_barrierattr_destroy(), + pthread_barrierattr_init() + + are present. +*/ +#undef _POSIX_BARRIERS +#define _POSIX_BARRIERS 200112L + +/* _SC_TIMEOUTS + The functions + + mq_timedreceive(), - not supported + mq_timedsend(), - not supported + posix_trace_timedgetnext_event(), - not supported + pthread_mutex_timedlock(), + pthread_rwlock_timedrdlock(), + pthread_rwlock_timedwrlock(), + sem_timedwait(), + + are present. */ +#undef _POSIX_TIMEOUTS +#define _POSIX_TIMEOUTS 200112L + +/* _SC_TIMERS - not supported + The functions + + clock_getres(), + clock_gettime(), + clock_settime(), + nanosleep(), + timer_create(), + timer_delete(), + timer_gettime(), + timer_getoverrun(), + timer_settime() + + are present. */ +/* #undef _POSIX_TIMERS */ + +/* _SC_CLOCK_SELECTION + This option implies the _POSIX_TIMERS option. The functions + + pthread_condattr_getclock(), + pthread_condattr_setclock(), + clock_nanosleep() + + are present. +*/ +#undef _POSIX_CLOCK_SELECTION +#define _POSIX_CLOCK_SELECTION 200112 + +/* _SC_SEMAPHORES + The include file is present. The functions + + sem_close(), + sem_destroy(), + sem_getvalue(), + sem_init(), + sem_open(), + sem_post(), + sem_trywait(), + sem_unlink(), + sem_wait() + + are present. */ +#undef _POSIX_SEMAPHORES +#define _POSIX_SEMAPHORES 200112 + +#endif /* WIN_PTHREADS_UNISTD_H */ diff --git a/vendor/winpthreads/include/sched.h b/vendor/winpthreads/include/sched.h new file mode 100644 index 00000000..abbf5cf0 --- /dev/null +++ b/vendor/winpthreads/include/sched.h @@ -0,0 +1,65 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ +#ifndef WIN_PTHREADS_SCHED_H +#define WIN_PTHREADS_SCHED_H + +#include +#include +#include + +#include +#include +#include + +#include "pthread_compat.h" + +/* Some POSIX realtime extensions, mostly stubbed */ +#define SCHED_OTHER 0 +#define SCHED_FIFO 1 +#define SCHED_RR 2 +#define SCHED_MIN SCHED_OTHER +#define SCHED_MAX SCHED_RR + +struct sched_param { + int sched_priority; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +WINPTHREAD_API int sched_yield(void); +WINPTHREAD_API int sched_get_priority_min(int pol); +WINPTHREAD_API int sched_get_priority_max(int pol); +WINPTHREAD_API int sched_getscheduler(pid_t pid); +WINPTHREAD_API int sched_setscheduler(pid_t pid, int pol, const struct sched_param *param); + +#ifdef __cplusplus +} +#endif + +#ifndef sched_rr_get_interval +#define sched_rr_get_interval(_p, _i) \ + ( errno = ENOTSUP, (int) -1 ) +#endif + +#endif /* WIN_PTHREADS_SCHED_H */ diff --git a/vendor/winpthreads/include/semaphore.h b/vendor/winpthreads/include/semaphore.h new file mode 100644 index 00000000..9a6c211a --- /dev/null +++ b/vendor/winpthreads/include/semaphore.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifndef WIN_PTHREADS_SEMAPHORE_H +#define WIN_PTHREADS_SEMAPHORE_H + +#include +#include "pthread_compat.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SEM_VALUE_MAX INT_MAX + +typedef void *sem_t; + +#define SEM_FAILED NULL + +WINPTHREAD_API int sem_init(sem_t * sem, int pshared, unsigned int value); + +WINPTHREAD_API int sem_destroy(sem_t *sem); + +WINPTHREAD_API int sem_trywait(sem_t *sem); + +WINPTHREAD_API int sem_wait(sem_t *sem); + +WINPTHREAD_API int sem_timedwait32(sem_t * sem, const struct _timespec32 *t); +WINPTHREAD_API int sem_timedwait64(sem_t * sem, const struct _timespec64 *t); +WINPTHREAD_SEM_DECL int sem_timedwait(sem_t * sem, const struct timespec *t) +{ +#if WINPTHREADS_TIME_BITS == 32 + return sem_timedwait32 (sem, (const struct _timespec32 *) t); +#else + return sem_timedwait64 (sem, (const struct _timespec64 *) t); +#endif +} + +WINPTHREAD_API int sem_post(sem_t *sem); + +WINPTHREAD_API int sem_post_multiple(sem_t *sem, int count); + +/* yes, it returns a semaphore (or SEM_FAILED) */ +WINPTHREAD_API sem_t * sem_open(const char * name, int oflag, mode_t mode, unsigned int value); + +WINPTHREAD_API int sem_close(sem_t * sem); + +WINPTHREAD_API int sem_unlink(const char * name); + +WINPTHREAD_API int sem_getvalue(sem_t * sem, int * sval); + +#ifdef __cplusplus +} +#endif + +#endif /* WIN_PTHREADS_SEMAPHORE_H */ diff --git a/vendor/winpthreads/src/barrier.c b/vendor/winpthreads/src/barrier.c new file mode 100644 index 00000000..b1be5126 --- /dev/null +++ b/vendor/winpthreads/src/barrier.c @@ -0,0 +1,254 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include + +/* public header files */ +#include "pthread.h" +#include "semaphore.h" +/* internal header files */ +#include "barrier.h" +#include "misc.h" + +static pthread_spinlock_t barrier_global = PTHREAD_SPINLOCK_INITIALIZER; + +static WINPTHREADS_ATTRIBUTE((noinline)) int +barrier_unref(volatile pthread_barrier_t *barrier, int res) +{ + pthread_spin_lock(&barrier_global); + assert((((barrier_t *)*barrier)->valid == LIFE_BARRIER) && (((barrier_t *)*barrier)->busy > 0)); + ((barrier_t *)*barrier)->busy -= 1; + pthread_spin_unlock(&barrier_global); + return res; +} + +static WINPTHREADS_ATTRIBUTE((noinline)) int barrier_ref(volatile pthread_barrier_t *barrier) +{ + int r = 0; + pthread_spin_lock(&barrier_global); + + if (!barrier || !*barrier || ((barrier_t *)*barrier)->valid != LIFE_BARRIER) r = EINVAL; + else { + ((barrier_t *)*barrier)->busy += 1; + } + + pthread_spin_unlock(&barrier_global); + + return r; +} + +static WINPTHREADS_ATTRIBUTE((noinline)) int +barrier_ref_destroy(volatile pthread_barrier_t *barrier, pthread_barrier_t *bDestroy) +{ + int r = 0; + + *bDestroy = NULL; + pthread_spin_lock(&barrier_global); + + if (!barrier || !*barrier || ((barrier_t *)*barrier)->valid != LIFE_BARRIER) r = EINVAL; + else { + barrier_t *b_ = (barrier_t *)*barrier; + if (b_->busy) r = EBUSY; + else { + *bDestroy = *barrier; + *barrier = NULL; + } + } + + pthread_spin_unlock(&barrier_global); + return r; +} + +static WINPTHREADS_ATTRIBUTE((noinline)) void +barrier_ref_set (volatile pthread_barrier_t *barrier, void *v) +{ + pthread_spin_lock(&barrier_global); + *barrier = v; + pthread_spin_unlock(&barrier_global); +} + +int pthread_barrier_destroy(pthread_barrier_t *b_) +{ + pthread_barrier_t bDestroy; + barrier_t *b; + int r; + + while ((r = barrier_ref_destroy(b_,&bDestroy)) == EBUSY) + Sleep(0); + + if (r) + return r; + + b = (barrier_t *)bDestroy; + + pthread_mutex_lock(&b->m); + + if (sem_destroy(&b->sems[0]) != 0) + { + /* Could this happen? */ + *b_ = bDestroy; + pthread_mutex_unlock (&b->m); + return EBUSY; + } + if (sem_destroy(&b->sems[1]) != 0) + { + sem_init (&b->sems[0], b->share, 0); + *b_ = bDestroy; + pthread_mutex_unlock (&b->m); + return -1; + } + pthread_mutex_unlock(&b->m); + if(pthread_mutex_destroy(&b->m) != 0) { + sem_init (&b->sems[0], b->share, 0); + sem_init (&b->sems[1], b->share, 0); + *b_ = bDestroy; + return -1; + } + b->valid = DEAD_BARRIER; + free(bDestroy); + return 0; + +} + +int +pthread_barrier_init (pthread_barrier_t *b_, const void *attr, + unsigned int count) +{ + barrier_t *b; + + if (!count || !b_) + return EINVAL; + + if ((b = (pthread_barrier_t)calloc(1,sizeof(*b))) == NULL) + return ENOMEM; + if (!attr || *((int **)attr) == NULL) + b->share = PTHREAD_PROCESS_PRIVATE; + else + memcpy (&b->share, *((void **) attr), sizeof (int)); + b->total = count; + b->count = count; + b->valid = LIFE_BARRIER; + b->sel = 0; + + if (pthread_mutex_init(&b->m, NULL) != 0) + { + free (b); + return ENOMEM; + } + + if (sem_init(&b->sems[0], b->share, 0) != 0) + { + pthread_mutex_destroy(&b->m); + free (b); + return ENOMEM; + } + if (sem_init(&b->sems[1], b->share, 0) != 0) + { + pthread_mutex_destroy(&b->m); + sem_destroy(&b->sems[0]); + free (b); + return ENOMEM; + } + barrier_ref_set (b_,b); + + return 0; +} + +int pthread_barrier_wait(pthread_barrier_t *b_) +{ + long sel; + int r, e, rslt; + barrier_t *b; + + r = barrier_ref(b_); + if(r) return r; + + b = (barrier_t *)*b_; + + if ((r = pthread_mutex_lock(&b->m)) != 0) return barrier_unref(b_,EINVAL); + sel = b->sel; + InterlockedDecrement((long*)&b->total); + if (b->total == 0) + { + b->total = b->count; + b->sel = (sel != 0 ? 0 : 1); + e = 1; + rslt = PTHREAD_BARRIER_SERIAL_THREAD; + r = (b->count > 1 ? sem_post_multiple (&b->sems[sel], b->count - 1) : 0); + } + else { e = 0; rslt= 0; } + pthread_mutex_unlock(&b->m); + if (!e) + r = sem_wait(&b->sems[sel]); + + if (!r) r = rslt; + return barrier_unref(b_,r); +} + +int pthread_barrierattr_init(void **attr) +{ + int *p; + + if ((p = (int *) calloc (1, sizeof (int))) == NULL) + return ENOMEM; + + *p = PTHREAD_PROCESS_PRIVATE; + *attr = p; + + return 0; +} + +int pthread_barrierattr_destroy(void **attr) +{ + void *p; + if (!attr || (p = *attr) == NULL) + return EINVAL; + *attr = NULL; + free (p); + return 0; +} + +int pthread_barrierattr_setpshared(void **attr, int s) +{ + if (!attr || *attr == NULL + || (s != PTHREAD_PROCESS_SHARED && s != PTHREAD_PROCESS_PRIVATE)) + return EINVAL; + memcpy (*attr, &s, sizeof (int)); + return 0; +} + +int pthread_barrierattr_getpshared(void **attr, int *s) +{ + if (!attr || !s || *attr == NULL) + return EINVAL; + memcpy (s, *attr, sizeof (int)); + return 0; +} diff --git a/vendor/winpthreads/src/barrier.h b/vendor/winpthreads/src/barrier.h new file mode 100644 index 00000000..1d1e83da --- /dev/null +++ b/vendor/winpthreads/src/barrier.h @@ -0,0 +1,50 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifndef WIN_PTHREADS_BARRIER_H +#define WIN_PTHREADS_BARRIER_H + +#define LIFE_BARRIER 0xBAB1FEED +#define DEAD_BARRIER 0xDEADB00F + +#define _PTHREAD_BARRIER_FLAG (1<<30) + +#define CHECK_BARRIER(b) \ + do { \ + if (!(b) || ( ((barrier_t *)(*b))->valid != (unsigned int)LIFE_BARRIER ) ) \ + return EINVAL; \ + } while (0) + +typedef struct barrier_t barrier_t; +struct barrier_t +{ + int valid; + int busy; + int count; + int total; + int share; + long sel; + pthread_mutex_t m; + sem_t sems[2]; +}; + +#endif diff --git a/vendor/winpthreads/src/clock.c b/vendor/winpthreads/src/clock.c new file mode 100644 index 00000000..922c5de5 --- /dev/null +++ b/vendor/winpthreads/src/clock.c @@ -0,0 +1,354 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the w64 mingw-runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include + +#define WINPTHREAD_CLOCK_DECL WINPTHREAD_API + +/* public header files */ +#include "pthread_time.h" +/* internal header files */ +#include "misc.h" + +#define POW10_7 10000000 +#define POW10_9 1000000000 + +/* Number of 100ns-seconds between the beginning of the Windows epoch + * (Jan. 1, 1601) and the Unix epoch (Jan. 1, 1970) + */ +#define DELTA_EPOCH_IN_100NS INT64_C(116444736000000000) + +static WINPTHREADS_INLINE int lc_set_errno(int result) +{ + if (result != 0) { + errno = result; + return -1; + } + return 0; +} + +/** + * Get the resolution of the specified clock clock_id and + * stores it in the struct timespec pointed to by res. + * @param clock_id The clock_id argument is the identifier of the particular + * clock on which to act. The following clocks are supported: + *
+ *     CLOCK_REALTIME  System-wide real-time clock. Setting this clock
+ *                 requires appropriate privileges.
+ *     CLOCK_MONOTONIC Clock that cannot be set and represents monotonic
+ *                 time since some unspecified starting point.
+ *     CLOCK_PROCESS_CPUTIME_ID High-resolution per-process timer from the CPU.
+ *     CLOCK_THREAD_CPUTIME_ID  Thread-specific CPU-time clock.
+ * 
+ * @param res The pointer to a timespec structure to receive the time + * resolution. + * @return If the function succeeds, the return value is 0. + * If the function fails, the return value is -1, + * with errno set to indicate the error. + */ +static int __clock_getres(clockid_t clock_id, struct _timespec64 *res) +{ + clockid_t id = clock_id; + + if (id == CLOCK_REALTIME && _pthread_get_system_time_best_as_file_time == GetSystemTimeAsFileTime) + id = CLOCK_REALTIME_COARSE; /* GetSystemTimePreciseAsFileTime() not available */ + + switch(id) { + case CLOCK_REALTIME: + case CLOCK_MONOTONIC: + { + LARGE_INTEGER pf; + + if (QueryPerformanceFrequency(&pf) == 0) + return lc_set_errno(EINVAL); + + res->tv_sec = 0; + res->tv_nsec = (int) ((POW10_9 + (pf.QuadPart >> 1)) / pf.QuadPart); + if (res->tv_nsec < 1) + res->tv_nsec = 1; + + return 0; + } + + case CLOCK_REALTIME_COARSE: + case CLOCK_PROCESS_CPUTIME_ID: + case CLOCK_THREAD_CPUTIME_ID: + { + DWORD timeAdjustment, timeIncrement; + BOOL isTimeAdjustmentDisabled; + + (void) GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, &isTimeAdjustmentDisabled); + res->tv_sec = 0; + res->tv_nsec = timeIncrement * 100; + + return 0; + } + default: + break; + } + + return lc_set_errno(EINVAL); +} + +/** + * Get the time of the specified clock clock_id and stores it in the struct + * timespec pointed to by tp. + * @param clock_id The clock_id argument is the identifier of the particular + * clock on which to act. The following clocks are supported: + *
+ *     CLOCK_REALTIME  System-wide real-time clock. Setting this clock
+ *                 requires appropriate privileges.
+ *     CLOCK_MONOTONIC Clock that cannot be set and represents monotonic
+ *                 time since some unspecified starting point.
+ *     CLOCK_PROCESS_CPUTIME_ID High-resolution per-process timer from the CPU.
+ *     CLOCK_THREAD_CPUTIME_ID  Thread-specific CPU-time clock.
+ * 
+ * @param tp The pointer to a timespec structure to receive the time. + * @return If the function succeeds, the return value is 0. + * If the function fails, the return value is -1, + * with errno set to indicate the error. + */ +static int __clock_gettime(clockid_t clock_id, struct _timespec64 *tp) +{ + unsigned __int64 t; + LARGE_INTEGER pf, pc; + union { + unsigned __int64 u64; + FILETIME ft; + } ct, et, kt, ut; + + switch(clock_id) { + case CLOCK_REALTIME: + { + _pthread_get_system_time_best_as_file_time(&ct.ft); + t = ct.u64 - DELTA_EPOCH_IN_100NS; + tp->tv_sec = t / POW10_7; + tp->tv_nsec = ((int) (t % POW10_7)) * 100; + + return 0; + } + + case CLOCK_REALTIME_COARSE: + { + GetSystemTimeAsFileTime(&ct.ft); + t = ct.u64 - DELTA_EPOCH_IN_100NS; + tp->tv_sec = t / POW10_7; + tp->tv_nsec = ((int) (t % POW10_7)) * 100; + + return 0; + } + + case CLOCK_MONOTONIC: + { + if (QueryPerformanceFrequency(&pf) == 0) + return lc_set_errno(EINVAL); + + if (QueryPerformanceCounter(&pc) == 0) + return lc_set_errno(EINVAL); + + tp->tv_sec = pc.QuadPart / pf.QuadPart; + tp->tv_nsec = (int) (((pc.QuadPart % pf.QuadPart) * POW10_9 + (pf.QuadPart >> 1)) / pf.QuadPart); + if (tp->tv_nsec >= POW10_9) { + tp->tv_sec ++; + tp->tv_nsec -= POW10_9; + } + + return 0; + } + + case CLOCK_PROCESS_CPUTIME_ID: + { + if(0 == GetProcessTimes(GetCurrentProcess(), &ct.ft, &et.ft, &kt.ft, &ut.ft)) + return lc_set_errno(EINVAL); + t = kt.u64 + ut.u64; + tp->tv_sec = t / POW10_7; + tp->tv_nsec = ((int) (t % POW10_7)) * 100; + + return 0; + } + + case CLOCK_THREAD_CPUTIME_ID: + { + if(0 == GetThreadTimes(GetCurrentThread(), &ct.ft, &et.ft, &kt.ft, &ut.ft)) + return lc_set_errno(EINVAL); + t = kt.u64 + ut.u64; + tp->tv_sec = t / POW10_7; + tp->tv_nsec = ((int) (t % POW10_7)) * 100; + + return 0; + } + + default: + break; + } + + return lc_set_errno(EINVAL); +} + +/** + * Sleep for the specified time. + * @param clock_id This argument should always be CLOCK_REALTIME (0). + * @param flags 0 for relative sleep interval, others for absolute waking up. + * @param request The desired sleep interval or absolute waking up time. + * @param remain The remain amount of time to sleep. + * The current implemention just ignore it. + * @return If the function succeeds, the return value is 0. + * If the function fails, the return value is -1, + * with errno set to indicate the error. + */ +static int __clock_nanosleep(clockid_t clock_id, int flags, + const struct _timespec64 *request, + struct _timespec64 *remain) +{ + struct _timespec64 tp; + + if (clock_id != CLOCK_REALTIME) + return lc_set_errno(EINVAL); + + if (flags == 0) + return nanosleep64(request, remain); + + /* TIMER_ABSTIME = 1 */ + __clock_gettime(CLOCK_REALTIME, &tp); + + tp.tv_sec = request->tv_sec - tp.tv_sec; + tp.tv_nsec = request->tv_nsec - tp.tv_nsec; + if (tp.tv_nsec < 0) { + tp.tv_nsec += POW10_9; + tp.tv_sec --; + } + + return nanosleep64(&tp, remain); +} + +/** + * Set the time of the specified clock clock_id. + * @param clock_id This argument should always be CLOCK_REALTIME (0). + * @param tp The requested time. + * @return If the function succeeds, the return value is 0. + * If the function fails, the return value is -1, + * with errno set to indicate the error. + */ +static int __clock_settime(clockid_t clock_id, const struct _timespec64 *tp) +{ + SYSTEMTIME st; + + union { + unsigned __int64 u64; + FILETIME ft; + } t; + + if (clock_id != CLOCK_REALTIME) + return lc_set_errno(EINVAL); + + t.u64 = tp->tv_sec * (__int64) POW10_7 + tp->tv_nsec / 100 + DELTA_EPOCH_IN_100NS; + if (FileTimeToSystemTime(&t.ft, &st) == 0) + return lc_set_errno(EINVAL); + + if (SetSystemTime(&st) == 0) + return lc_set_errno(EPERM); + + return 0; +} + +/** + * Versions to use with 64-bit time_t (struct _timespec64) + */ + +int clock_getres64 (clockid_t clock_id, struct _timespec64 *tp) +{ + return __clock_getres (clock_id, tp); +} + +int clock_gettime64 (clockid_t clock_id, struct _timespec64 *tp) +{ + return __clock_gettime (clock_id, tp); +} + +int clock_settime64 (clockid_t clock_id, const struct _timespec64 *tp) +{ + return __clock_settime (clock_id, tp); +} + +int clock_nanosleep64 (clockid_t clock_id, int flags, + const struct _timespec64 *request, struct _timespec64 *remain) +{ + return __clock_nanosleep (clock_id, flags, request, remain); +} + +/** + * Versions to use with 32-bit time_t (struct _timespec32) + */ + +int clock_getres32 (clockid_t clock_id, struct _timespec32 *tp) +{ + struct _timespec64 tp64 = {0}; + + if (__clock_getres (clock_id, &tp64) == -1) + return -1; + + tp->tv_sec = (__time32_t) tp64.tv_sec; + tp->tv_nsec = tp64.tv_nsec; + + return 0; +} + +int clock_gettime32 (clockid_t clock_id, struct _timespec32 *tp) +{ + struct _timespec64 tp64 = {0}; + + if (__clock_gettime (clock_id, &tp64) == -1) + return -1; + + if (tp64.tv_sec > INT_MAX) + { + _set_errno (EOVERFLOW); + return -1; + } + + tp->tv_sec = (__time32_t) tp64.tv_sec; + tp->tv_nsec = tp64.tv_nsec; + + return 0; +} + +int clock_settime32 (clockid_t clock_id, const struct _timespec32 *tp) +{ + struct _timespec64 tp64 = {.tv_sec = tp->tv_sec, .tv_nsec = tp->tv_nsec}; + return __clock_settime (clock_id, &tp64); +} + +int clock_nanosleep32 (clockid_t clock_id, int flags, + const struct _timespec32 *request, struct _timespec32 *remain) +{ + struct _timespec64 request64 = { + .tv_sec = request->tv_sec, + .tv_nsec = request->tv_nsec + }; + struct _timespec64 remain64 = {0}; + + if (__clock_nanosleep (clock_id, flags, &request64, &remain64) == -1) + return -1; + + assert (remain64.tv_sec <= INT_MAX); + + if (remain != NULL) { + remain->tv_sec = (__time32_t)remain64.tv_sec; + remain->tv_nsec = remain64.tv_nsec; + } + + return 0; +} diff --git a/vendor/winpthreads/src/cond.c b/vendor/winpthreads/src/cond.c new file mode 100644 index 00000000..a77cd3ef --- /dev/null +++ b/vendor/winpthreads/src/cond.c @@ -0,0 +1,717 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/* + * Posix Condition Variables for Microsoft Windows. + * 22-9-2010 Partly based on the ACE framework implementation. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include + +#define WINPTHREAD_COND_DECL WINPTHREAD_API + +/* public header files */ +#include "pthread.h" +#include "pthread_time.h" +/* internal header files */ +#include "cond.h" +#include "misc.h" +#include "thread.h" + +#include "pthread_compat.h" + +int __pthread_shallcancel (void); + +static int do_sema_b_wait (HANDLE sema, int nointerrupt, DWORD timeout,CRITICAL_SECTION *cs, LONG *val); +static int do_sema_b_release(HANDLE sema, LONG count,CRITICAL_SECTION *cs, LONG *val); +static void cleanup_wait(void *arg); + +typedef struct sCondWaitHelper { + cond_t *c; + pthread_mutex_t *external_mutex; + int *r; +} sCondWaitHelper; + +int do_sema_b_wait_intern (HANDLE sema, int nointerrupt, DWORD timeout); + +static pthread_spinlock_t cond_locked = PTHREAD_SPINLOCK_INITIALIZER; + +static int +cond_static_init (pthread_cond_t *c) +{ + int r = 0; + + pthread_spin_lock (&cond_locked); + if (c == NULL) + r = EINVAL; + else if (*c == PTHREAD_COND_INITIALIZER) + r = pthread_cond_init (c, NULL); + else + /* We assume someone was faster ... */ + r = 0; + pthread_spin_unlock (&cond_locked); + return r; +} + +int +pthread_condattr_destroy (pthread_condattr_t *a) +{ + if (!a) + return EINVAL; + *a = 0; + return 0; +} + +int +pthread_condattr_init (pthread_condattr_t *a) +{ + if (!a) + return EINVAL; + *a = 0; + return 0; +} + +int +pthread_condattr_getpshared (const pthread_condattr_t *a, int *s) +{ + if (!a || !s) + return EINVAL; + *s = *a; + return 0; +} + +int +pthread_condattr_getclock (const pthread_condattr_t *a, clockid_t *clock_id) +{ + if (!a || !clock_id) + return EINVAL; + *clock_id = 0; + return 0; +} + +int +pthread_condattr_setclock(pthread_condattr_t *a, clockid_t clock_id) +{ + if (!a || clock_id != 0) + return EINVAL; + return 0; +} + +int +pthread_condattr_setpshared (pthread_condattr_t *a, int s) +{ + if (!a || (s != PTHREAD_PROCESS_SHARED && s != PTHREAD_PROCESS_PRIVATE)) + return EINVAL; + if (s == PTHREAD_PROCESS_SHARED) + { + *a = PTHREAD_PROCESS_PRIVATE; + return ENOSYS; + } + *a = s; + return 0; +} + +int +pthread_cond_init (pthread_cond_t *c, const pthread_condattr_t *a) +{ + cond_t *_c; + int r = 0; + + if (!c) + return EINVAL; + if (a && *a == PTHREAD_PROCESS_SHARED) + return ENOSYS; + + if ((_c = calloc(1, sizeof(*_c))) == NULL) + return ENOMEM; + + _c->valid = DEAD_COND; + _c->busy = 0; + _c->waiters_count_ = 0; + _c->waiters_count_gone_ = 0; + _c->waiters_count_unblock_ = 0; + + _c->sema_q = CreateSemaphore (NULL, /* no security */ + 0, /* initially 0 */ + 0x7fffffff, /* max count */ + NULL); /* unnamed */ + _c->sema_b = CreateSemaphore (NULL, /* no security */ + 0, /* initially 0 */ + 0x7fffffff, /* max count */ + NULL); + if (_c->sema_q == NULL || _c->sema_b == NULL) { + if (_c->sema_q != NULL) + CloseHandle (_c->sema_q); + if (_c->sema_b != NULL) + CloseHandle (_c->sema_b); + free (_c); + r = EAGAIN; + } else { + InitializeCriticalSection(&_c->waiters_count_lock_); + InitializeCriticalSection(&_c->waiters_b_lock_); + InitializeCriticalSection(&_c->waiters_q_lock_); + _c->value_q = 0; + _c->value_b = 1; + } + if (!r) + { + _c->valid = LIFE_COND; + *c = (pthread_cond_t)_c; + } + else + *c = (pthread_cond_t)NULL; + return r; +} + +int +pthread_cond_destroy (pthread_cond_t *c) +{ + cond_t *_c; + int r; + if (!c || !*c) + return EINVAL; + if (*c == PTHREAD_COND_INITIALIZER) + { + pthread_spin_lock (&cond_locked); + if (*c == PTHREAD_COND_INITIALIZER) + { + *c = (pthread_cond_t)NULL; + r = 0; + } + else + r = EBUSY; + pthread_spin_unlock (&cond_locked); + return r; + } + _c = (cond_t *) *c; + r = do_sema_b_wait(_c->sema_b, 0, INFINITE,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + return r; + if (!TryEnterCriticalSection (&_c->waiters_count_lock_)) + { + do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b); + return EBUSY; + } + if (_c->waiters_count_ > _c->waiters_count_gone_) + { + r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b); + if (!r) r = EBUSY; + LeaveCriticalSection(&_c->waiters_count_lock_); + return r; + } + *c = (pthread_cond_t)NULL; + do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b); + + if (!CloseHandle (_c->sema_q) && !r) + r = EINVAL; + if (!CloseHandle (_c->sema_b) && !r) + r = EINVAL; + LeaveCriticalSection (&_c->waiters_count_lock_); + DeleteCriticalSection(&_c->waiters_count_lock_); + DeleteCriticalSection(&_c->waiters_b_lock_); + DeleteCriticalSection(&_c->waiters_q_lock_); + _c->valid = DEAD_COND; + free(_c); + return 0; +} + +int +pthread_cond_signal (pthread_cond_t *c) +{ + cond_t *_c; + int r; + + if (!c || !*c) + return EINVAL; + _c = (cond_t *)*c; + if (_c == (cond_t *)PTHREAD_COND_INITIALIZER) + return 0; + else if (_c->valid != (unsigned int)LIFE_COND) + return EINVAL; + + EnterCriticalSection (&_c->waiters_count_lock_); + /* If there aren't any waiters, then this is a no-op. */ + if (_c->waiters_count_unblock_ != 0) + { + if (_c->waiters_count_ == 0) + { + LeaveCriticalSection (&_c->waiters_count_lock_); + /* pthread_testcancel(); */ + return 0; + } + _c->waiters_count_ -= 1; + _c->waiters_count_unblock_ += 1; + } + else if (_c->waiters_count_ > _c->waiters_count_gone_) + { + r = do_sema_b_wait (_c->sema_b, 1, INFINITE,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + { + LeaveCriticalSection (&_c->waiters_count_lock_); + /* pthread_testcancel(); */ + return r; + } + if (_c->waiters_count_gone_ != 0) + { + _c->waiters_count_ -= _c->waiters_count_gone_; + _c->waiters_count_gone_ = 0; + } + _c->waiters_count_ -= 1; + _c->waiters_count_unblock_ = 1; + } + else + { + LeaveCriticalSection (&_c->waiters_count_lock_); + /* pthread_testcancel(); */ + return 0; + } + LeaveCriticalSection (&_c->waiters_count_lock_); + r = do_sema_b_release(_c->sema_q, 1,&_c->waiters_q_lock_,&_c->value_q); + /* pthread_testcancel(); */ + return r; +} + +int +pthread_cond_broadcast (pthread_cond_t *c) +{ + cond_t *_c; + int r; + int relCnt = 0; + + if (!c || !*c) + return EINVAL; + _c = (cond_t *)*c; + if (_c == (cond_t*)PTHREAD_COND_INITIALIZER) + return 0; + else if (_c->valid != (unsigned int)LIFE_COND) + return EINVAL; + + EnterCriticalSection (&_c->waiters_count_lock_); + /* If there aren't any waiters, then this is a no-op. */ + if (_c->waiters_count_unblock_ != 0) + { + if (_c->waiters_count_ == 0) + { + LeaveCriticalSection (&_c->waiters_count_lock_); + /* pthread_testcancel(); */ + return 0; + } + relCnt = _c->waiters_count_; + _c->waiters_count_ = 0; + _c->waiters_count_unblock_ += relCnt; + } + else if (_c->waiters_count_ > _c->waiters_count_gone_) + { + r = do_sema_b_wait (_c->sema_b, 1, INFINITE,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + { + LeaveCriticalSection (&_c->waiters_count_lock_); + /* pthread_testcancel(); */ + return r; + } + if (_c->waiters_count_gone_ != 0) + { + _c->waiters_count_ -= _c->waiters_count_gone_; + _c->waiters_count_gone_ = 0; + } + relCnt = _c->waiters_count_; + _c->waiters_count_ = 0; + _c->waiters_count_unblock_ = relCnt; + } + else + { + LeaveCriticalSection (&_c->waiters_count_lock_); + /* pthread_testcancel(); */ + return 0; + } + LeaveCriticalSection (&_c->waiters_count_lock_); + r = do_sema_b_release(_c->sema_q, relCnt,&_c->waiters_q_lock_,&_c->value_q); + /* pthread_testcancel(); */ + return r; +} + +int +pthread_cond_wait (pthread_cond_t *c, pthread_mutex_t *external_mutex) +{ + sCondWaitHelper ch; + cond_t *_c; + int r; + + /* pthread_testcancel(); */ + + if (!c || *c == (pthread_cond_t)NULL) + return EINVAL; + _c = (cond_t *)*c; + if (*c == PTHREAD_COND_INITIALIZER) + { + r = cond_static_init(c); + if (r != 0 && r != EBUSY) + return r; + _c = (cond_t *) *c; + } else if (_c->valid != (unsigned int)LIFE_COND) + return EINVAL; + +tryagain: + r = do_sema_b_wait (_c->sema_b, 0, INFINITE,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + return r; + + if (!TryEnterCriticalSection (&_c->waiters_count_lock_)) + { + r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + return r; + sched_yield(); + goto tryagain; + } + + _c->waiters_count_++; + LeaveCriticalSection(&_c->waiters_count_lock_); + r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + return r; + + ch.c = _c; + ch.r = &r; + ch.external_mutex = external_mutex; + + pthread_cleanup_push(cleanup_wait, (void *) &ch); + r = pthread_mutex_unlock(external_mutex); + if (!r) + r = do_sema_b_wait (_c->sema_q, 0, INFINITE,&_c->waiters_q_lock_,&_c->value_q); + + pthread_cleanup_pop(1); + return r; +} + +static int +pthread_cond_timedwait_impl (pthread_cond_t *c, pthread_mutex_t *external_mutex, const struct _timespec64 *t, int rel) +{ + sCondWaitHelper ch; + DWORD dwr; + int r; + cond_t *_c; + + /* pthread_testcancel(); */ + + if (!c || !*c) + return EINVAL; + _c = (cond_t *)*c; + if (_c == (cond_t *)PTHREAD_COND_INITIALIZER) + { + r = cond_static_init(c); + if (r && r != EBUSY) + return r; + _c = (cond_t *) *c; + } else if ((_c)->valid != (unsigned int)LIFE_COND) + return EINVAL; + + if (rel == 0) + { + dwr = dwMilliSecs(_pthread_rel_time_in_ms(t)); + } + else + { + dwr = dwMilliSecs(_pthread_time_in_ms_from_timespec(t)); + } + +tryagain: + r = do_sema_b_wait (_c->sema_b, 0, INFINITE,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + return r; + + if (!TryEnterCriticalSection (&_c->waiters_count_lock_)) + { + r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + return r; + sched_yield(); + goto tryagain; + } + + _c->waiters_count_++; + LeaveCriticalSection(&_c->waiters_count_lock_); + r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + return r; + + ch.c = _c; + ch.r = &r; + ch.external_mutex = external_mutex; + { + pthread_cleanup_push(cleanup_wait, (void *) &ch); + + r = pthread_mutex_unlock(external_mutex); + if (!r) + r = do_sema_b_wait (_c->sema_q, 0, dwr,&_c->waiters_q_lock_,&_c->value_q); + + pthread_cleanup_pop(1); + } + return r; +} + +int +pthread_cond_timedwait64(pthread_cond_t *c, pthread_mutex_t *m, const struct _timespec64 *t) +{ + return pthread_cond_timedwait_impl(c, m, t, 0); +} + +int +pthread_cond_timedwait32(pthread_cond_t *c, pthread_mutex_t *m, const struct _timespec32 *t) +{ + struct _timespec64 t64 = {.tv_sec = t->tv_sec, .tv_nsec = t->tv_nsec}; + return pthread_cond_timedwait_impl(c, m, &t64, 0); +} + +int +pthread_cond_timedwait64_relative_np(pthread_cond_t *c, pthread_mutex_t *m, const struct _timespec64 *t) +{ + return pthread_cond_timedwait_impl(c, m, t, 1); +} + +int +pthread_cond_timedwait32_relative_np(pthread_cond_t *c, pthread_mutex_t *m, const struct _timespec32 *t) +{ + struct _timespec64 t64 = {.tv_sec = t->tv_sec, .tv_nsec = t->tv_nsec}; + return pthread_cond_timedwait_impl(c, m, &t64, 1); +} + +static void +cleanup_wait (void *arg) +{ + int n, r; + sCondWaitHelper *ch = (sCondWaitHelper *) arg; + cond_t *_c; + + _c = ch->c; + EnterCriticalSection (&_c->waiters_count_lock_); + n = _c->waiters_count_unblock_; + if (n != 0) + _c->waiters_count_unblock_ -= 1; + else if ((INT_MAX/2) - 1 == _c->waiters_count_gone_) + { + _c->waiters_count_gone_ += 1; + r = do_sema_b_wait (_c->sema_b, 1, INFINITE,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + { + LeaveCriticalSection(&_c->waiters_count_lock_); + ch->r[0] = r; + return; + } + _c->waiters_count_ -= _c->waiters_count_gone_; + r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + { + LeaveCriticalSection(&_c->waiters_count_lock_); + ch->r[0] = r; + return; + } + _c->waiters_count_gone_ = 0; + } + else + _c->waiters_count_gone_ += 1; + LeaveCriticalSection (&_c->waiters_count_lock_); + + if (n == 1) + { + r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + { + ch->r[0] = r; + return; + } + } + r = pthread_mutex_lock(ch->external_mutex); + if (r != 0) + ch->r[0] = r; +} + +static int +do_sema_b_wait (HANDLE sema, int nointerrupt, DWORD timeout,CRITICAL_SECTION *cs, LONG *val) +{ + int r; + LONG v; + EnterCriticalSection(cs); + InterlockedDecrement(val); + v = val[0]; + LeaveCriticalSection(cs); + if (v >= 0) + return 0; + r = do_sema_b_wait_intern (sema, nointerrupt, timeout); + EnterCriticalSection(cs); + if (r != 0) + InterlockedIncrement(val); + LeaveCriticalSection(cs); + return r; +} + +int +do_sema_b_wait_intern (HANDLE sema, int nointerrupt, DWORD timeout) +{ + HANDLE arr[2]; + DWORD maxH = 1; + int r = 0; + DWORD res, dt; + if (nointerrupt == 1) + { + res = _pthread_wait_for_single_object(sema, timeout); + switch (res) { + case WAIT_TIMEOUT: + r = ETIMEDOUT; + break; + case WAIT_ABANDONED: + r = EPERM; + break; + case WAIT_OBJECT_0: + break; + default: + /*We can only return EINVAL though it might not be posix compliant */ + r = EINVAL; + } + if (r != 0 && r != EINVAL && WaitForSingleObject(sema, 0) == WAIT_OBJECT_0) + r = 0; + return r; + } + arr[0] = sema; + arr[1] = (HANDLE) pthread_getevent (); + if (arr[1] != NULL) maxH += 1; + if (maxH == 2) + { +redo: + res = _pthread_wait_for_multiple_objects(maxH, arr, 0, timeout); + switch (res) { + case WAIT_TIMEOUT: + r = ETIMEDOUT; + break; + case (WAIT_OBJECT_0 + 1): + ResetEvent(arr[1]); + if (nointerrupt != 2) + { + pthread_testcancel(); + return EINVAL; + } + pthread_testcancel (); + goto redo; + case WAIT_ABANDONED: + r = EPERM; + break; + case WAIT_OBJECT_0: + r = 0; + break; + default: + /*We can only return EINVAL though it might not be posix compliant */ + r = EINVAL; + } + if (r != 0 && r != EINVAL && WaitForSingleObject(arr[0], 0) == WAIT_OBJECT_0) + r = 0; + if (r != 0 && nointerrupt != 2 && __pthread_shallcancel ()) + return EINVAL; + return r; + } + if (timeout == INFINITE) + { + do { + res = _pthread_wait_for_single_object(sema, 40); + switch (res) { + case WAIT_TIMEOUT: + r = ETIMEDOUT; + break; + case WAIT_ABANDONED: + r = EPERM; + break; + case WAIT_OBJECT_0: + r = 0; + break; + default: + /*We can only return EINVAL though it might not be posix compliant */ + r = EINVAL; + } + if (r != 0 && __pthread_shallcancel ()) + { + if (nointerrupt != 2) + pthread_testcancel(); + return EINVAL; + } + } while (r == ETIMEDOUT); + if (r != 0 && r != EINVAL && WaitForSingleObject(sema, 0) == WAIT_OBJECT_0) + r = 0; + return r; + } + dt = 20; + do { + if (dt > timeout) dt = timeout; + res = _pthread_wait_for_single_object(sema, dt); + switch (res) { + case WAIT_TIMEOUT: + r = ETIMEDOUT; + break; + case WAIT_ABANDONED: + r = EPERM; + break; + case WAIT_OBJECT_0: + r = 0; + break; + default: + /*We can only return EINVAL though it might not be posix compliant */ + r = EINVAL; + } + timeout -= dt; + if (timeout != 0 && r != 0 && __pthread_shallcancel ()) + return EINVAL; + } while (r == ETIMEDOUT && timeout != 0); + if (r != 0 && r == ETIMEDOUT && WaitForSingleObject(sema, 0) == WAIT_OBJECT_0) + r = 0; + if (r != 0 && nointerrupt != 2) + pthread_testcancel(); + return r; +} + +static int +do_sema_b_release(HANDLE sema, LONG count,CRITICAL_SECTION *cs, LONG *val) +{ + int wc; + EnterCriticalSection(cs); + if (((long long) val[0] + (long long) count) > (long long) 0x7fffffffLL) + { + LeaveCriticalSection(cs); + return ERANGE; + } + wc = -val[0]; + InterlockedExchangeAdd(val, count); + if (wc <= 0 || ReleaseSemaphore(sema, (wc < count ? wc : count), NULL)) + { + LeaveCriticalSection(cs); + return 0; + } + InterlockedExchangeAdd(val, -count); + LeaveCriticalSection(cs); + return EINVAL; +} diff --git a/vendor/winpthreads/src/cond.h b/vendor/winpthreads/src/cond.h new file mode 100644 index 00000000..fcf7db35 --- /dev/null +++ b/vendor/winpthreads/src/cond.h @@ -0,0 +1,57 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifndef WIN_PTHREADS_COND_H +#define WIN_PTHREADS_COND_H + +#define CHECK_COND(c) \ + do { \ + if (!(c) || !*c || (*c == PTHREAD_COND_INITIALIZER) \ + || ( ((cond_t *)(*c))->valid != (unsigned int)LIFE_COND ) ) \ + return EINVAL; \ + } while (0) + +#define LIFE_COND 0xC0BAB1FD +#define DEAD_COND 0xC0DEADBF + +#define STATIC_COND_INITIALIZER(x) ((pthread_cond_t)(x) == ((pthread_cond_t)PTHREAD_COND_INITIALIZER)) + +typedef struct cond_t cond_t; +struct cond_t +{ + unsigned int valid; + int busy; + LONG waiters_count_; /* Number of waiting threads. */ + LONG waiters_count_unblock_; /* Number of waiting threads whitch can be unblocked. */ + LONG waiters_count_gone_; /* Number of waiters which are gone. */ + CRITICAL_SECTION waiters_count_lock_; /* Serialize access to . */ + CRITICAL_SECTION waiters_q_lock_; /* Serialize access to sema_q. */ + LONG value_q; + CRITICAL_SECTION waiters_b_lock_; /* Serialize access to sema_b. */ + LONG value_b; + HANDLE sema_q; /* Semaphore used to queue up threads waiting for the condition to + become signaled. */ + HANDLE sema_b; /* Semaphore used to queue up threads waiting for the condition which + became signaled. */ +}; + +#endif diff --git a/vendor/winpthreads/src/config.h b/vendor/winpthreads/src/config.h new file mode 100644 index 00000000..2a0269a3 --- /dev/null +++ b/vendor/winpthreads/src/config.h @@ -0,0 +1,5 @@ +/* Minimal hand-written config.h for building vendored winpthreads with a modern toolchain. + winpthreads only uses it for optional feature probes; the portable fallbacks are correct. */ +#ifndef WINPTHREADS_CONFIG_H +#define WINPTHREADS_CONFIG_H +#endif diff --git a/vendor/winpthreads/src/libgcc/dll_dependency.S b/vendor/winpthreads/src/libgcc/dll_dependency.S new file mode 100644 index 00000000..21e1c890 --- /dev/null +++ b/vendor/winpthreads/src/libgcc/dll_dependency.S @@ -0,0 +1,90 @@ +/* Implementation for gcc's internal stack-allocation routines. */ +#if defined(__i386__) || (defined(__x86_64__) && !defined(__arm64ec__)) +.global ___chkstk +.global __alloca + +.global ___chkstk_ms +___chkstk_ms: +#ifdef _WIN64 + pushq %rax + pushq %rcx + cmpq $0x1000, %rax + leaq 24(%rsp), %rcx + jb .Lchkstk_ms_end +.Lchkstk_ms_loop: + subq $0x1000, %rcx + subq $0x1000, %rax + orq $0x0, (%rcx) + cmpq $0x1000, %rax + ja .Lchkstk_ms_loop +.Lchkstk_ms_end: + subq %rax, %rcx + orq $0x0, (%rcx) + popq %rcx + popq %rax + ret +#else + pushl %eax + pushl %ecx + cmpl $0x1000, %eax + leal 12(%esp), %ecx + jb chkstk_ms_end +chkstk_ms_loop: + subl $0x1000, %ecx + subl $0x1000, %eax + orl $0x0, (%ecx) + cmpl $0x1000, %eax + ja chkstk_ms_loop +chkstk_ms_end: + subl %eax, %ecx + orl $0x0, (%ecx) + popl %ecx + popl %eax + ret +#endif + +#ifdef _WIN64 +__alloca: + movq %rcx, %rax +.align 4 +___chkstk: + popq %r11 + movq %rsp, %r10 + cmpq $0x1000, %rax + jb .Lchkstk_end +.Lchkstk_loop: + subq $0x1000, %r10 + subq $0x1000, %rax + orl $0x0, (%r10) + cmpq $0x1000, %rax + ja .Lchkstk_loop +.Lchkstk_end: + subq %rax, %r10 + movq %rsp, %rax + orl $0x0, (%r10) + movq %r10, %rsp + pushq %r11 + ret +#else +___chkstk: +__alloca: + pushl %ecx + leal 8(%esp), %ecx + cmpl $0x1000, %eax /* > 4k ?*/ + jb chkstk_end +chkstk_loop: + subl $0x1000, %ecx + subl $0x1000, %eax + orl $0x0, (%ecx) + cmpl $0x1000, %eax + ja chkstk_loop +chkstk_end: + subl %eax, %ecx + orl $0x0, (%ecx) + movl %esp, %eax + movl %ecx, %esp + movl (%eax), %ecx + pushl 4(%eax) + ret +#endif +#endif diff --git a/vendor/winpthreads/src/libgcc/dll_math.c b/vendor/winpthreads/src/libgcc/dll_math.c new file mode 100644 index 00000000..8b9ffaf0 --- /dev/null +++ b/vendor/winpthreads/src/libgcc/dll_math.c @@ -0,0 +1,574 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LIBKERN_QUAD_H_ +#define _LIBKERN_QUAD_H_ + +/* + * Quad arithmetic. + * + * This library makes the following assumptions: + * + * - The type long long (aka quad_t) exists. + * + * - A quad variable is exactly twice as long as `long'. + * + * - The machine's arithmetic is two's complement. + * + * This library can provide 128-bit arithmetic on a machine with 128-bit + * quads and 64-bit longs, for instance, or 96-bit arithmetic on machines + * with 48-bit longs. + */ +/* +#include +#include +#include +#include +*/ + +#include +typedef long long quad_t; +typedef unsigned long long u_quad_t; +typedef unsigned long u_long; +#ifndef CHAR_BIT +#define CHAR_BIT __CHAR_BIT__ +#endif + +/* + * Define the order of 32-bit words in 64-bit words. + * For little endian only. + */ +#define _QUAD_HIGHWORD 1 +#define _QUAD_LOWWORD 0 + +/* + * Depending on the desired operation, we view a `long long' (aka quad_t) in + * one or more of the following formats. + */ +union uu { + quad_t q; /* as a (signed) quad */ + quad_t uq; /* as an unsigned quad */ + long sl[2]; /* as two signed longs */ + u_long ul[2]; /* as two unsigned longs */ +}; + +/* + * Define high and low longwords. + */ +#define H _QUAD_HIGHWORD +#define L _QUAD_LOWWORD + +/* + * Total number of bits in a quad_t and in the pieces that make it up. + * These are used for shifting, and also below for halfword extraction + * and assembly. + */ +#define QUAD_BITS (sizeof(quad_t) * CHAR_BIT) +#define LONG_BITS (sizeof(long) * CHAR_BIT) +#define HALF_BITS (sizeof(long) * CHAR_BIT / 2) + +/* + * Extract high and low shortwords from longword, and move low shortword of + * longword to upper half of long, i.e., produce the upper longword of + * ((quad_t)(x) << (number_of_bits_in_long/2)). (`x' must actually be u_long.) + * + * These are used in the multiply code, to split a longword into upper + * and lower halves, and to reassemble a product as a quad_t, shifted left + * (sizeof(long)*CHAR_BIT/2). + */ +#define HHALF(x) ((x) >> HALF_BITS) +#define LHALF(x) ((x) & ((1 << HALF_BITS) - 1)) +#define LHUP(x) ((x) << HALF_BITS) + +typedef unsigned int qshift_t; + +quad_t __ashldi3(quad_t, qshift_t); +quad_t __ashrdi3(quad_t, qshift_t); +int __cmpdi2(quad_t a, quad_t b); +quad_t __divdi3(quad_t a, quad_t b); +quad_t __lshrdi3(quad_t, qshift_t); +quad_t __moddi3(quad_t a, quad_t b); +u_quad_t __qdivrem(u_quad_t u, u_quad_t v, u_quad_t *rem); +u_quad_t __udivdi3(u_quad_t a, u_quad_t b); +u_quad_t __umoddi3(u_quad_t a, u_quad_t b); +int __ucmpdi2(u_quad_t a, u_quad_t b); +quad_t __divmoddi4(quad_t a, quad_t b, quad_t *rem); +u_quad_t __udivmoddi4(u_quad_t a, u_quad_t b, u_quad_t *rem); + +#endif /* !_LIBKERN_QUAD_H_ */ + +#if defined (_X86_) && !defined (__x86_64__) +/* + * Shift a (signed) quad value left (arithmetic shift left). + * This is the same as logical shift left! + */ +quad_t +__ashldi3(quad_t a, qshift_t shift) +{ + union uu aa; + + aa.q = a; + if (shift >= LONG_BITS) { + aa.ul[H] = shift >= QUAD_BITS ? 0 : + aa.ul[L] << (shift - LONG_BITS); + aa.ul[L] = 0; + } else if (shift > 0) { + aa.ul[H] = (aa.ul[H] << shift) | + (aa.ul[L] >> (LONG_BITS - shift)); + aa.ul[L] <<= shift; + } + return (aa.q); +} + +/* + * Shift a (signed) quad value right (arithmetic shift right). + */ +quad_t +__ashrdi3(quad_t a, qshift_t shift) +{ + union uu aa; + + aa.q = a; + if (shift >= LONG_BITS) { + long s; + + /* + * Smear bits rightward using the machine's right-shift + * method, whether that is sign extension or zero fill, + * to get the `sign word' s. Note that shifting by + * LONG_BITS is undefined, so we shift (LONG_BITS-1), + * then 1 more, to get our answer. + */ + s = (aa.sl[H] >> (LONG_BITS - 1)) >> 1; + aa.ul[L] = shift >= QUAD_BITS ? s : + aa.sl[H] >> (shift - LONG_BITS); + aa.ul[H] = s; + } else if (shift > 0) { + aa.ul[L] = (aa.ul[L] >> shift) | + (aa.ul[H] << (LONG_BITS - shift)); + aa.sl[H] >>= shift; + } + return (aa.q); +} + +/* + * Return 0, 1, or 2 as a <, =, > b respectively. + * Both a and b are considered signed---which means only the high word is + * signed. + */ +int +__cmpdi2(quad_t a, quad_t b) +{ + union uu aa, bb; + + aa.q = a; + bb.q = b; + return (aa.sl[H] < bb.sl[H] ? 0 : aa.sl[H] > bb.sl[H] ? 2 : + aa.ul[L] < bb.ul[L] ? 0 : aa.ul[L] > bb.ul[L] ? 2 : 1); +} + +/* + * Divide two signed quads. + * ??? if -1/2 should produce -1 on this machine, this code is wrong + */ +quad_t +__divdi3(quad_t a, quad_t b) +{ + u_quad_t ua, ub, uq; + int neg; + + if (a < 0) + ua = -(u_quad_t)a, neg = 1; + else + ua = a, neg = 0; + if (b < 0) + ub = -(u_quad_t)b, neg ^= 1; + else + ub = b; + uq = __qdivrem(ua, ub, (u_quad_t *)0); + return (neg ? -uq : uq); +} + +/* + * Shift an (unsigned) quad value right (logical shift right). + */ +quad_t +__lshrdi3(quad_t a, qshift_t shift) +{ + union uu aa; + + aa.q = a; + if (shift >= LONG_BITS) { + aa.ul[L] = shift >= QUAD_BITS ? 0 : + aa.ul[H] >> (shift - LONG_BITS); + aa.ul[H] = 0; + } else if (shift > 0) { + aa.ul[L] = (aa.ul[L] >> shift) | + (aa.ul[H] << (LONG_BITS - shift)); + aa.ul[H] >>= shift; + } + return (aa.q); +} + +/* + * Return remainder after dividing two signed quads. + * + * XXX + * If -1/2 should produce -1 on this machine, this code is wrong. + */ +quad_t +__moddi3(quad_t a, quad_t b) +{ + u_quad_t ua, ub, ur; + int neg; + + if (a < 0) + ua = -(u_quad_t)a, neg = 1; + else + ua = a, neg = 0; + if (b < 0) + ub = -(u_quad_t)b; + else + ub = b; + (void)__qdivrem(ua, ub, &ur); + return (neg ? -ur : ur); +} + + +/* + * Multiprecision divide. This algorithm is from Knuth vol. 2 (2nd ed), + * section 4.3.1, pp. 257--259. + */ + +#define B (1 << HALF_BITS) /* digit base */ + +/* Combine two `digits' to make a single two-digit number. */ +#define COMBINE(a, b) (((u_long)(a) << HALF_BITS) | (b)) + +/* select a type for digits in base B: use unsigned short if they fit */ +#if ULONG_MAX == 0xffffffff && USHRT_MAX >= 0xffff +typedef unsigned short digit; +#else +typedef u_long digit; +#endif + +/* + * Shift p[0]..p[len] left `sh' bits, ignoring any bits that + * `fall out' the left (there never will be any such anyway). + * We may assume len >= 0. NOTE THAT THIS WRITES len+1 DIGITS. + */ +static void +__shl(register digit *p, register int len, register int sh) +{ + register int i; + + for (i = 0; i < len; i++) + p[i] = LHALF(p[i] << sh) | (p[i + 1] >> (HALF_BITS - sh)); + p[i] = LHALF(p[i] << sh); +} + +/* + * __qdivrem(u, v, rem) returns u/v and, optionally, sets *rem to u%v. + * + * We do this in base 2-sup-HALF_BITS, so that all intermediate products + * fit within u_long. As a consequence, the maximum length dividend and + * divisor are 4 `digits' in this base (they are shorter if they have + * leading zeros). + */ +u_quad_t +__qdivrem(u_quad_t uq, u_quad_t vq, u_quad_t *arq) +{ + union uu tmp; + digit *u, *v, *q; + register digit v1, v2; + u_long qhat, rhat, t; + int m, n, d, j, i; + digit uspace[5], vspace[5], qspace[5]; + + /* + * Take care of special cases: divide by zero, and u < v. + */ + if (vq == 0) { + /* divide by zero. */ + static volatile const unsigned int zero = 0; + + tmp.ul[H] = tmp.ul[L] = 1 / zero; + if (arq) + *arq = uq; + return (tmp.q); + } + if (uq < vq) { + if (arq) + *arq = uq; + return (0); + } + u = &uspace[0]; + v = &vspace[0]; + q = &qspace[0]; + + /* + * Break dividend and divisor into digits in base B, then + * count leading zeros to determine m and n. When done, we + * will have: + * u = (u[1]u[2]...u[m+n]) sub B + * v = (v[1]v[2]...v[n]) sub B + * v[1] != 0 + * 1 < n <= 4 (if n = 1, we use a different division algorithm) + * m >= 0 (otherwise u < v, which we already checked) + * m + n = 4 + * and thus + * m = 4 - n <= 2 + */ + tmp.uq = uq; + u[0] = 0; + u[1] = HHALF(tmp.ul[H]); + u[2] = LHALF(tmp.ul[H]); + u[3] = HHALF(tmp.ul[L]); + u[4] = LHALF(tmp.ul[L]); + tmp.uq = vq; + v[1] = HHALF(tmp.ul[H]); + v[2] = LHALF(tmp.ul[H]); + v[3] = HHALF(tmp.ul[L]); + v[4] = LHALF(tmp.ul[L]); + for (n = 4; v[1] == 0; v++) { + if (--n == 1) { + u_long rbj; /* r*B+u[j] (not root boy jim) */ + digit q1, q2, q3, q4; + + /* + * Change of plan, per exercise 16. + * r = 0; + * for j = 1..4: + * q[j] = floor((r*B + u[j]) / v), + * r = (r*B + u[j]) % v; + * We unroll this completely here. + */ + t = v[2]; /* nonzero, by definition */ + q1 = u[1] / t; + rbj = COMBINE(u[1] % t, u[2]); + q2 = rbj / t; + rbj = COMBINE(rbj % t, u[3]); + q3 = rbj / t; + rbj = COMBINE(rbj % t, u[4]); + q4 = rbj / t; + if (arq) + *arq = rbj % t; + tmp.ul[H] = COMBINE(q1, q2); + tmp.ul[L] = COMBINE(q3, q4); + return (tmp.q); + } + } + + /* + * By adjusting q once we determine m, we can guarantee that + * there is a complete four-digit quotient at &qspace[1] when + * we finally stop. + */ + for (m = 4 - n; u[1] == 0; u++) + m--; + for (i = 4 - m; --i >= 0;) + q[i] = 0; + q += 4 - m; + + /* + * Here we run Program D, translated from MIX to C and acquiring + * a few minor changes. + * + * D1: choose multiplier 1 << d to ensure v[1] >= B/2. + */ + d = 0; + for (t = v[1]; t < B / 2; t <<= 1) + d++; + if (d > 0) { + __shl(&u[0], m + n, d); /* u <<= d */ + __shl(&v[1], n - 1, d); /* v <<= d */ + } + /* + * D2: j = 0. + */ + j = 0; + v1 = v[1]; /* for D3 -- note that v[1..n] are constant */ + v2 = v[2]; /* for D3 */ + do { + register digit uj0, uj1, uj2; + + /* + * D3: Calculate qhat (\^q, in TeX notation). + * Let qhat = min((u[j]*B + u[j+1])/v[1], B-1), and + * let rhat = (u[j]*B + u[j+1]) mod v[1]. + * While rhat < B and v[2]*qhat > rhat*B+u[j+2], + * decrement qhat and increase rhat correspondingly. + * Note that if rhat >= B, v[2]*qhat < rhat*B. + */ + uj0 = u[j + 0]; /* for D3 only -- note that u[j+...] change */ + uj1 = u[j + 1]; /* for D3 only */ + uj2 = u[j + 2]; /* for D3 only */ + if (uj0 == v1) { + qhat = B; + rhat = uj1; + goto qhat_too_big; + } else { + u_long nn = COMBINE(uj0, uj1); + qhat = nn / v1; + rhat = nn % v1; + } + while (v2 * qhat > COMBINE(rhat, uj2)) { + qhat_too_big: + qhat--; + if ((rhat += v1) >= B) + break; + } + /* + * D4: Multiply and subtract. + * The variable `t' holds any borrows across the loop. + * We split this up so that we do not require v[0] = 0, + * and to eliminate a final special case. + */ + for (t = 0, i = n; i > 0; i--) { + t = u[i + j] - v[i] * qhat - t; + u[i + j] = LHALF(t); + t = (B - HHALF(t)) & (B - 1); + } + t = u[j] - t; + u[j] = LHALF(t); + /* + * D5: test remainder. + * There is a borrow if and only if HHALF(t) is nonzero; + * in that (rare) case, qhat was too large (by exactly 1). + * Fix it by adding v[1..n] to u[j..j+n]. + */ + if (HHALF(t)) { + qhat--; + for (t = 0, i = n; i > 0; i--) { /* D6: add back. */ + t += u[i + j] + v[i]; + u[i + j] = LHALF(t); + t = HHALF(t); + } + u[j] = LHALF(u[j] + t); + } + q[j] = qhat; + } while (++j <= m); /* D7: loop on j. */ + + /* + * If caller wants the remainder, we have to calculate it as + * u[m..m+n] >> d (this is at most n digits and thus fits in + * u[m+1..m+n], but we may need more source digits). + */ + if (arq) { + if (d) { + for (i = m + n; i > m; --i) + u[i] = (u[i] >> d) | + LHALF(u[i - 1] << (HALF_BITS - d)); + u[i] = 0; + } + tmp.ul[H] = COMBINE(uspace[1], uspace[2]); + tmp.ul[L] = COMBINE(uspace[3], uspace[4]); + *arq = tmp.q; + } + + tmp.ul[H] = COMBINE(qspace[1], qspace[2]); + tmp.ul[L] = COMBINE(qspace[3], qspace[4]); + return (tmp.q); +} + +/* + * Return 0, 1, or 2 as a <, =, > b respectively. + * Neither a nor b are considered signed. + */ +int +__ucmpdi2(u_quad_t a, u_quad_t b) +{ + union uu aa, bb; + + aa.uq = a; + bb.uq = b; + return (aa.ul[H] < bb.ul[H] ? 0 : aa.ul[H] > bb.ul[H] ? 2 : + aa.ul[L] < bb.ul[L] ? 0 : aa.ul[L] > bb.ul[L] ? 2 : 1); +} + +/* + * Divide two unsigned quads. + */ +u_quad_t +__udivdi3(u_quad_t a, u_quad_t b) +{ + + return (__qdivrem(a, b, (u_quad_t *)0)); +} + +/* + * Return remainder after dividing two unsigned quads. + */ +u_quad_t +__umoddi3(u_quad_t a, u_quad_t b) +{ + u_quad_t r; + + (void)__qdivrem(a, b, &r); + return (r); +} + +/* + * Divide two signed quads. + * This function is new in GCC 7. + */ +quad_t +__divmoddi4(quad_t a, quad_t b, quad_t *rem) +{ + u_quad_t ua, ub, uq, ur; + int negq, negr; + + if (a < 0) + ua = -(u_quad_t)a, negq = 1, negr = 1; + else + ua = a, negq = 0, negr = 0; + if (b < 0) + ub = -(u_quad_t)b, negq ^= 1; + else + ub = b; + uq = __qdivrem(ua, ub, &ur); + if (rem) + *rem = (negr ? -ur : ur); + return (negq ? -uq : uq); +} + +u_quad_t +__udivmoddi4(u_quad_t a, u_quad_t b, u_quad_t *rem) +{ + return __qdivrem(a, b, rem); +} + +#else +static int __attribute__((unused)) dummy; +#endif /*deined (_X86_) && !defined (__x86_64__)*/ + diff --git a/vendor/winpthreads/src/misc.c b/vendor/winpthreads/src/misc.c new file mode 100644 index 00000000..b2d5cebc --- /dev/null +++ b/vendor/winpthreads/src/misc.c @@ -0,0 +1,234 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define WIN32_LEAN_AND_MEAN +#include + +/* public header files */ +#include "pthread.h" +/* internal header files */ +#include "misc.h" + +void (WINAPI *_pthread_get_system_time_best_as_file_time) (LPFILETIME) = NULL; +static ULONGLONG (WINAPI *_pthread_get_tick_count_64) (VOID); +HRESULT (WINAPI *_pthread_set_thread_description) (HANDLE, PCWSTR) = NULL; +BOOL (WINAPI *_pthread_get_handle_information) (HANDLE, LPDWORD) = NULL; + +#if defined(__GNUC__) || defined(__clang__) +#if __GNUC__ >= 9 && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wprio-ctor-dtor" +#endif +__attribute__((constructor(0))) +#endif +static void winpthreads_init(void) +{ + HMODULE mod = GetModuleHandleA("kernel32.dll"); + if (mod) + { + _pthread_get_handle_information = + (BOOL (WINAPI *)(HANDLE, LPDWORD))(void*) GetProcAddress(mod, "GetHandleInformation"); + + _pthread_get_tick_count_64 = + (ULONGLONG (WINAPI *)(VOID))(void*) GetProcAddress(mod, "GetTickCount64"); + + _pthread_set_thread_description = + (HRESULT (WINAPI *)(HANDLE, PCWSTR))(void*) GetProcAddress(mod, "SetThreadDescription"); + + /* <1us precision on Windows 10 */ + _pthread_get_system_time_best_as_file_time = + (void (WINAPI *)(LPFILETIME))(void*) GetProcAddress(mod, "GetSystemTimePreciseAsFileTime"); + } + + if (!_pthread_get_system_time_best_as_file_time) + /* >15ms precision on Windows 10 */ + _pthread_get_system_time_best_as_file_time = GetSystemTimeAsFileTime; + + /* Although SetThreadDescription lives in kernel32.dll, on Windows Server 2016, + * Windows 10 LTSB 2016 and Windows 10 version 1607, it was only available in + * kernelbase.dll. So, load it from there for maximum coverage. + */ + if (!_pthread_set_thread_description) + { + mod = GetModuleHandleA("kernelbase.dll"); + if (mod) + { + _pthread_set_thread_description = + (HRESULT (WINAPI *)(HANDLE, PCWSTR))(void*) GetProcAddress(mod, "SetThreadDescription"); + } + } +} +#if defined(__GNUC__) && __GNUC__ >= 9 && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +/* Force a reference to __xc_t to prevent whole program optimization + * from discarding the variable. */ + +/* On x86, symbols are prefixed with an underscore. */ +# if defined(_M_IX86) +# pragma comment(linker, "/include:___xc_t") +# else +# pragma comment(linker, "/include:__xc_t") +# endif + +#pragma section(".CRT$XCT", long, read) +__declspec(allocate(".CRT$XCT")) +extern const _PVFV __xc_t; +const _PVFV __xc_t = winpthreads_init; +#endif + +unsigned long long _pthread_time_in_ms(void) +{ + FILETIME ft; + + GetSystemTimeAsFileTime(&ft); + return (((unsigned long long)ft.dwHighDateTime << 32) + ft.dwLowDateTime + - 0x19DB1DED53E8000ULL) / 10000ULL; +} + +unsigned long long _pthread_time_in_ms_from_timespec(const struct _timespec64 *ts) +{ + unsigned long long t = (unsigned long long) ts->tv_sec * 1000LL; + /* The +999999 is here to ensure that the division always rounds up */ + t += (unsigned long long) (ts->tv_nsec + 999999) / 1000000; + + return t; +} + +unsigned long long _pthread_rel_time_in_ms(const struct _timespec64 *ts) +{ + unsigned long long t1 = _pthread_time_in_ms_from_timespec(ts); + unsigned long long t2 = _pthread_time_in_ms(); + + /* Prevent underflow */ + if (t1 < t2) return 0; + return t1 - t2; +} + +static unsigned long long +_pthread_get_tick_count (long long *frequency) +{ + if (_pthread_get_tick_count_64 != NULL) + return _pthread_get_tick_count_64 (); + + LARGE_INTEGER freq, timestamp; + + if (*frequency == 0) + { + if (QueryPerformanceFrequency (&freq)) + *frequency = freq.QuadPart; + else + *frequency = -1; + } + + if (*frequency > 0 && QueryPerformanceCounter (×tamp)) + return timestamp.QuadPart / (*frequency / 1000); + + /* Fallback */ + return GetTickCount (); +} + +/* A wrapper around WaitForSingleObject() that ensures that + * the wait function does not time out before the time + * actually runs out. This is needed because WaitForSingleObject() + * might have poor accuracy, returning earlier than expected. + * On the other hand, returning a bit *later* than expected + * is acceptable in a preemptive multitasking environment. + */ +unsigned long +_pthread_wait_for_single_object (void *handle, unsigned long timeout) +{ + DWORD result; + unsigned long long start_time, end_time; + unsigned long wait_time; + long long frequency = 0; + + if (timeout == INFINITE || timeout == 0) + return WaitForSingleObject ((HANDLE) handle, (DWORD) timeout); + + start_time = _pthread_get_tick_count (&frequency); + end_time = start_time + timeout; + wait_time = timeout; + + do + { + unsigned long long current_time; + + result = WaitForSingleObject ((HANDLE) handle, (DWORD) wait_time); + if (result != WAIT_TIMEOUT) + break; + + current_time = _pthread_get_tick_count (&frequency); + if (current_time >= end_time) + break; + + wait_time = (DWORD) (end_time - current_time); + } while (TRUE); + + return result; +} + +/* A wrapper around WaitForMultipleObjects() that ensures that + * the wait function does not time out before the time + * actually runs out. This is needed because WaitForMultipleObjects() + * might have poor accuracy, returning earlier than expected. + * On the other hand, returning a bit *later* than expected + * is acceptable in a preemptive multitasking environment. + */ +unsigned long +_pthread_wait_for_multiple_objects (unsigned long count, void **handles, unsigned int all, unsigned long timeout) +{ + DWORD result; + unsigned long long start_time, end_time; + unsigned long wait_time; + long long frequency = 0; + + if (timeout == INFINITE || timeout == 0) + return WaitForMultipleObjects ((DWORD) count, (HANDLE *) handles, all, (DWORD) timeout); + + start_time = _pthread_get_tick_count (&frequency); + end_time = start_time + timeout; + wait_time = timeout; + + do + { + unsigned long long current_time; + + result = WaitForMultipleObjects ((DWORD) count, (HANDLE *) handles, all, (DWORD) wait_time); + if (result != WAIT_TIMEOUT) + break; + + current_time = _pthread_get_tick_count (&frequency); + if (current_time >= end_time) + break; + + wait_time = (DWORD) (end_time - current_time); + } while (TRUE); + + return result; +} diff --git a/vendor/winpthreads/src/misc.h b/vendor/winpthreads/src/misc.h new file mode 100644 index 00000000..dc11eaff --- /dev/null +++ b/vendor/winpthreads/src/misc.h @@ -0,0 +1,112 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifndef WIN_PTHREADS_MISC_H +#define WIN_PTHREADS_MISC_H + +#include +/* public header files */ +#include "pthread_compat.h" + +#define PTR2INT(x) ((int)(uintptr_t)(x)) + +#if SIZE_MAX>UINT_MAX +typedef long long LONGBAG; +#else +typedef long LONGBAG; +#endif + +extern BOOL (WINAPI *_pthread_get_handle_information) (HANDLE, LPDWORD); + +/* For gcc and clang define DUMMY_WRITABLE_DWORD as C99 compound literal. + * For other pre-C99 compilers declare DUMMY_WRITABLE_DWORD as static variable. + */ +#if defined(__GNUC__) || defined(__clang__) +#define DUMMY_WRITABLE_DWORD (DWORD){0} +#else +static DWORD DUMMY_WRITABLE_DWORD; +#endif + +#define TEST_HANDLE(h) \ + (((h) != NULL && (h) != INVALID_HANDLE_VALUE) && ( \ + _pthread_get_handle_information == NULL || \ + _pthread_get_handle_information((h), &DUMMY_WRITABLE_DWORD) || \ + GetLastError() == ERROR_CALL_NOT_IMPLEMENTED \ + )) + +#define CHECK_HANDLE2(h, e) \ + do { \ + if (!TEST_HANDLE(h)) \ + return e; \ + } while (0) + +#define CHECK_HANDLE(h) CHECK_HANDLE2(h, EINVAL) + +#define CHECK_PTR(p) do { if (!(p)) return EINVAL; } while (0) + +#define UPD_RESULT(x,r) do { int _r = (x); (r) = (r) ? (r) : _r; } while (0) + +#define CHECK_THREAD(t) \ + do { \ + CHECK_PTR(t); \ + CHECK_HANDLE((t)->h); \ + } while (0) + +#define CHECK_OBJECT(o, e) \ + do { \ + if (!(o)) return e; \ + CHECK_HANDLE2((o)->h, e); \ + } while (0) + +#define VALID(x) if (!(p)) return EINVAL; + +/* ms can be 64 bit, solve wrap-around issues: */ +static WINPTHREADS_INLINE unsigned long dwMilliSecs(unsigned long long ms) +{ + if (ms >= 0xffffffffULL) return 0xfffffffful; + return (unsigned long) ms; +} + +unsigned long long _pthread_time_in_ms(void); +unsigned long long _pthread_time_in_ms_from_timespec(const struct _timespec64 *ts); +unsigned long long _pthread_rel_time_in_ms(const struct _timespec64 *ts); +unsigned long _pthread_wait_for_single_object (void *handle, unsigned long timeout); +unsigned long _pthread_wait_for_multiple_objects (unsigned long count, void **handles, unsigned int all, unsigned long timeout); + +extern void (WINAPI *_pthread_get_system_time_best_as_file_time) (LPFILETIME); +extern HRESULT (WINAPI *_pthread_set_thread_description) (HANDLE, PCWSTR); + +#if defined(__GNUC__) || defined(__clang__) +#define likely(cond) __builtin_expect((cond) != 0, 1) +#define unlikely(cond) __builtin_expect((cond) != 0, 0) +#else +#define likely(cond) (cond) +#define unlikely(cond) (cond) +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define UNREACHABLE() __builtin_unreachable() +#elif defined(_MSC_VER) +#define UNREACHABLE() __assume(0) +#endif + +#endif diff --git a/vendor/winpthreads/src/mutex.c b/vendor/winpthreads/src/mutex.c new file mode 100644 index 00000000..ae478586 --- /dev/null +++ b/vendor/winpthreads/src/mutex.c @@ -0,0 +1,403 @@ +/* + Copyright (c) 2011, 2014 mingw-w64 project + Copyright (c) 2015 Intel Corporation + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include + +#define WINPTHREAD_MUTEX_DECL WINPTHREAD_API + +/* public header files */ +#include "pthread.h" +/* internal header files */ +#include "misc.h" + +typedef enum { + Unlocked, /* Not locked. */ + Locked, /* Locked but without waiters. */ + Waiting, /* Locked, may have waiters. */ +} mutex_state_t; + +typedef enum { + Normal, + Errorcheck, + Recursive, +} mutex_type_t; + +/* The heap-allocated part of a mutex. */ +typedef struct { + mutex_state_t state; + mutex_type_t type; + HANDLE event; /* Auto-reset event, or NULL if not yet allocated. */ + unsigned rec_lock; /* For recursive mutexes, the number of times the + mutex has been locked in excess by the same thread. */ + volatile DWORD owner; /* For recursive and error-checking mutexes, the + ID of the owning thread if the mutex is locked. */ +} mutex_impl_t; + +/* Whether a mutex is still a static initializer (not a pointer to + a mutex_impl_t). */ +static BOOL +is_static_initializer(pthread_mutex_t m) +{ + /* Treat 0 as a static initializer as well (for normal mutexes), + to tolerate sloppy code in libgomp. (We should rather fix that code!) */ + intptr_t v = (intptr_t)m; + return v >= -3 && v <= 0; +/* Should be simple: + return (uintptr_t)m >= (uintptr_t)-3; */ +} + +/* Create and return the implementation part of a mutex from a static + initialiser. Return NULL on out-of-memory error. */ +static WINPTHREADS_ATTRIBUTE((noinline)) mutex_impl_t * +mutex_impl_init(pthread_mutex_t *m, mutex_impl_t *mi) +{ + mutex_impl_t *new_mi = malloc(sizeof(mutex_impl_t)); + if (new_mi == NULL) + return NULL; + new_mi->state = Unlocked; + new_mi->type = (mi == (void *)PTHREAD_RECURSIVE_MUTEX_INITIALIZER ? Recursive + : mi == (void *)PTHREAD_ERRORCHECK_MUTEX_INITIALIZER ? Errorcheck + : Normal); + new_mi->event = NULL; + new_mi->rec_lock = 0; + new_mi->owner = (DWORD)-1; + if (InterlockedCompareExchangePointer((PVOID volatile *)m, new_mi, mi) == mi) { + return new_mi; + } else { + /* Someone created the struct before us. */ + free(new_mi); + return (mutex_impl_t *)*m; + } +} + +/* Return the implementation part of a mutex, creating it if necessary. + Return NULL on out-of-memory error. */ +static WINPTHREADS_INLINE mutex_impl_t * +mutex_impl(pthread_mutex_t *m) +{ + mutex_impl_t *mi = (mutex_impl_t *)*m; + if (is_static_initializer((pthread_mutex_t)mi)) { + return mutex_impl_init(m, mi); + } else { + /* mi cannot be null here; avoid a test in the fast path. */ + if (mi == NULL) + UNREACHABLE(); + return mi; + } +} + +/* Lock a mutex. Give up after 'timeout' ms (with ETIMEDOUT), + or never if timeout=INFINITE. */ +static WINPTHREADS_INLINE int +pthread_mutex_lock_intern (pthread_mutex_t *m, DWORD timeout) +{ + mutex_impl_t *mi = mutex_impl(m); + if (mi == NULL) + return ENOMEM; + mutex_state_t old_state = InterlockedExchange((long *)&mi->state, Locked); + if (unlikely(old_state != Unlocked)) { + /* The mutex is already locked. */ + + if (mi->type != Normal) { + /* Recursive or Errorcheck */ + if (mi->owner == GetCurrentThreadId()) { + /* FIXME: A recursive mutex should not need two atomic ops when locking + recursively. We could rewrite by doing compare-and-swap instead of + test-and-set the first time, but it would lead to more code + duplication and add a conditional branch to the critical path. */ + InterlockedCompareExchange((long *)&mi->state, old_state, Locked); + if (mi->type == Recursive) { + mi->rec_lock++; + return 0; + } else { + /* type == Errorcheck */ + return EDEADLK; + } + } + } + + /* Make sure there is an event object on which to wait. */ + if (mi->event == NULL) { + /* Make an auto-reset event object. */ + HANDLE ev = CreateEvent(NULL, FALSE, FALSE, NULL); + if (ev == NULL) { + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + return EPERM; + default: + return ENOMEM; /* Probably accurate enough. */ + } + } + if (InterlockedCompareExchangePointer(&mi->event, ev, NULL) != NULL) { + /* Someone created the event before us. */ + CloseHandle(ev); + } + } + + /* At this point, mi->event is non-NULL. */ + + while (InterlockedExchange((long *)&mi->state, Waiting) != Unlocked) { + /* For timed locking attempts, it is possible (although unlikely) + that we are woken up but someone else grabs the lock before us, + and we have to go back to sleep again. In that case, the total + wait may be longer than expected. */ + + unsigned r = _pthread_wait_for_single_object(mi->event, timeout); + switch (r) { + case WAIT_TIMEOUT: + return ETIMEDOUT; + case WAIT_OBJECT_0: + break; + default: + return EINVAL; + } + } + } + + if (mi->type != Normal) + mi->owner = GetCurrentThreadId(); + + return 0; +} + +int +pthread_mutex_lock (pthread_mutex_t *m) +{ + return pthread_mutex_lock_intern (m, INFINITE); +} + +/* Internal version which always uses `struct _timespec64`. */ +static int __pthread_mutex_timedlock(pthread_mutex_t *m, const struct _timespec64 *ts) +{ + unsigned long long patience; + if (ts != NULL) { + unsigned long long end = _pthread_time_in_ms_from_timespec(ts); + unsigned long long now = _pthread_time_in_ms(); + patience = end > now ? end - now : 0; + if (patience > 0xffffffff) + patience = INFINITE; + } else { + patience = INFINITE; + } + return pthread_mutex_lock_intern(m, patience); +} + +int pthread_mutex_timedlock64(pthread_mutex_t *m, const struct _timespec64 *ts) +{ + return __pthread_mutex_timedlock (m, ts); +} + +int pthread_mutex_timedlock32(pthread_mutex_t *m, const struct _timespec32 *ts) +{ + struct _timespec64 ts64 = {.tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec}; + return __pthread_mutex_timedlock (m, &ts64); +} + +int pthread_mutex_unlock(pthread_mutex_t *m) +{ + /* Here m might an initialiser of an error-checking or recursive mutex, in + which case the behaviour is well-defined, so we can't skip this check. */ + mutex_impl_t *mi = mutex_impl(m); + if (mi == NULL) + return ENOMEM; + + if (unlikely(mi->type != Normal)) { + if (mi->state == Unlocked) + return EPERM; + if (mi->owner != GetCurrentThreadId()) + return EPERM; + if (mi->rec_lock > 0) { + mi->rec_lock--; + return 0; + } + mi->owner = (DWORD)-1; + } + if (unlikely(InterlockedExchange((long *)&mi->state, Unlocked) == Waiting)) { + if (!SetEvent(mi->event)) + return EPERM; + } + return 0; +} + +int pthread_mutex_trylock(pthread_mutex_t *m) +{ + mutex_impl_t *mi = mutex_impl(m); + if (mi == NULL) + return ENOMEM; + + if (InterlockedCompareExchange((long *)&mi->state, Locked, Unlocked) == Unlocked) { + if (mi->type != Normal) + mi->owner = GetCurrentThreadId(); + return 0; + } else { + if (mi->type == Recursive && mi->owner == GetCurrentThreadId()) { + mi->rec_lock++; + return 0; + } + return EBUSY; + } +} + +int +pthread_mutex_init (pthread_mutex_t *m, const pthread_mutexattr_t *a) +{ + pthread_mutex_t init = PTHREAD_MUTEX_INITIALIZER; + if (a != NULL) { + int pshared; + if (pthread_mutexattr_getpshared(a, &pshared) == 0 + && pshared == PTHREAD_PROCESS_SHARED) + return ENOSYS; + + int type; + if (pthread_mutexattr_gettype(a, &type) == 0) { + switch (type) { + case PTHREAD_MUTEX_ERRORCHECK: + init = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER; + break; + case PTHREAD_MUTEX_RECURSIVE: + init = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; + break; + default: + init = PTHREAD_MUTEX_INITIALIZER; + break; + } + } + } + *m = init; + return 0; +} + +int pthread_mutex_destroy (pthread_mutex_t *m) +{ + mutex_impl_t *mi = (mutex_impl_t *)*m; + if (!is_static_initializer((pthread_mutex_t)mi)) { + if (mi->event != NULL) + CloseHandle(mi->event); + free(mi); + /* Sabotage attempts to re-use the mutex before initialising it again. */ + *m = (pthread_mutex_t)NULL; + } + + return 0; +} + +int pthread_mutexattr_init(pthread_mutexattr_t *a) +{ + *a = PTHREAD_MUTEX_NORMAL | (PTHREAD_PROCESS_PRIVATE << 3); + return 0; +} + +int pthread_mutexattr_destroy(pthread_mutexattr_t *a) +{ + if (!a) + return EINVAL; + + return 0; +} + +int pthread_mutexattr_gettype(const pthread_mutexattr_t *a, int *type) +{ + if (!a || !type) + return EINVAL; + + *type = *a & 3; + + return 0; +} + +int pthread_mutexattr_settype(pthread_mutexattr_t *a, int type) +{ + if (!a || (type != PTHREAD_MUTEX_NORMAL && type != PTHREAD_MUTEX_RECURSIVE && type != PTHREAD_MUTEX_ERRORCHECK)) + return EINVAL; + *a &= ~3; + *a |= type; + + return 0; +} + +int pthread_mutexattr_getpshared(const pthread_mutexattr_t *a, int *type) +{ + if (!a || !type) + return EINVAL; + *type = (*a & 4 ? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE); + + return 0; +} + +int pthread_mutexattr_setpshared(pthread_mutexattr_t * a, int type) +{ + int r = 0; + if (!a || (type != PTHREAD_PROCESS_SHARED + && type != PTHREAD_PROCESS_PRIVATE)) + return EINVAL; + if (type == PTHREAD_PROCESS_SHARED) + { + type = PTHREAD_PROCESS_PRIVATE; + r = ENOSYS; + } + type = (type == PTHREAD_PROCESS_SHARED ? 4 : 0); + + *a &= ~4; + *a |= type; + + return r; +} + +int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *a, int *type) +{ + *type = *a & (8 + 16); + + return 0; +} + +int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int type) +{ + if ((type & (8 + 16)) != 8 + 16) return EINVAL; + + *a &= ~(8 + 16); + *a |= type; + + return 0; +} + +int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *a, int * prio) +{ + *prio = *a / PTHREAD_PRIO_MULT; + return 0; +} + +int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *a, int prio) +{ + *a &= (PTHREAD_PRIO_MULT - 1); + *a += prio * PTHREAD_PRIO_MULT; + + return 0; +} diff --git a/vendor/winpthreads/src/nanosleep.c b/vendor/winpthreads/src/nanosleep.c new file mode 100644 index 00000000..a81bf2c8 --- /dev/null +++ b/vendor/winpthreads/src/nanosleep.c @@ -0,0 +1,109 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the w64 mingw-runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include + +#define WINPTHREAD_NANOSLEEP_DECL WINPTHREAD_API + +/* public header files */ +#include "pthread.h" +#include "pthread_time.h" +/* internal header files */ +#include "thread.h" + +#define POW10_3 1000 +#define POW10_4 10000 +#define POW10_6 1000000 +#define POW10_9 1000000000 +#define MAX_SLEEP_IN_MS 4294967294UL + +/** + * Sleep for the specified time. + * @param request The desired amount of time to sleep. + * @param remain The remain amount of time to sleep. + * @return If the function succeeds, the return value is 0. + * If the function fails, the return value is -1, + * with errno set to indicate the error. + */ +static int __nanosleep(const struct _timespec64 *request, struct _timespec64 *remain) +{ + unsigned long ms, rc = 0; + unsigned __int64 u64, want, real; + + union { + unsigned __int64 ns100; + FILETIME ft; + } _start, _end; + + if (request->tv_sec < 0 || request->tv_nsec < 0 || request->tv_nsec >= POW10_9) { + errno = EINVAL; + return -1; + } + + if (remain != NULL) GetSystemTimeAsFileTime(&_start.ft); + + want = u64 = request->tv_sec * POW10_3 + request->tv_nsec / POW10_6; + while (u64 > 0 && rc == 0) { + if (u64 >= MAX_SLEEP_IN_MS) ms = MAX_SLEEP_IN_MS; + else ms = (unsigned long) u64; + + u64 -= ms; + rc = _pthread_delay_np_ms(ms); + } + + if (rc != 0) { /* WAIT_IO_COMPLETION (192) */ + if (remain != NULL) { + GetSystemTimeAsFileTime(&_end.ft); + real = (_end.ns100 - _start.ns100) / POW10_4; + + if (real >= want) u64 = 0; + else u64 = want - real; + + remain->tv_sec = u64 / POW10_3; + remain->tv_nsec = (long) (u64 % POW10_3) * POW10_6; + } + + errno = EINTR; + return -1; + } + + return 0; +} + +int nanosleep64(const struct _timespec64 *request, struct _timespec64 *remain) +{ + return __nanosleep (request, remain); +} + +int nanosleep32(const struct _timespec32 *request, struct _timespec32 *remain) +{ + struct _timespec64 request64 = { + .tv_sec = request->tv_sec, + .tv_nsec = request->tv_nsec + }; + struct _timespec64 remain64 = {0}; + + if (__nanosleep (&request64, &remain64) == -1) + return -1; + + assert (remain64.tv_sec <= INT_MAX); + + if (remain != NULL) { + remain->tv_sec = (__time32_t)remain64.tv_sec; + remain->tv_nsec = remain64.tv_nsec; + } + + return 0; +} diff --git a/vendor/winpthreads/src/rwlock.c b/vendor/winpthreads/src/rwlock.c new file mode 100644 index 00000000..68ef2a73 --- /dev/null +++ b/vendor/winpthreads/src/rwlock.c @@ -0,0 +1,546 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include + +#define WINPTHREAD_RWLOCK_DECL WINPTHREAD_API + +/* public header files */ +#include "pthread.h" +/* internal header files */ +#include "misc.h" +#include "rwlock.h" +#include "thread.h" + +static pthread_spinlock_t rwl_global = PTHREAD_SPINLOCK_INITIALIZER; + +static WINPTHREADS_ATTRIBUTE((noinline)) int rwlock_static_init(pthread_rwlock_t *rw); + +static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_unref(volatile pthread_rwlock_t *rwl, int res) +{ + pthread_spin_lock(&rwl_global); + assert((((rwlock_t *)*rwl)->valid == LIFE_RWLOCK) && (((rwlock_t *)*rwl)->busy > 0)); + ((rwlock_t *)*rwl)->busy--; + pthread_spin_unlock(&rwl_global); + return res; +} + +static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_ref(pthread_rwlock_t *rwl, int f ) +{ + int r = 0; + if (STATIC_RWL_INITIALIZER(*rwl)) { + r = rwlock_static_init(rwl); + if (r != 0 && r != EBUSY) + return r; + } + pthread_spin_lock(&rwl_global); + + if (!rwl || !*rwl || ((rwlock_t *)*rwl)->valid != LIFE_RWLOCK) r = EINVAL; + else { + ((rwlock_t *)*rwl)->busy ++; + } + + pthread_spin_unlock(&rwl_global); + + return r; +} + +static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_ref_unlock(pthread_rwlock_t *rwl ) +{ + int r = 0; + + pthread_spin_lock(&rwl_global); + + if (!rwl || !*rwl || ((rwlock_t *)*rwl)->valid != LIFE_RWLOCK) r = EINVAL; + else if (STATIC_RWL_INITIALIZER(*rwl)) r= EPERM; + else { + ((rwlock_t *)*rwl)->busy ++; + } + + pthread_spin_unlock(&rwl_global); + + return r; +} + +static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_ref_destroy(pthread_rwlock_t *rwl, pthread_rwlock_t *rDestroy ) +{ + int r = 0; + + *rDestroy = (pthread_rwlock_t)NULL; + pthread_spin_lock(&rwl_global); + + if (!rwl || !*rwl) r = EINVAL; + else { + rwlock_t *r_ = (rwlock_t *)*rwl; + if (STATIC_RWL_INITIALIZER(*rwl)) *rwl = (pthread_rwlock_t)NULL; + else if (r_->valid != LIFE_RWLOCK) r = EINVAL; + else if (r_->busy) r = EBUSY; + else { + *rDestroy = *rwl; + *rwl = (pthread_rwlock_t)NULL; + } + } + + pthread_spin_unlock(&rwl_global); + return r; +} + +static int rwlock_gain_both_locks(rwlock_t *rwlock) +{ + int ret; + ret = pthread_mutex_lock(&rwlock->mex); + if (ret != 0) + return ret; + ret = pthread_mutex_lock(&rwlock->mcomplete); + if (ret != 0) + pthread_mutex_unlock(&rwlock->mex); + return ret; +} + +static int rwlock_free_both_locks(rwlock_t *rwlock, int last_fail) +{ + int ret, ret2; + ret = pthread_mutex_unlock(&rwlock->mcomplete); + ret2 = pthread_mutex_unlock(&rwlock->mex); + if (last_fail && ret2 != 0) + ret = ret2; + else if (!last_fail && !ret) + ret = ret2; + return ret; +} + +static pthread_spinlock_t cond_locked = PTHREAD_SPINLOCK_INITIALIZER; + +static WINPTHREADS_ATTRIBUTE((noinline)) int rwlock_static_init(pthread_rwlock_t *rw) +{ + int r; + pthread_spin_lock(&cond_locked); + if (*rw != PTHREAD_RWLOCK_INITIALIZER) + { + pthread_spin_unlock(&cond_locked); + return EINVAL; + } + r = pthread_rwlock_init (rw, NULL); + pthread_spin_unlock(&cond_locked); + + return r; +} + +int pthread_rwlock_init (pthread_rwlock_t *rwlock_, const pthread_rwlockattr_t *attr) +{ + rwlock_t *rwlock; + int r; + + if(!rwlock_) + return EINVAL; + *rwlock_ = (pthread_rwlock_t)NULL; + if ((rwlock = calloc(1, sizeof(*rwlock))) == NULL) + return ENOMEM; + rwlock->valid = DEAD_RWLOCK; + + rwlock->nex_count = rwlock->nsh_count = rwlock->ncomplete = 0; + if ((r = pthread_mutex_init (&rwlock->mex, NULL)) != 0) + { + free(rwlock); + return r; + } + if ((r = pthread_mutex_init (&rwlock->mcomplete, NULL)) != 0) + { + pthread_mutex_destroy(&rwlock->mex); + free(rwlock); + return r; + } + if ((r = pthread_cond_init (&rwlock->ccomplete, NULL)) != 0) + { + pthread_mutex_destroy(&rwlock->mex); + pthread_mutex_destroy (&rwlock->mcomplete); + free(rwlock); + return r; + } + rwlock->valid = LIFE_RWLOCK; + *rwlock_ = (pthread_rwlock_t)rwlock; + return r; +} + +int pthread_rwlock_destroy (pthread_rwlock_t *rwlock_) +{ + rwlock_t *rwlock; + pthread_rwlock_t rDestroy; + int r, r2; + + pthread_spin_lock(&cond_locked); + r = rwl_ref_destroy(rwlock_,&rDestroy); + pthread_spin_unlock(&cond_locked); + + if(r) return r; + if(!rDestroy) return 0; /* destroyed a (still) static initialized rwl */ + + rwlock = (rwlock_t *)rDestroy; + r = rwlock_gain_both_locks (rwlock); + if (r != 0) + { + *rwlock_ = rDestroy; + return r; + } + if (rwlock->nsh_count > rwlock->ncomplete || rwlock->nex_count > 0) + { + *rwlock_ = rDestroy; + r = rwlock_free_both_locks(rwlock, 1); + if (!r) + r = EBUSY; + return r; + } + rwlock->valid = DEAD_RWLOCK; + r = rwlock_free_both_locks(rwlock, 0); + if (r != 0) { *rwlock_ = rDestroy; return r; } + + r = pthread_cond_destroy(&rwlock->ccomplete); + r2 = pthread_mutex_destroy(&rwlock->mex); + if (!r) r = r2; + r2 = pthread_mutex_destroy(&rwlock->mcomplete); + if (!r) r = r2; + rwlock->valid = DEAD_RWLOCK; + free((void *)rDestroy); + return 0; +} + +int pthread_rwlock_rdlock (pthread_rwlock_t *rwlock_) +{ + rwlock_t *rwlock; + int ret; + + /* pthread_testcancel(); */ + + ret = rwl_ref(rwlock_,0); + if(ret != 0) return ret; + + rwlock = (rwlock_t *)*rwlock_; + + ret = pthread_mutex_lock(&rwlock->mex); + if (ret != 0) return rwl_unref(rwlock_, ret); + InterlockedIncrement((long*)&rwlock->nsh_count); + if (rwlock->nsh_count == INT_MAX) + { + ret = pthread_mutex_lock(&rwlock->mcomplete); + if (ret != 0) + { + pthread_mutex_unlock(&rwlock->mex); + return rwl_unref(rwlock_,ret); + } + rwlock->nsh_count -= rwlock->ncomplete; + rwlock->ncomplete = 0; + ret = rwlock_free_both_locks(rwlock, 0); + return rwl_unref(rwlock_, ret); + } + ret = pthread_mutex_unlock(&rwlock->mex); + return rwl_unref(rwlock_, ret); +} + +/* Internal version which always uses `struct _timespec64`. */ +static int __pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock_, const struct _timespec64 *ts) +{ + rwlock_t *rwlock; + int ret; + + /* pthread_testcancel(); */ + + ret = rwl_ref(rwlock_,0); + if(ret != 0) return ret; + + rwlock = (rwlock_t *)*rwlock_; + if ((ret = pthread_mutex_timedlock64 (&rwlock->mex, ts)) != 0) + return rwl_unref(rwlock_, ret); + InterlockedIncrement(&rwlock->nsh_count); + if (rwlock->nsh_count == INT_MAX) + { + ret = pthread_mutex_timedlock64(&rwlock->mcomplete, ts); + if (ret != 0) + { + if (ret == ETIMEDOUT) + InterlockedIncrement(&rwlock->ncomplete); + pthread_mutex_unlock(&rwlock->mex); + return rwl_unref(rwlock_, ret); + } + rwlock->nsh_count -= rwlock->ncomplete; + rwlock->ncomplete = 0; + ret = rwlock_free_both_locks(rwlock, 0); + return rwl_unref(rwlock_, ret); + } + ret = pthread_mutex_unlock(&rwlock->mex); + return rwl_unref(rwlock_, ret); +} + +int pthread_rwlock_timedrdlock64(pthread_rwlock_t *l, const struct _timespec64 *ts) +{ + return __pthread_rwlock_timedrdlock (l, ts); +} + +int pthread_rwlock_timedrdlock32(pthread_rwlock_t *l, const struct _timespec32 *ts) +{ + struct _timespec64 ts64 = {.tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec}; + return __pthread_rwlock_timedrdlock (l, &ts64); +} + +int pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock_) +{ + rwlock_t *rwlock; + int ret; + + ret = rwl_ref(rwlock_,RWL_TRY); + if(ret != 0) return ret; + + rwlock = (rwlock_t *)*rwlock_; + ret = pthread_mutex_trylock(&rwlock->mex); + if (ret != 0) + return rwl_unref(rwlock_, ret); + InterlockedIncrement(&rwlock->nsh_count); + if (rwlock->nsh_count == INT_MAX) + { + ret = pthread_mutex_lock(&rwlock->mcomplete); + if (ret != 0) + { + pthread_mutex_unlock(&rwlock->mex); + return rwl_unref(rwlock_, ret); + } + rwlock->nsh_count -= rwlock->ncomplete; + rwlock->ncomplete = 0; + ret = rwlock_free_both_locks(rwlock, 0); + return rwl_unref(rwlock_, ret); + } + ret = pthread_mutex_unlock(&rwlock->mex); + return rwl_unref(rwlock_,ret); +} + +int pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock_) +{ + rwlock_t *rwlock; + int ret; + + ret = rwl_ref(rwlock_,RWL_TRY); + if(ret != 0) return ret; + + rwlock = (rwlock_t *)*rwlock_; + ret = pthread_mutex_trylock (&rwlock->mex); + if (ret != 0) + return rwl_unref(rwlock_, ret); + ret = pthread_mutex_trylock(&rwlock->mcomplete); + if (ret != 0) + { + int r1 = pthread_mutex_unlock(&rwlock->mex); + if (r1 != 0) + ret = r1; + return rwl_unref(rwlock_, ret); + } + if (rwlock->nex_count != 0) + return rwl_unref(rwlock_, EBUSY); + if (rwlock->ncomplete > 0) + { + rwlock->nsh_count -= rwlock->ncomplete; + rwlock->ncomplete = 0; + } + if (rwlock->nsh_count > 0) + { + ret = rwlock_free_both_locks(rwlock, 0); + if (!ret) + ret = EBUSY; + return rwl_unref(rwlock_, ret); + } + rwlock->nex_count = 1; + return rwl_unref(rwlock_, 0); +} + +int pthread_rwlock_unlock (pthread_rwlock_t *rwlock_) +{ + rwlock_t *rwlock; + int ret; + + ret = rwl_ref_unlock(rwlock_); + if(ret != 0) return ret; + + rwlock = (rwlock_t *)*rwlock_; + if (rwlock->nex_count == 0) + { + ret = pthread_mutex_lock(&rwlock->mcomplete); + if (!ret) + { + int r1; + InterlockedIncrement(&rwlock->ncomplete); + if (rwlock->ncomplete == 0) + ret = pthread_cond_signal(&rwlock->ccomplete); + r1 = pthread_mutex_unlock(&rwlock->mcomplete); + if (!ret) + ret = r1; + } + } + else + { + InterlockedDecrement(&rwlock->nex_count); + ret = rwlock_free_both_locks(rwlock, 0); + } + return rwl_unref(rwlock_, ret); +} + +static void st_cancelwrite (void *arg) +{ + rwlock_t *rwlock = (rwlock_t *)arg; + + rwlock->nsh_count = - rwlock->ncomplete; + rwlock->ncomplete = 0; + rwlock_free_both_locks(rwlock, 0); +} + +int pthread_rwlock_wrlock (pthread_rwlock_t *rwlock_) +{ + rwlock_t *rwlock; + int ret; + + /* pthread_testcancel(); */ + ret = rwl_ref(rwlock_,0); + if(ret != 0) return ret; + + rwlock = (rwlock_t *)*rwlock_; + ret = rwlock_gain_both_locks(rwlock); + if (ret != 0) + return rwl_unref(rwlock_,ret); + + if (rwlock->nex_count == 0) + { + if (rwlock->ncomplete > 0) + { + rwlock->nsh_count -= rwlock->ncomplete; + rwlock->ncomplete = 0; + } + if (rwlock->nsh_count > 0) + { + rwlock->ncomplete = -rwlock->nsh_count; + pthread_cleanup_push(st_cancelwrite, (void *) rwlock); + do { + ret = pthread_cond_wait(&rwlock->ccomplete, &rwlock->mcomplete); + } while (!ret && rwlock->ncomplete < 0); + + pthread_cleanup_pop(!ret ? 0 : 1); + if (!ret) + rwlock->nsh_count = 0; + } + } + if(!ret) + InterlockedIncrement((long*)&rwlock->nex_count); + return rwl_unref(rwlock_,ret); +} + +/* Internal version which always uses `struct _timespec64`. */ +static int __pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock_, const struct _timespec64 *ts) +{ + int ret; + rwlock_t *rwlock; + + /* pthread_testcancel(); */ + if (!rwlock_ || !ts) + return EINVAL; + if ((ret = rwl_ref(rwlock_,0)) != 0) + return ret; + rwlock = (rwlock_t *)*rwlock_; + + ret = pthread_mutex_timedlock64(&rwlock->mex, ts); + if (ret != 0) + return rwl_unref(rwlock_,ret); + ret = pthread_mutex_timedlock64(&rwlock->mcomplete, ts); + if (ret != 0) + { + pthread_mutex_unlock(&rwlock->mex); + return rwl_unref(rwlock_,ret); + } + if (rwlock->nex_count == 0) + { + if (rwlock->ncomplete > 0) + { + rwlock->nsh_count -= rwlock->ncomplete; + rwlock->ncomplete = 0; + } + if (rwlock->nsh_count > 0) + { + rwlock->ncomplete = -rwlock->nsh_count; + pthread_cleanup_push(st_cancelwrite, (void *) rwlock); + do { + ret = pthread_cond_timedwait64(&rwlock->ccomplete, &rwlock->mcomplete, ts); + } while (rwlock->ncomplete < 0 && !ret); + pthread_cleanup_pop(!ret ? 0 : 1); + + if (!ret) + rwlock->nsh_count = 0; + } + } + if(!ret) + InterlockedIncrement((long*)&rwlock->nex_count); + return rwl_unref(rwlock_,ret); +} + +int pthread_rwlock_timedwrlock64(pthread_rwlock_t *rwlock, const struct _timespec64 *ts) +{ + return __pthread_rwlock_timedwrlock (rwlock, ts); +} + +int pthread_rwlock_timedwrlock32(pthread_rwlock_t *rwlock, const struct _timespec32 *ts) +{ + struct _timespec64 ts64 = {.tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec}; + return __pthread_rwlock_timedwrlock (rwlock, &ts64); +} + +int pthread_rwlockattr_destroy(pthread_rwlockattr_t *a) +{ + if (!a) + return EINVAL; + return 0; +} + +int pthread_rwlockattr_init(pthread_rwlockattr_t *a) +{ + if (!a) + return EINVAL; + *a = PTHREAD_PROCESS_PRIVATE; + return 0; +} + +int pthread_rwlockattr_getpshared(pthread_rwlockattr_t *a, int *s) +{ + if (!a || !s) + return EINVAL; + *s = *a; + return 0; +} + +int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *a, int s) +{ + if (!a || (s != PTHREAD_PROCESS_SHARED && s != PTHREAD_PROCESS_PRIVATE)) + return EINVAL; + *a = s; + return 0; +} diff --git a/vendor/winpthreads/src/rwlock.h b/vendor/winpthreads/src/rwlock.h new file mode 100644 index 00000000..8faf12b9 --- /dev/null +++ b/vendor/winpthreads/src/rwlock.h @@ -0,0 +1,46 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifndef WIN_PTHREADS_RWLOCK_H +#define WIN_PTHREADS_RWLOCK_H + +#define LIFE_RWLOCK 0xBAB1F0ED +#define DEAD_RWLOCK 0xDEADB0EF + +#define STATIC_RWL_INITIALIZER(x) ((pthread_rwlock_t)(x) == ((pthread_rwlock_t)PTHREAD_RWLOCK_INITIALIZER)) + +typedef struct rwlock_t rwlock_t; +struct rwlock_t { + unsigned int valid; + int busy; + LONG nex_count; /* Exclusive access counter. */ + LONG nsh_count; /* Shared access counter. */ + LONG ncomplete; /* Shared completed counter. */ + pthread_mutex_t mex; /* Exclusive access protection. */ + pthread_mutex_t mcomplete; /* Shared completed protection. */ + pthread_cond_t ccomplete; /* Shared access completed queue. */ +}; + +#define RWL_SET 0x01 +#define RWL_TRY 0x02 + +#endif diff --git a/vendor/winpthreads/src/sched.c b/vendor/winpthreads/src/sched.c new file mode 100644 index 00000000..0bdcc3ca --- /dev/null +++ b/vendor/winpthreads/src/sched.c @@ -0,0 +1,226 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#define WIN32_LEAN_AND_MEAN +#include + +/* public header files */ +#include "pthread.h" +/* internal header files */ +#include "misc.h" +#include "thread.h" + +int sched_get_priority_min(int pol) +{ + if (pol < SCHED_MIN || pol > SCHED_MAX) { + errno = EINVAL; + return -1; + } + + return THREAD_PRIORITY_IDLE; +} + +int sched_get_priority_max(int pol) +{ + if (pol < SCHED_MIN || pol > SCHED_MAX) { + errno = EINVAL; + return -1; + } + + return THREAD_PRIORITY_TIME_CRITICAL; +} + +int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *p) +{ + int r = 0; + + if (attr == NULL || p == NULL) { + return EINVAL; + } + memcpy(&attr->param, p, sizeof (*p)); + return r; +} + +int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *p) +{ + int r = 0; + + if (attr == NULL || p == NULL) { + return EINVAL; + } + memcpy(p, &attr->param, sizeof (*p)); + return r; +} + +int pthread_attr_setschedpolicy (pthread_attr_t *attr, int pol) +{ + if (!attr || pol < SCHED_MIN || pol > SCHED_MAX) + return EINVAL; + if (pol != SCHED_OTHER) + return ENOTSUP; + return 0; +} + +int pthread_attr_getschedpolicy (const pthread_attr_t *attr, int *pol) +{ + if (!attr || !pol) + return EINVAL; + *pol = SCHED_OTHER; + return 0; +} + +static int pthread_check(pthread_t t) +{ + struct _pthread_v *pv; + + if (!t) + return ESRCH; + pv = __pth_gpointer_locked (t); + if (pv->ended == 0) + return 0; + CHECK_OBJECT(pv, ESRCH); + return 0; +} + +int pthread_getschedparam(pthread_t t, int *pol, struct sched_param *p) +{ + int r; + //if (!t) + // t = pthread_self(); + + if ((r = pthread_check(t)) != 0) + { + return r; + } + + if (!p || !pol) + { + return EINVAL; + } + *pol = __pth_gpointer_locked (t)->sched_pol; + p->sched_priority = __pth_gpointer_locked (t)->sched.sched_priority; + + return 0; +} + +int pthread_setschedparam(pthread_t t, int pol, const struct sched_param *p) +{ + struct _pthread_v *pv; + int r, pr = 0; + //if (!t.p) t = pthread_self(); + + if ((r = pthread_check(t)) != 0) + return r; + + if (pol < SCHED_MIN || pol > SCHED_MAX || p == NULL) + return EINVAL; + if (pol != SCHED_OTHER) + return ENOTSUP; + pr = p->sched_priority; + if (pr < sched_get_priority_min(pol) || pr > sched_get_priority_max(pol)) + return EINVAL; + + /* See msdn: there are actually 7 priorities: + THREAD_PRIORITY_IDLE - -15 + THREAD_PRIORITY_LOWEST -2 + THREAD_PRIORITY_BELOW_NORMAL -1 + THREAD_PRIORITY_NORMAL 0 + THREAD_PRIORITY_ABOVE_NORMAL 1 + THREAD_PRIORITY_HIGHEST 2 + THREAD_PRIORITY_TIME_CRITICAL 15 + */ + if (pr <= THREAD_PRIORITY_IDLE) { + pr = THREAD_PRIORITY_IDLE; + } else if (pr <= THREAD_PRIORITY_LOWEST) { + pr = THREAD_PRIORITY_LOWEST; + } else if (pr >= THREAD_PRIORITY_TIME_CRITICAL) { + pr = THREAD_PRIORITY_TIME_CRITICAL; + } else if (pr >= THREAD_PRIORITY_HIGHEST) { + pr = THREAD_PRIORITY_HIGHEST; + } + pv = __pth_gpointer_locked (t); + if (SetThreadPriority(pv->h, pr)) { + pv->sched_pol = pol; + pv->sched.sched_priority = p->sched_priority; + } else + r = EINVAL; + return r; +} + +int sched_getscheduler(pid_t pid) +{ + if (pid != 0) + { + HANDLE h = NULL; + int selfPid = (int) GetCurrentProcessId (); + + if (pid != (pid_t) selfPid && (h = OpenProcess (PROCESS_QUERY_INFORMATION, 0, (DWORD) pid)) == NULL) + { + errno = (GetLastError () == (0xFF & ERROR_ACCESS_DENIED)) ? EPERM : ESRCH; + return -1; + } + if (h) + CloseHandle (h); + } + return SCHED_OTHER; +} + +int sched_setscheduler(pid_t pid, int pol, const struct sched_param *param) +{ + if (!param) + { + errno = EINVAL; + return -1; + } + if (pid != 0) + { + HANDLE h = NULL; + int selfPid = (int) GetCurrentProcessId (); + + if (pid != (pid_t) selfPid && (h = OpenProcess (PROCESS_SET_INFORMATION, 0, (DWORD) pid)) == NULL) + { + errno = (GetLastError () == (0xFF & ERROR_ACCESS_DENIED)) ? EPERM : ESRCH; + return -1; + } + if (h) + CloseHandle (h); + } + + if (pol != SCHED_OTHER) + { + errno = ENOSYS; + return -1; + } + return SCHED_OTHER; +} + +int sched_yield(void) +{ + Sleep(0); + return 0; +} diff --git a/vendor/winpthreads/src/sem.c b/vendor/winpthreads/src/sem.c new file mode 100644 index 00000000..68b33b8b --- /dev/null +++ b/vendor/winpthreads/src/sem.c @@ -0,0 +1,381 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include + +/* public header files */ +#include "pthread.h" +#include "semaphore.h" +/* internal header files */ +#include "misc.h" +#include "sem.h" +#include "thread.h" + +int do_sema_b_wait_intern (HANDLE sema, int nointerrupt, DWORD timeout); + +static int +sem_result (int res) +{ + if (res != 0) { + errno = res; + return -1; + } + return 0; +} + +int +sem_init (sem_t *sem, int pshared, unsigned int value) +{ + _sem_t *sv; + + if (!sem || value > (unsigned int)SEM_VALUE_MAX) + return sem_result (EINVAL); + if (pshared != PTHREAD_PROCESS_PRIVATE) + return sem_result (EPERM); + + if ((sv = (sem_t) calloc (1,sizeof (*sv))) == NULL) + return sem_result (ENOMEM); + + sv->value = value; + if (pthread_mutex_init (&sv->vlock, NULL) != 0) + { + free (sv); + return sem_result (ENOSPC); + } + if ((sv->s = CreateSemaphore (NULL, 0, SEM_VALUE_MAX, NULL)) == NULL) + { + pthread_mutex_destroy (&sv->vlock); + free (sv); + return sem_result (ENOSPC); + } + + sv->valid = LIFE_SEM; + *sem = sv; + return 0; +} + +int +sem_destroy (sem_t *sem) +{ + int r; + _sem_t *sv = NULL; + + if (!sem || (sv = *sem) == NULL) + return sem_result (EINVAL); + if ((r = pthread_mutex_lock (&sv->vlock)) != 0) + return sem_result (r); + +#if 0 + /* We don't wait for destroying a semaphore ... + or? */ + if (sv->value < 0) + { + pthread_mutex_unlock (&sv->vlock); + return sem_result (EBUSY); + } +#endif + + if (!CloseHandle (sv->s)) + { + pthread_mutex_unlock (&sv->vlock); + return sem_result (EINVAL); + } + *sem = NULL; + sv->value = SEM_VALUE_MAX; + pthread_mutex_unlock(&sv->vlock); + Sleep (0); + while (pthread_mutex_destroy (&sv->vlock) == EBUSY) + Sleep (0); + sv->valid = DEAD_SEM; + free (sv); + return 0; +} + +static int +sem_std_enter (sem_t *sem,_sem_t **svp, int do_test) +{ + int r; + _sem_t *sv; + + if (do_test) + pthread_testcancel (); + if (!sem) + return sem_result (EINVAL); + sv = *sem; + if (sv == NULL) + return sem_result (EINVAL); + + if ((r = pthread_mutex_lock (&sv->vlock)) != 0) + return sem_result (r); + + if (*sem == NULL) + { + pthread_mutex_unlock(&sv->vlock); + return sem_result (EINVAL); + } + *svp = sv; + return 0; +} + +int +sem_trywait (sem_t *sem) +{ + _sem_t *sv; + + if (sem_std_enter (sem, &sv, 0) != 0) + return -1; + if (sv->value <= 0) + { + pthread_mutex_unlock (&sv->vlock); + return sem_result (EAGAIN); + } + sv->value--; + pthread_mutex_unlock (&sv->vlock); + + return 0; +} + +struct sSemTimedWait +{ + sem_t *p; + int *ret; +}; + +static void +clean_wait_sem (void *s) +{ + struct sSemTimedWait *p = (struct sSemTimedWait *) s; + _sem_t *sv = NULL; + + if (sem_std_enter (p->p, &sv, 0) != 0) + return; + + if (WaitForSingleObject (sv->s, 0) != WAIT_OBJECT_0) + InterlockedIncrement (&sv->value); + else if (p->ret) + p->ret[0] = 0; + pthread_mutex_unlock (&sv->vlock); +} + +int +sem_wait (sem_t *sem) +{ + long cur_v; + int ret = 0; + _sem_t *sv; + HANDLE semh; + struct sSemTimedWait arg; + + if (sem_std_enter (sem, &sv, 1) != 0) + return -1; + + arg.ret = &ret; + arg.p = sem; + InterlockedDecrement (&sv->value); + cur_v = sv->value; + semh = sv->s; + pthread_mutex_unlock (&sv->vlock); + + if (cur_v >= 0) + return 0; + else + { + pthread_cleanup_push (clean_wait_sem, (void *) &arg); + ret = do_sema_b_wait_intern (semh, 2, INFINITE); + pthread_cleanup_pop (ret); + if (ret == EINVAL) + return 0; + } + + if (!ret) + return 0; + + return sem_result (ret); +} + +/* Internal version which always uses `struct _timespec64`. */ +static int +__sem_timedwait (sem_t *sem, const struct _timespec64 *t) +{ + int cur_v, ret = 0; + DWORD dwr; + HANDLE semh; + _sem_t *sv; + struct sSemTimedWait arg; + + if (!t) + return sem_wait (sem); + dwr = dwMilliSecs(_pthread_rel_time_in_ms (t)); + + if (sem_std_enter (sem, &sv, 1) != 0) + return -1; + + arg.ret = &ret; + arg.p = sem; + InterlockedDecrement (&sv->value); + cur_v = sv->value; + semh = sv->s; + pthread_mutex_unlock(&sv->vlock); + + if (cur_v >= 0) + return 0; + else + { + pthread_cleanup_push (clean_wait_sem, (void *) &arg); + ret = do_sema_b_wait_intern (semh, 2, dwr); + pthread_cleanup_pop (ret); + if (ret == EINVAL) + return 0; + } + + if (!ret) + return 0; + return sem_result (ret); +} + +int sem_timedwait64(sem_t *sem, const struct _timespec64 *t) +{ + return __sem_timedwait (sem, t); +} + +int sem_timedwait32(sem_t *sem, const struct _timespec32 *t) +{ + struct _timespec64 t64 = {0}; + + if (t != NULL) + { + t64.tv_sec = t->tv_sec; + t64.tv_nsec = t->tv_nsec; + } + + return __sem_timedwait (sem, t == NULL ? NULL : &t64); +} + +int +sem_post (sem_t *sem) +{ + _sem_t *sv; + + if (sem_std_enter (sem, &sv, 0) != 0) + return -1; + + if (sv->value >= SEM_VALUE_MAX) + { + pthread_mutex_unlock (&sv->vlock); + return sem_result (ERANGE); + } + InterlockedIncrement (&sv->value); + if (sv->value > 0 || ReleaseSemaphore (sv->s, 1, NULL)) + { + pthread_mutex_unlock (&sv->vlock); + return 0; + } + InterlockedDecrement (&sv->value); + pthread_mutex_unlock (&sv->vlock); + + return sem_result (EINVAL); +} + +int +sem_post_multiple (sem_t *sem, int count) +{ + int waiters_count; + _sem_t *sv; + + if (count <= 0) + return sem_result (EINVAL); + if (sem_std_enter (sem, &sv, 0) != 0) + return -1; + + if (sv->value > (SEM_VALUE_MAX - count)) + { + pthread_mutex_unlock (&sv->vlock); + return sem_result (ERANGE); + } + waiters_count = -sv->value; + sv->value += count; + /*InterlockedExchangeAdd((long*)&sv->value, (long) count);*/ + if (waiters_count <= 0 + || ReleaseSemaphore (sv->s, + (waiters_count < count ? waiters_count + : count), NULL)) + { + pthread_mutex_unlock(&sv->vlock); + return 0; + } + /*InterlockedExchangeAdd((long*)&sv->value, -((long) count));*/ + sv->value -= count; + pthread_mutex_unlock(&sv->vlock); + return sem_result (EINVAL); +} + +sem_t * +sem_open (const char *name, int oflag, mode_t mode, unsigned int value) +{ + sem_result (ENOSYS); + return NULL; +} + +int +sem_close (sem_t *sem) +{ + return sem_result (ENOSYS); +} + +int +sem_unlink (const char *name) +{ + return sem_result (ENOSYS); +} + +int +sem_getvalue (sem_t *sem, int *sval) +{ + _sem_t *sv; + int r; + + if (!sval) + return sem_result (EINVAL); + + if (!sem || (sv = *sem) == NULL) + return sem_result (EINVAL); + + if ((r = pthread_mutex_lock (&sv->vlock)) != 0) + return sem_result (r); + if (*sem == NULL) + { + pthread_mutex_unlock (&sv->vlock); + return sem_result (EINVAL); + } + + *sval = (int) sv->value; + pthread_mutex_unlock (&sv->vlock); + return 0; +} diff --git a/vendor/winpthreads/src/sem.h b/vendor/winpthreads/src/sem.h new file mode 100644 index 00000000..ddfc00a6 --- /dev/null +++ b/vendor/winpthreads/src/sem.h @@ -0,0 +1,38 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifndef WIN_SEM +#define WIN_SEM + +#define LIFE_SEM 0xBAB1F00D +#define DEAD_SEM 0xDEADBEEF + +typedef struct _sem_t _sem_t; +struct _sem_t +{ + unsigned int valid; + HANDLE s; + volatile long value; + pthread_mutex_t vlock; +}; + +#endif /* WIN_SEM */ diff --git a/vendor/winpthreads/src/spinlock.c b/vendor/winpthreads/src/spinlock.c new file mode 100644 index 00000000..c6648bc8 --- /dev/null +++ b/vendor/winpthreads/src/spinlock.c @@ -0,0 +1,371 @@ +/* + Copyright (c) 2026 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#define WIN32_LEAN_AND_MEAN +#include + +/* public header files */ +#include "pthread.h" +/* internal header files */ +#include "misc.h" + +/** + * Reference: + * + * pthread_spin_init(), pthread_spin_destroy(): + * + * + * pthread_spin_lock(), pthread_spin_trylock(): + * + * + * pthread_spin_unlock(): + * + */ + +/** + * Internal structure pointed to by `pthread_spinlock_t` objects. + */ +typedef struct { + /** + * This value is used to indicate that spin lock has no owner. + */ +#define THREAD_ID_NO_OWNER ((DWORD)-1) + /** + * ID of the thread which holds the lock. + */ + DWORD ThreadId; + /** + * Auto-reset event. + * + * This event is created in signaled state, which means any thread can + * `WaitForSingleObject` on it; only one thread will be released at a time, + * after which this event will be automatically put into non-signaled state. + * + * The released thread owns the lock; it unlocks it by calling `SetEvent` on + * this event, which puts this event into signaled state, allowing system + * release another thread which waits on it. + * + * The cycle repeats until this event is destroyed. + */ + HANDLE Event; +} WinpthreadsSpinlock; + +#define WINPTHREADS_SPINLOCK_INITIALIZER ((WinpthreadsSpinlock *) PTHREAD_SPINLOCK_INITIALIZER) + +/** + * Obtain pointer to `WinpthreadsSpinlock` structure pointed to by `lock`. + * + * If `lock` points to statically initialzied `pthread_spinlock_t` object, + * allocate `WinpthreadsSpinlock` structure and store its address in `*lock`. + * + * On success, stores pointer to `WinpthreadsSpinlock` structure in `*spinlock`. + * + * Returns zero on success and an error-code on failure. + */ +static WINPTHREADS_INLINE int WinpthreadsSpinlockGet (pthread_spinlock_t *lock, WinpthreadsSpinlock **spinlock) +{ + WinpthreadsSpinlock **pSpinlock = (WinpthreadsSpinlock **) lock; + + /** + * POSIX: + * + * If an implementation detects that the value specified by the lock argument + * to pthread_spin_*() does not refer to an initialized spin lock object, + * it is recommended that the function should fail and report an [EINVAL] error. + */ + if (unlikely (pSpinlock == NULL)) { + return EINVAL; + } + + if (*pSpinlock == WINPTHREADS_SPINLOCK_INITIALIZER) { + /** + * We need to avoid race condition when more than one thread calls + * `pthread_spin_[try]lock` on statically initialized `pthread_spinlock_t` + * at the same time. + * + * Store newly initialized spinlock in `spinlock`, which points to local + * variable supplied by the caller, and then store it in `*lock`. + * + * If some other thread was faster then us, destroy newly created spinlock + * and use spinlock pointed to by `lock`. + */ + int error_code = pthread_spin_init ((pthread_spinlock_t *) spinlock, PTHREAD_PROCESS_PRIVATE); + + if (error_code) { + return error_code; + } + + WinpthreadsSpinlock *oldLock = (WinpthreadsSpinlock *) InterlockedCompareExchangePointer ( + (void **) pSpinlock, *spinlock, WINPTHREADS_SPINLOCK_INITIALIZER + ); + + /** + * Some other thread was faster than us. + */ + if (unlikely (oldLock != WINPTHREADS_SPINLOCK_INITIALIZER)) { + pthread_spin_destroy ((pthread_spinlock_t *) spinlock); + *spinlock = oldLock; + } + } else { + *spinlock = *pSpinlock; + } + + if (unlikely (*spinlock == NULL)) { + return EINVAL; + } + + return 0; +} + +int pthread_spin_init (pthread_spinlock_t *lock, int pshared) +{ + WinpthreadsSpinlock **pSpinlock = (WinpthreadsSpinlock **) lock; + + if (unlikely (pSpinlock == NULL)) { + return EINVAL; + } + + if (unlikely (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED)) { + return EINVAL; + } + + if (unlikely (pshared == PTHREAD_PROCESS_SHARED)) { + return ENOSYS; + } + + WinpthreadsSpinlock *wSpinlock = calloc (1, sizeof (WinpthreadsSpinlock)); + + /** + * The pthread_spin_init() function shall fail if: + * + * [ENOMEM] + * Insufficient memory exists to initialize the lock. + */ + if (wSpinlock == NULL) { + return ENOMEM; + } + + wSpinlock->ThreadId = THREAD_ID_NO_OWNER; + wSpinlock->Event = CreateEventA (NULL, FALSE, TRUE, NULL); + + /** + * The pthread_spin_init() function shall fail if: + * + * [EAGAIN] + * The system lacks the necessary resources to initialize another spin lock. + */ + if (wSpinlock->Event == NULL) { + free (wSpinlock); + return EAGAIN; + } + + *pSpinlock = wSpinlock; + + return 0; +} + +int pthread_spin_destroy (pthread_spinlock_t *lock) +{ + WinpthreadsSpinlock **pSpinlock = (WinpthreadsSpinlock **) lock; + + /** + * POSIX: + * + * If an implementation detects that the value specified by the lock argument + * to pthread_spin_destroy() does not refer to an initialized spin lock object, + * it is recommended that the function should fail and report an [EINVAL] error. + */ + if (unlikely (pSpinlock == NULL)) { + return EINVAL; + } + + /** + * If `lock` points to statically initialized `pthread_spinlock_t` object, + * this will immediately invalidate it, minimizing the window for + * `pthread_spin_lock` and `pthread_spin_trylock` to attempt using it. + */ + WinpthreadsSpinlock *wSpinlock = InterlockedCompareExchangePointer ( + (void **) pSpinlock, NULL, WINPTHREADS_SPINLOCK_INITIALIZER + ); + + if (unlikely (wSpinlock == NULL)) { + return EINVAL; + } + + if (unlikely (wSpinlock == WINPTHREADS_SPINLOCK_INITIALIZER)) { + return 0; + } + + switch (_pthread_wait_for_single_object (wSpinlock->Event, 0)) { + /** + * `wSpinlock->Event` was in signaled state, which means it was unlocked; + * we are holding the lock now which prevents other threads from locking it. + */ + case WAIT_OBJECT_0: + break; + /** + * `wSpinlock->Event` was in not-signaled state, which means some thread + * holds the lock. + * + * POSIX: + * + * If an implementation detects that the value specified by the lock argument + * to pthread_spin_destroy() or pthread_spin_init() refers to a locked spin + * lock object, or detects that the value specified by the lock argument to + * pthread_spin_init() refers to an already initialized spin lock object, + * it is recommended that the function should fail and report an [EBUSY] error. + */ + case WAIT_TIMEOUT: + return EBUSY; + default: + return EINVAL; + } + + /** + * Invalidate `pthread_spinlock_t` object pointed by `lock`. + */ + InterlockedExchangePointer ((void **) pSpinlock, NULL); + + CloseHandle (wSpinlock->Event); + free (wSpinlock); + + return 0; +} + +int pthread_spin_lock (pthread_spinlock_t *lock) +{ + WinpthreadsSpinlock *wSpinlock = NULL; + + int error_code = WinpthreadsSpinlockGet (lock, &wSpinlock); + + if (error_code) { + return error_code; + } + + DWORD threadId = GetCurrentThreadId (); + + /** + * The pthread_spin_lock() function may fail if: + * + * [EDEADLK] + * A deadlock condition was detected. + */ + if (unlikely (wSpinlock->ThreadId == threadId)) { + return EDEADLK; + } + + switch (_pthread_wait_for_single_object (wSpinlock->Event, INFINITE)) { + /** + * We are holding the lock now and `wSpinlock->Event` was reset to + * non-signaled state. + */ + case WAIT_OBJECT_0: + break; + default: + return EINVAL; + } + + wSpinlock->ThreadId = threadId; + + return 0; +} + +int pthread_spin_trylock (pthread_spinlock_t *lock) +{ + WinpthreadsSpinlock *wSpinlock = NULL; + + int error_code = WinpthreadsSpinlockGet (lock, &wSpinlock); + + if (error_code) { + return error_code; + } + + /** + * Unlike `pthread_spin_lock`, no deadlock can occur even if calling thread + * owns the lock. + */ + switch (_pthread_wait_for_single_object (wSpinlock->Event, 0)) { + /** + * `wSpinlock->Event` was in signaled state, which means it was unlocked; + * we are holding the lock now and `wSpinlock->Event` was reset to + * non-signaled state. + */ + case WAIT_OBJECT_0: + break; + /** + * `wSpinlock->Event` was in non-signaled state, which means some thread + * holds the lock. + * + * The pthread_spin_trylock() function shall fail if: + * + * [EBUSY] + * A thread currently holds the lock. + */ + case WAIT_TIMEOUT: + return EBUSY; + default: + return EINVAL; + } + + wSpinlock->ThreadId = GetCurrentThreadId (); + + return 0; +} + +int pthread_spin_unlock (pthread_spinlock_t *lock) +{ + WinpthreadsSpinlock *wSpinlock = NULL; + + int error_code = WinpthreadsSpinlockGet (lock, &wSpinlock); + + if (error_code) { + return error_code; + } + + DWORD threadId = GetCurrentThreadId (); + + /** + * POSIX: + * + * If an implementation detects that the value specified by the lock argument + * to pthread_spin_unlock() refers to a spin lock object for which the + * current thread does not hold the lock, it is recommended that the function + * should fail and report an [EPERM] error. + */ + if (unlikely (wSpinlock->ThreadId != threadId)) { + return EPERM; + } + + wSpinlock->ThreadId = THREAD_ID_NO_OWNER; + + if (!SetEvent (wSpinlock->Event)) { + return EINVAL; + } + + return 0; +} diff --git a/vendor/winpthreads/src/thread.c b/vendor/winpthreads/src/thread.c new file mode 100644 index 00000000..47637042 --- /dev/null +++ b/vendor/winpthreads/src/thread.c @@ -0,0 +1,1910 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#if defined(__arm__) || defined(__aarch64__) +/* We use setjmp/longjmp through asynchronous function calls via + * SetThreadContext below. This makes unwinding from longjmp not + * work reliably; therefore use a version of setjmp/longjmp that doesn't + * rely on SEH. */ +#define __USE_MINGW_SETJMP_NON_SEH +#endif + +#define __LARGE_MBSTATE_T + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include + +#define WINPTHREAD_THREAD_DECL WINPTHREAD_API + +/* public header files */ +#include "pthread.h" +/* internal header files */ +#include "misc.h" +#include "thread.h" + +static _pthread_v *__pthread_self_lite (void); + +void (**_pthread_key_dest)(void *) = NULL; + +static volatile long _pthread_cancelling; +static int _pthread_concur; + +/* FIXME Will default to zero as needed */ +static pthread_once_t _pthread_tls_once; +static DWORD _pthread_tls = 0xffffffff; + +static pthread_rwlock_t _pthread_key_lock = PTHREAD_RWLOCK_INITIALIZER; +static unsigned long _pthread_key_max=0L; +static unsigned long _pthread_key_sch=0L; + +static _pthread_v *pthr_root = NULL, *pthr_last = NULL; +static pthread_mutex_t mtx_pthr_locked = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; + +static __pthread_idlist *idList = NULL; +static size_t idListCnt = 0; +static size_t idListMax = 0; +static pthread_t idListNextId = 0; + +#if defined(__SEH__) && (!defined(__clang__) || __clang_major__ >= 7) +#define SEH_INLINE_ASM +#ifdef __arm__ +#define ASM_EXCEPT "%%except" +#else +#define ASM_EXCEPT "@except" +#endif +#endif + +#if !defined(_MSC_VER) && (defined(__i386__) || defined(SEH_INLINE_ASM)) +static EXCEPTION_DISPOSITION __cdecl +SetThreadName_SEH (EXCEPTION_RECORD *ExceptionRecord, PVOID EstablisherFrame, CONTEXT *ContextRecord, PVOID DispatcherContext) +{ + /* Do not be confused with VEH handlers and CRT except filters which returns LONG value with UPPER_CASE constants. + * SEH handlers like this one return value from EXCEPTION_DISPOSITION enum which has CamelCase constants. + * UPPER_CASE EXCEPTION_CONTINUE_SEARCH and CamelCase ExceptionContinueSearch are different constants. + */ + if (!(ExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING) && + !(ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE) && + ExceptionRecord->ExceptionCode == EXCEPTION_SET_THREAD_NAME) + return ExceptionContinueExecution; + + return ExceptionContinueSearch; +} +#endif + +typedef struct _THREADNAME_INFO +{ + DWORD dwType; /* must be 0x1000 */ + LPCSTR szName; /* pointer to name (in user addr space) */ + DWORD dwThreadID; /* thread ID (-1=caller thread) */ + DWORD dwFlags; /* reserved for future use, must be zero */ +} THREADNAME_INFO; + +#if !defined(_MSC_VER) && !defined(__i386__) && defined(SEH_INLINE_ASM) +WINPTHREADS_ATTRIBUTE((noinline)) /* required for asm .seh_handler directive */ +#endif +static void +SetThreadName (DWORD dwThreadID, LPCSTR szThreadName) +{ + THREADNAME_INFO info; + DWORD infosize; + + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + + infosize = sizeof (info) / sizeof (ULONG_PTR); + + /* Exception has to be processed otherwise it will crash the process. */ + +#if defined(_MSC_VER) + /* msvc supports __try / __except syntax, so use it */ + __try + { + RaiseException (EXCEPTION_SET_THREAD_NAME, 0, infosize, (ULONG_PTR *)&info); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + } +#else + /* gcc does not support __try / __except syntax, so manually register SEH handler */ +#if defined(__i386__) + /* On 32-bit x86 is SEH handler registered and unregistered at runtime */ + EXCEPTION_REGISTRATION_RECORD exception_record = { + .Next = (EXCEPTION_REGISTRATION_RECORD *) __readfsdword (0), /* current SEH handler */ + .Handler = (PEXCEPTION_ROUTINE)(void*) SetThreadName_SEH, + }; + __writefsdword (0, (DWORD) &exception_record); /* register our SEH handler */ + RaiseException (EXCEPTION_SET_THREAD_NAME, 0, infosize, (ULONG_PTR *) &info); + __writefsdword (0, (DWORD) exception_record.Next); /* unregister our SEH handler */ +#elif defined(SEH_INLINE_ASM) + /* On other platforms SEH handlers are registered at compile time. + Assembler directive .seh_handler statically register SEH handler for + the whole current function. It does not matter at which line is this + directive called. It always applies for the whole function, so also + for code before the directive itself. As this function does not do + anything else, we can register our SEH handler for the whole function. + This function has to be marked as noinline to ensure that the SEH + handler would not be registered for a caller. + */ + asm volatile (".seh_handler %c0, " ASM_EXCEPT :: "i" (SetThreadName_SEH)); + RaiseException (EXCEPTION_SET_THREAD_NAME, 0, infosize, (ULONG_PTR *) &info); +#else + /* Other compilers / platforms do not provide SEH support */ +#endif +#endif +} + +/* Search the list idList for an element with identifier ID. If + found, its associated _pthread_v pointer is returned, otherwise + NULL. + NOTE: This method is not locked. */ +static struct _pthread_v * +__pthread_get_pointer (pthread_t id) +{ + size_t l, r, p; + if (!idListCnt) + return NULL; + if (idListCnt == 1) + return (idList[0].id == id ? idList[0].ptr : NULL); + l = 0; r = idListCnt - 1; + while (l <= r) + { + p = (l + r) >> 1; + if (idList[p].id == id) + return idList[p].ptr; + else if (idList[p].id > id) + { + if (p == l) + return NULL; + r = p - 1; + } + else + { + l = p + 1; + } + } + + return NULL; +} + +static void +__pth_remove_use_for_key (pthread_key_t key) +{ + int i; + + pthread_mutex_lock (&mtx_pthr_locked); + for (i = 0; i < idListCnt; i++) + { + if (idList[i].ptr != NULL + && idList[i].ptr->keyval != NULL + && key < idList[i].ptr->keymax) + { + idList[i].ptr->keyval[key] = NULL; + idList[i].ptr->keyval_set[key] = 0; + } + } + pthread_mutex_unlock (&mtx_pthr_locked); +} + +/* Search the list idList for an element with identifier ID. If + found, its associated _pthread_v pointer is returned, otherwise + NULL. + NOTE: This method uses lock mtx_pthr_locked. */ +struct _pthread_v * +__pth_gpointer_locked (pthread_t id) +{ + struct _pthread_v *ret; + if (!id) + return NULL; + pthread_mutex_lock (&mtx_pthr_locked); + ret = __pthread_get_pointer (id); + pthread_mutex_unlock (&mtx_pthr_locked); + return ret; +} + +/* Registers in the list idList an element with _pthread_v pointer + and creates and unique identifier ID. If successful created the + ID of this element is returned, otherwise on failure zero ID gets + returned. + NOTE: This method is not locked. */ +static pthread_t +__pthread_register_pointer (struct _pthread_v *ptr) +{ + __pthread_idlist *e; + size_t i; + + if (!ptr) + return 0; + /* Check if a resize of list is necessary. */ + if (idListCnt >= idListMax) + { + if (!idListCnt) + { + e = (__pthread_idlist *) malloc (sizeof (__pthread_idlist) * 16); + if (!e) + return 0; + idListMax = 16; + idList = e; + } + else + { + e = (__pthread_idlist *) realloc (idList, sizeof (__pthread_idlist) * (idListMax + 16)); + if (!e) + return 0; + idListMax += 16; + idList = e; + } + } + do + { + ++idListNextId; + /* If two MSB are set we reset to id 1. We need to check here bits + to avoid gcc's no-overflow issue on increment. Additionally we + need to handle different size of pthread_t on 32-bit/64-bit. */ + if ((idListNextId & ( ((pthread_t) 1) << ((sizeof (pthread_t) * 8) - 2))) != 0) + idListNextId = 1; + } + while (idListNextId == 0 || __pthread_get_pointer (idListNextId)); + /* We assume insert at end of list. */ + i = idListCnt; + if (i != 0) + { + /* Find position we can actual insert sorted. */ + while (i > 0 && idList[i - 1].id > idListNextId) + --i; + if (i != idListCnt) + memmove (&idList[i + 1], &idList[i], sizeof (__pthread_idlist) * (idListCnt - i)); + } + idList[i].id = idListNextId; + idList[i].ptr = ptr; + ++idListCnt; + return idListNextId; +} + +/* Deregisters in the list idList an element with identifier ID and + returns its _pthread_v pointer on success. Otherwise NULL is returned. + NOTE: This method is not locked. */ +static struct _pthread_v * +__pthread_deregister_pointer (pthread_t id) +{ + size_t l, r, p; + if (!idListCnt) + return NULL; + l = 0; r = idListCnt - 1; + while (l <= r) + { + p = (l + r) >> 1; + if (idList[p].id == id) + { + struct _pthread_v *ret = idList[p].ptr; + p++; + if (p < idListCnt) + memmove (&idList[p - 1], &idList[p], sizeof (__pthread_idlist) * (idListCnt - p)); + --idListCnt; + /* Is this last element in list then free list. */ + if (idListCnt == 0) + { + free (idList); + idListCnt = idListMax = 0; + } + return ret; + } + else if (idList[p].id > id) + { + if (p == l) + return NULL; + r = p - 1; + } + else + { + l = p + 1; + } + } + return NULL; +} + +/* Save a _pthread_v element for reuse in pool. */ +static void +push_pthread_mem (_pthread_v *sv) +{ + if (!sv || sv->next != NULL) + return; + pthread_mutex_lock (&mtx_pthr_locked); + if (sv->x != 0) + __pthread_deregister_pointer (sv->x); + if (sv->keyval) + free (sv->keyval); + if (sv->keyval_set) + free (sv->keyval_set); + if (sv->thread_name) + free (sv->thread_name); + memset (sv, 0, sizeof(struct _pthread_v)); + if (pthr_last == NULL) + pthr_root = pthr_last = sv; + else + { + pthr_last->next = sv; + pthr_last = sv; + } + pthread_mutex_unlock (&mtx_pthr_locked); +} + +/* Get a _pthread_v element from pool, or allocate it. + Note the unique identifier is created for the element here, too. */ +static _pthread_v * +pop_pthread_mem (void) +{ + _pthread_v *r = NULL; + + pthread_mutex_lock (&mtx_pthr_locked); + if ((r = pthr_root) == NULL) + { + if ((r = (_pthread_v *)calloc (1,sizeof(struct _pthread_v))) != NULL) + { + r->x = __pthread_register_pointer (r); + if (r->x == 0) + { + free (r); + r = NULL; + } + } + pthread_mutex_unlock (&mtx_pthr_locked); + return r; + } + r->x = __pthread_register_pointer (r); + if (r->x == 0) + r = NULL; + else + { + if((pthr_root = r->next) == NULL) + pthr_last = NULL; + + r->next = NULL; + } + pthread_mutex_unlock (&mtx_pthr_locked); + return r; +} + +/* Free memory consumed in _pthread_v pointer pool. */ +static void +free_pthread_mem (void) +{ +#if 0 + _pthread_v *t; + + pthread_mutex_lock (&mtx_pthr_locked); + t = pthr_root; + while (t != NULL) + { + _pthread_v *sv = t; + t = t->next; + if (sv->x != 0 && sv->ended == 0 && sv->valid != DEAD_THREAD) + { + pthread_mutex_unlock (&mtx_pthr_locked); + pthread_cancel (t->x); + Sleep (0); + pthread_mutex_lock (&mtx_pthr_locked); + t = pthr_root; + continue; + } + else if (sv->x != 0 && sv->valid != DEAD_THREAD) + { + pthread_mutex_unlock (&mtx_pthr_locked); + Sleep (0); + pthread_mutex_lock (&mtx_pthr_locked); + continue; + } + if (sv->x != 0) + __pthread_deregister_pointer (sv->x); + sv->x = 0; + free (sv); + pthr_root = t; + } + pthread_mutex_unlock (&mtx_pthr_locked); +#endif + return; +} + +static void +replace_spin_keys (pthread_spinlock_t *old, pthread_spinlock_t new) +{ + if (old == NULL) + return; + + if (EPERM == pthread_spin_destroy (old)) + { +#define THREADERR "Error cleaning up spin_keys for thread %lu.\n" + char threaderr[sizeof(THREADERR) + 8] = { 0 }; + snprintf(threaderr, sizeof(threaderr), THREADERR, GetCurrentThreadId()); +#undef THREADERR + OutputDebugStringA (threaderr); + abort (); + } + + *old = new; +} + +/* Hook for TLS-based deregistration/registration of thread. */ +static void WINAPI +__dyn_tls_pthread (HANDLE hDllHandle, DWORD dwReason, LPVOID lpreserved) +{ + _pthread_v *t = NULL; + pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER; + + if (dwReason == DLL_PROCESS_DETACH) + { + free_pthread_mem (); + } + else if (dwReason == DLL_PROCESS_ATTACH) + { + } + else if (dwReason == DLL_THREAD_DETACH) + { + if (_pthread_tls != 0xffffffff) + t = (_pthread_v *)TlsGetValue(_pthread_tls); + if (t && t->thread_noposix != 0) + { + _pthread_cleanup_dest (t->x); + if (t->h != NULL) + { + CloseHandle (t->h); + if (t->evStart) + CloseHandle (t->evStart); + t->evStart = NULL; + t->h = NULL; + } + pthread_mutex_destroy (&t->p_clock); + replace_spin_keys (&t->spin_keys, new_spin_keys); + push_pthread_mem (t); + t = NULL; + TlsSetValue (_pthread_tls, t); + } + else if (t && t->ended == 0) + { + if (t->evStart) + CloseHandle(t->evStart); + t->evStart = NULL; + t->ended = 1; + _pthread_cleanup_dest (t->x); + if ((t->p_state & PTHREAD_CREATE_DETACHED) == PTHREAD_CREATE_DETACHED) + { + t->valid = DEAD_THREAD; + if (t->h != NULL) + CloseHandle (t->h); + t->h = NULL; + pthread_mutex_destroy(&t->p_clock); + replace_spin_keys (&t->spin_keys, new_spin_keys); + push_pthread_mem (t); + t = NULL; + TlsSetValue (_pthread_tls, t); + return; + } + pthread_mutex_destroy(&t->p_clock); + replace_spin_keys (&t->spin_keys, new_spin_keys); + } + else if (t) + { + if (t->evStart) + CloseHandle (t->evStart); + t->evStart = NULL; + pthread_mutex_destroy (&t->p_clock); + replace_spin_keys (&t->spin_keys, new_spin_keys); + } + } +} + +/* TLS-runtime section variable. */ + +/* Force a reference to _tls_used to make the linker create the TLS + * directory if it's not already there. (e.g. if __declspec(thread) + * is not used). + * Force a reference to __xl_f to prevent whole program optimization + * from discarding the variable. */ +#if defined(__GNUC__) +extern const IMAGE_TLS_DIRECTORY _tls_used; +static __attribute__((used)) const IMAGE_TLS_DIRECTORY *const _include_tls_used = &_tls_used; +#elif defined(_MSC_VER) +/* On x86, symbols are prefixed with an underscore. */ +# if defined(_M_IX86) +# pragma comment(linker, "/include:__tls_used") +# pragma comment(linker, "/include:___xl_f") +# else +# pragma comment(linker, "/include:_tls_used") +# pragma comment(linker, "/include:__xl_f") +# endif + +/* .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK + * pointers. Pick an arbitrary location for our callback. + * + * See VC\...\crt\src\vcruntime\tlssup.cpp for reference. */ + +# pragma section(".CRT$XLF", long, read) +#endif + +#if defined(__GNUC__) +static __attribute__((used)) +#endif +WINPTHREADS_ATTRIBUTE((WINPTHREADS_SECTION(".CRT$XLF"))) +const PIMAGE_TLS_CALLBACK __xl_f = __dyn_tls_pthread; + +/* Internal collect-once structure. */ +typedef struct collect_once_t { + pthread_once_t *o; + pthread_mutex_t m; + int count; + struct collect_once_t *next; +} collect_once_t; + +static collect_once_t *once_obj = NULL; + +static pthread_spinlock_t once_global = PTHREAD_SPINLOCK_INITIALIZER; + +static collect_once_t * +enterOnceObject (pthread_once_t *o) +{ + collect_once_t *c, *p = NULL; + pthread_spin_lock (&once_global); + c = once_obj; + while (c != NULL && c->o != o) + { + c = (p = c)->next; + } + if (!c) + { + c = (collect_once_t *) calloc(1,sizeof(collect_once_t)); + c->o = o; + c->count = 1; + if (!p) + once_obj = c; + else + p->next = c; + pthread_mutex_init(&c->m, NULL); + } + else + c->count += 1; + pthread_spin_unlock (&once_global); + return c; +} + +static void +leaveOnceObject (collect_once_t *c) +{ + collect_once_t *h, *p = NULL; + if (!c) + return; + pthread_spin_lock (&once_global); + h = once_obj; + while (h != NULL && c != h) + h = (p = h)->next; + + if (h) + { + c->count -= 1; + if (c->count == 0) + { + pthread_mutex_destroy(&c->m); + if (!p) + once_obj = c->next; + else + p->next = c->next; + free (c); + } + } + else + fprintf(stderr, "%p not found?!?!\n", (void *) c); + pthread_spin_unlock (&once_global); +} + +static void +_pthread_once_cleanup (void *o) +{ + collect_once_t *co = (collect_once_t *) o; + pthread_mutex_unlock (&co->m); + leaveOnceObject (co); +} + +static int +_pthread_once_raw (pthread_once_t *o, void (*func)(void)) +{ + collect_once_t *co; + long state = *o; + + CHECK_PTR(o); + CHECK_PTR(func); + + if (state == 1) + return 0; + co = enterOnceObject(o); + pthread_mutex_lock(&co->m); + if (*o == 0) + { + func(); + *o = 1; + } + else if (*o != 1) + fprintf (stderr," once %p is %ld\n", (void *) o, (long) *o); + pthread_mutex_unlock(&co->m); + leaveOnceObject(co); + + /* Done */ + return 0; +} + +/* Unimplemented. */ +void * +pthread_timechange_handler_np(void *dummy) +{ + return NULL; +} + +/* Compatibility routine for pthread-win32. It waits for ellapse of + interval and additionally checks for possible thread-cancelation. */ +static int +__pthread_delay_np (const struct _timespec64 *interval) +{ + DWORD to = (!interval ? 0 : dwMilliSecs (_pthread_time_in_ms_from_timespec (interval))); + struct _pthread_v *s = __pthread_self_lite (); + + if (!to) + { + pthread_testcancel (); + Sleep (0); + pthread_testcancel (); + return 0; + } + pthread_testcancel (); + if (s->evStart) + _pthread_wait_for_single_object (s->evStart, to); + else + Sleep (to); + pthread_testcancel (); + return 0; +} + +int +pthread_delay64_np (const struct _timespec64 *interval) +{ + return __pthread_delay_np(interval); +} + +int +pthread_delay32_np (const struct _timespec32 *interval) +{ + struct _timespec64 interval64 = {.tv_sec = interval->tv_sec, .tv_nsec = interval->tv_nsec}; + return __pthread_delay_np(&interval64); +} + +int +_pthread_delay_np_ms (DWORD to) +{ + struct _pthread_v *s = __pthread_self_lite (); + + if (!to) + { + pthread_testcancel (); + Sleep (0); + pthread_testcancel (); + return 0; + } + pthread_testcancel (); + if (s->evStart) + _pthread_wait_for_single_object (s->evStart, to); + else + Sleep (to); + pthread_testcancel (); + return 0; +} + +/* Compatibility routine for pthread-win32. It returns the + amount of available CPUs on system. */ +int +pthread_num_processors_np(void) +{ + int r = 0; + DWORD_PTR ProcessAffinityMask, SystemAffinityMask; + + if (GetProcessAffinityMask(GetCurrentProcess(), &ProcessAffinityMask, &SystemAffinityMask)) + { + for(; ProcessAffinityMask != 0; ProcessAffinityMask >>= 1) + r += (ProcessAffinityMask & 1) != 0; + } + /* assume at least 1 */ + return r ? r : 1; +} + +/* Compatiblity routine for pthread-win32. Allows to set amount of used + CPUs for process. */ +int +pthread_set_num_processors_np(int n) +{ + DWORD_PTR ProcessAffinityMask, ProcessNewAffinityMask = 0, SystemAffinityMask; + int r = 0; + /* need at least 1 */ + n = n ? n : 1; + if (GetProcessAffinityMask (GetCurrentProcess (), &ProcessAffinityMask, &SystemAffinityMask)) + { + for (; ProcessAffinityMask != 0; ProcessAffinityMask >>= 1) + { + ProcessNewAffinityMask <<= 1; + if ((ProcessAffinityMask & 1) != 0 && r < n) + { + ProcessNewAffinityMask |= 1; + r++; + } + } + SetProcessAffinityMask (GetCurrentProcess (),ProcessNewAffinityMask); + } + return r; +} + +int +pthread_once (pthread_once_t *o, void (*func)(void)) +{ + collect_once_t *co; + long state = *o; + + CHECK_PTR(o); + CHECK_PTR(func); + + if (state == 1) + return 0; + co = enterOnceObject(o); + pthread_mutex_lock(&co->m); + if (*o == 0) + { + pthread_cleanup_push(_pthread_once_cleanup, co); + func(); + pthread_cleanup_pop(0); + *o = 1; + } + else if (*o != 1) + fprintf (stderr," once %p is %ld\n", (void *) o, (long) *o); + pthread_mutex_unlock(&co->m); + leaveOnceObject(co); + + return 0; +} + +int +pthread_key_create (pthread_key_t *key, void (* dest)(void *)) +{ + unsigned int i; + long nmax; + void (**d)(void *); + + if (!key) + return EINVAL; + + pthread_rwlock_wrlock (&_pthread_key_lock); + + for (i = _pthread_key_sch; i < _pthread_key_max; i++) + { + if (!_pthread_key_dest[i]) + { + *key = i; + if (dest) + _pthread_key_dest[i] = dest; + else + _pthread_key_dest[i] = (void(*)(void *))1; + pthread_rwlock_unlock (&_pthread_key_lock); + return 0; + } + } + + for (i = 0; i < _pthread_key_sch; i++) + { + if (!_pthread_key_dest[i]) + { + *key = i; + if (dest) + _pthread_key_dest[i] = dest; + else + _pthread_key_dest[i] = (void(*)(void *))1; + pthread_rwlock_unlock (&_pthread_key_lock); + + return 0; + } + } + + if (_pthread_key_max == PTHREAD_KEYS_MAX) + { + pthread_rwlock_unlock(&_pthread_key_lock); + return ENOMEM; + } + + nmax = _pthread_key_max * 2; + if (nmax == 0) + nmax = _pthread_key_max + 1; + if (nmax > PTHREAD_KEYS_MAX) + nmax = PTHREAD_KEYS_MAX; + + /* No spare room anywhere */ + d = (void (__cdecl **)(void *))realloc(_pthread_key_dest, nmax * sizeof(*d)); + if (!d) + { + pthread_rwlock_unlock (&_pthread_key_lock); + return ENOMEM; + } + + /* Clear new region */ + memset ((void *) &d[_pthread_key_max], 0, (nmax-_pthread_key_max)*sizeof(void *)); + + /* Use new region */ + _pthread_key_dest = d; + _pthread_key_sch = _pthread_key_max + 1; + *key = _pthread_key_max; + _pthread_key_max = nmax; + + if (dest) + _pthread_key_dest[*key] = dest; + else + _pthread_key_dest[*key] = (void(*)(void *))1; + + pthread_rwlock_unlock (&_pthread_key_lock); + return 0; +} + +int +pthread_key_delete (pthread_key_t key) +{ + if (key >= _pthread_key_max || !_pthread_key_dest) + return EINVAL; + + pthread_rwlock_wrlock (&_pthread_key_lock); + + _pthread_key_dest[key] = NULL; + + /* Start next search from our location */ + if (_pthread_key_sch > key) + _pthread_key_sch = key; + /* So now we need to walk the complete list of threads + and remove key's reference for it. */ + __pth_remove_use_for_key (key); + + pthread_rwlock_unlock (&_pthread_key_lock); + return 0; +} + +void * +pthread_getspecific (pthread_key_t key) +{ + DWORD lasterr = GetLastError (); + void *r; + _pthread_v *t = __pthread_self_lite (); + pthread_spin_lock (&t->spin_keys); + r = (key >= t->keymax || t->keyval_set[key] == 0 ? NULL : t->keyval[key]); + pthread_spin_unlock (&t->spin_keys); + SetLastError (lasterr); + return r; +} + +int +pthread_setspecific (pthread_key_t key, const void *value) +{ + DWORD lasterr = GetLastError (); + _pthread_v *t = __pthread_self_lite (); + + pthread_spin_lock (&t->spin_keys); + + if (key >= t->keymax) + { + int keymax = (key + 1); + void **kv; + unsigned char *kv_set; + + kv = (void **) realloc (t->keyval, keymax * sizeof (void *)); + + if (!kv) + { + pthread_spin_unlock (&t->spin_keys); + return ENOMEM; + } + kv_set = (unsigned char *) realloc (t->keyval_set, keymax); + if (!kv_set) + { + pthread_spin_unlock (&t->spin_keys); + return ENOMEM; + } + + /* Clear new region */ + memset (&kv[t->keymax], 0, (keymax - t->keymax)*sizeof(void *)); + memset (&kv_set[t->keymax], 0, (keymax - t->keymax)); + + t->keyval = kv; + t->keyval_set = kv_set; + t->keymax = keymax; + } + + t->keyval[key] = (void *) value; + t->keyval_set[key] = 1; + pthread_spin_unlock (&t->spin_keys); + SetLastError (lasterr); + + return 0; +} + +int +pthread_equal (pthread_t t1, pthread_t t2) +{ + return (t1 == t2); +} + +void +pthread_tls_init (void) +{ + _pthread_tls = TlsAlloc(); + + /* Cannot continue if out of indexes */ + if (_pthread_tls == TLS_OUT_OF_INDEXES) + abort(); +} + +void +_pthread_cleanup_dest (pthread_t t) +{ + _pthread_v *tv; + unsigned int j; + int i; + + if (!t) + return; + tv = __pth_gpointer_locked (t); + if (!tv) + return; + + for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) + { + int flag = 0; + + pthread_spin_lock (&tv->spin_keys); + for (i = tv->keymax - 1; i >= 0; i--) + { + void *val = tv->keyval[i]; + + if (tv->keyval_set[i]) + { + pthread_rwlock_rdlock (&_pthread_key_lock); + if ((uintptr_t) _pthread_key_dest[i] > 1) + { + /* Call destructor */ + tv->keyval[i] = NULL; + tv->keyval_set[i] = 0; + pthread_spin_unlock (&tv->spin_keys); + _pthread_key_dest[i](val); + pthread_spin_lock (&tv->spin_keys); + flag = 1; + } + else + { + tv->keyval[i] = NULL; + tv->keyval_set[i] = 0; + } + pthread_rwlock_unlock(&_pthread_key_lock); + } + } + pthread_spin_unlock (&tv->spin_keys); + /* Nothing to do? */ + if (!flag) + return; + } +} + +static _pthread_v * +__pthread_self_lite (void) +{ + _pthread_v *t; + pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER; + + _pthread_once_raw (&_pthread_tls_once, pthread_tls_init); + + t = (_pthread_v *) TlsGetValue (_pthread_tls); + if (t) + return t; + /* Main thread? */ + t = (struct _pthread_v *) pop_pthread_mem (); + + /* If cannot initialize main thread, then the only thing we can do is return null pthread_t */ + if (!__xl_f || !t) + return 0; + + t->p_state = PTHREAD_DEFAULT_ATTR /*| PTHREAD_CREATE_DETACHED*/; + t->tid = GetCurrentThreadId(); + t->evStart = CreateEvent (NULL, 1, 0, NULL); + t->p_clock = PTHREAD_MUTEX_INITIALIZER; + replace_spin_keys (&t->spin_keys, new_spin_keys); + t->sched_pol = SCHED_OTHER; + t->h = NULL; //GetCurrentThread(); + if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &t->h, 0, FALSE, DUPLICATE_SAME_ACCESS)) + abort (); + t->sched.sched_priority = GetThreadPriority(t->h); + t->ended = 0; + t->thread_noposix = 1; + + /* Save for later */ + if (!TlsSetValue(_pthread_tls, t)) + abort (); + return t; +} + +pthread_t +pthread_self (void) +{ + _pthread_v *t = __pthread_self_lite (); + + if (!t) + return 0; + return t->x; +} + +/* Internal helper for getting event handle of thread T. */ +void * +pthread_getevent (void) +{ + _pthread_v *t = __pthread_self_lite (); + return (!t ? NULL : t->evStart); +} + +/* Internal helper for getting thread handle of thread T. */ +void * +pthread_gethandle (pthread_t t) +{ + struct _pthread_v *tv = __pth_gpointer_locked (t); + return (!tv ? NULL : tv->h); +} + +/* Internal helper for getting pointer of clean of current thread. */ +struct _pthread_cleanup ** +pthread_getclean (void) +{ + struct _pthread_v *t = __pthread_self_lite (); + if (!t) return NULL; + return &t->clean; +} + +int +pthread_get_concurrency (int *val) +{ + *val = _pthread_concur; + return 0; +} + +int +pthread_set_concurrency (int val) +{ + _pthread_concur = val; + return 0; +} + +void +pthread_exit (void *res) +{ + _pthread_v *t = NULL; + unsigned rslt = (unsigned) ((intptr_t) res); + struct _pthread_v *id = __pthread_self_lite (); + + id->ret_arg = res; + + _pthread_cleanup_dest (id->x); + if (id->thread_noposix == 0) + longjmp(id->jb, 1); + + /* Make sure we free ourselves if we are detached */ + if ((t = (_pthread_v *)TlsGetValue(_pthread_tls)) != NULL) + { + if (!t->h) + { + t->valid = DEAD_THREAD; + if (t->evStart) + CloseHandle (t->evStart); + t->evStart = NULL; + rslt = (unsigned) (size_t) t->ret_arg; + push_pthread_mem(t); + t = NULL; + TlsSetValue (_pthread_tls, t); + } + else + { + rslt = (unsigned) (size_t) t->ret_arg; + t->ended = 1; + if (t->evStart) + CloseHandle (t->evStart); + t->evStart = NULL; + if ((t->p_state & PTHREAD_CREATE_DETACHED) == PTHREAD_CREATE_DETACHED) + { + t->valid = DEAD_THREAD; + CloseHandle (t->h); + t->h = NULL; + push_pthread_mem(t); + t = NULL; + TlsSetValue(_pthread_tls, t); + } + } + } + /* Time to die */ + _endthreadex(rslt); +} + +void +_pthread_invoke_cancel (void) +{ + _pthread_cleanup *pcup; + struct _pthread_v *se = __pthread_self_lite (); + se->in_cancel = 1; + _pthread_setnobreak (1); + InterlockedDecrement(&_pthread_cancelling); + + /* Call cancel queue */ + for (pcup = se->clean; pcup; pcup = pcup->next) + { + pcup->func((pthread_once_t *)pcup->arg); + } + + _pthread_setnobreak (0); + pthread_exit(PTHREAD_CANCELED); +} + +int +__pthread_shallcancel (void) +{ + struct _pthread_v *t; + if (!_pthread_cancelling) + return 0; + t = __pthread_self_lite (); + if (t == NULL) + return 0; + if (t->nobreak <= 0 && t->cancelled && (t->p_state & PTHREAD_CANCEL_ENABLE)) + return 1; + return 0; +} + +void +_pthread_setnobreak (int v) +{ + struct _pthread_v *t = __pthread_self_lite (); + if (t == NULL) + return; + if (v > 0) + InterlockedIncrement ((long*)&t->nobreak); + else + InterlockedDecrement((long*)&t->nobreak); +} + +void +pthread_testcancel (void) +{ + struct _pthread_v *self = __pthread_self_lite (); + + if (!self || self->in_cancel) + return; + if (!_pthread_cancelling) + return; + pthread_mutex_lock (&self->p_clock); + + if (self->cancelled && (self->p_state & PTHREAD_CANCEL_ENABLE) && self->nobreak <= 0) + { + self->in_cancel = 1; + self->p_state &= ~PTHREAD_CANCEL_ENABLE; + if (self->evStart) + ResetEvent (self->evStart); + pthread_mutex_unlock (&self->p_clock); + _pthread_invoke_cancel (); + } + pthread_mutex_unlock (&self->p_clock); +} + +int +pthread_cancel (pthread_t t) +{ + struct _pthread_v *tv = __pth_gpointer_locked (t); + + if (tv == NULL) + return ESRCH; + CHECK_OBJECT(tv, ESRCH); + /*if (tv->ended) return ESRCH;*/ + pthread_mutex_lock(&tv->p_clock); + if (pthread_equal(pthread_self(), t)) + { + if(tv->cancelled) + { + pthread_mutex_unlock(&tv->p_clock); + return (tv->in_cancel ? ESRCH : 0); + } + tv->cancelled = 1; + InterlockedIncrement(&_pthread_cancelling); + if(tv->evStart) SetEvent(tv->evStart); + if ((tv->p_state & PTHREAD_CANCEL_ASYNCHRONOUS) != 0 && (tv->p_state & PTHREAD_CANCEL_ENABLE) != 0) + { + tv->p_state &= ~PTHREAD_CANCEL_ENABLE; + tv->in_cancel = 1; + pthread_mutex_unlock(&tv->p_clock); + _pthread_invoke_cancel(); + } + else + pthread_mutex_unlock(&tv->p_clock); + return 0; + } + + if ((tv->p_state & PTHREAD_CANCEL_ASYNCHRONOUS) != 0 && (tv->p_state & PTHREAD_CANCEL_ENABLE) != 0) + { + /* Dangerous asynchronous cancelling */ + CONTEXT ctxt; + + if(tv->in_cancel) + { + pthread_mutex_unlock(&tv->p_clock); + return (tv->in_cancel ? ESRCH : 0); + } + /* Already done? */ + if(tv->cancelled || tv->in_cancel) + { + /* ??? pthread_mutex_unlock (&tv->p_clock); */ + return ESRCH; + } + + ctxt.ContextFlags = CONTEXT_CONTROL; + + SuspendThread (tv->h); + if (WaitForSingleObject (tv->h, 0) == WAIT_TIMEOUT) + { + GetThreadContext(tv->h, &ctxt); +#ifdef _M_X64 + ctxt.Rip = (uintptr_t) _pthread_invoke_cancel; +#elif defined(_M_IX86) + ctxt.Eip = (uintptr_t) _pthread_invoke_cancel; +#elif defined(_M_ARM) || defined(_M_ARM64) + ctxt.Pc = (uintptr_t) _pthread_invoke_cancel; +#else +#error Unsupported architecture +#endif + SetThreadContext (tv->h, &ctxt); + + /* Also try deferred Cancelling */ + tv->cancelled = 1; + tv->p_state &= ~PTHREAD_CANCEL_ENABLE; + tv->in_cancel = 1; + + /* Notify everyone to look */ + InterlockedIncrement (&_pthread_cancelling); + if (tv->evStart) + SetEvent (tv->evStart); + pthread_mutex_unlock (&tv->p_clock); + + ResumeThread (tv->h); + } + } + else + { + if (tv->cancelled == 0) + { + /* Safe deferred Cancelling */ + tv->cancelled = 1; + + /* Notify everyone to look */ + InterlockedIncrement (&_pthread_cancelling); + if (tv->evStart) + SetEvent (tv->evStart); + } + else + { + pthread_mutex_unlock (&tv->p_clock); + return (tv->in_cancel ? ESRCH : 0); + } + } + pthread_mutex_unlock (&tv->p_clock); + return 0; +} + +/* half-stubbed version as we don't really well support signals */ +int +pthread_kill (pthread_t t, int sig) +{ + struct _pthread_v *tv; + + pthread_mutex_lock (&mtx_pthr_locked); + tv = __pthread_get_pointer (t); + if (!tv || t != tv->x || tv->in_cancel || tv->ended || tv->h == NULL + || tv->h == INVALID_HANDLE_VALUE) + { + pthread_mutex_unlock (&mtx_pthr_locked); + return ESRCH; + } + pthread_mutex_unlock (&mtx_pthr_locked); + if (!sig) + return 0; + if (sig < SIGINT || sig > NSIG) + return EINVAL; + return pthread_cancel(t); +} + +unsigned +_pthread_get_state (const pthread_attr_t *attr, unsigned flag) +{ + return (attr->p_state & flag); +} + +int +_pthread_set_state (pthread_attr_t *attr, unsigned flag, unsigned val) +{ + if (~flag & val) + return EINVAL; + attr->p_state &= ~flag; + attr->p_state |= val; + + return 0; +} + +int +pthread_attr_init (pthread_attr_t *attr) +{ + memset (attr, 0, sizeof (pthread_attr_t)); + attr->p_state = PTHREAD_DEFAULT_ATTR; + attr->stack = NULL; + attr->s_size = 0; + return 0; +} + +int +pthread_attr_destroy (pthread_attr_t *attr) +{ + /* No need to do anything */ + memset (attr, 0, sizeof(pthread_attr_t)); + return 0; +} + +int +pthread_attr_setdetachstate (pthread_attr_t *a, int flag) +{ + return _pthread_set_state(a, PTHREAD_CREATE_DETACHED, flag); +} + +int +pthread_attr_getdetachstate (const pthread_attr_t *a, int *flag) +{ + *flag = _pthread_get_state(a, PTHREAD_CREATE_DETACHED); + return 0; +} + +int +pthread_attr_setinheritsched (pthread_attr_t *a, int flag) +{ + if (!a || (flag != PTHREAD_INHERIT_SCHED && flag != PTHREAD_EXPLICIT_SCHED)) + return EINVAL; + return _pthread_set_state(a, PTHREAD_INHERIT_SCHED, flag); +} + +int +pthread_attr_getinheritsched (const pthread_attr_t *a, int *flag) +{ + *flag = _pthread_get_state(a, PTHREAD_INHERIT_SCHED); + return 0; +} + +int +pthread_attr_setscope (pthread_attr_t *a, int flag) +{ + return _pthread_set_state(a, PTHREAD_SCOPE_SYSTEM, flag); +} + +int +pthread_attr_getscope (const pthread_attr_t *a, int *flag) +{ + *flag = _pthread_get_state(a, PTHREAD_SCOPE_SYSTEM); + return 0; +} + +int +pthread_attr_getstack (const pthread_attr_t *attr, void **stack, size_t *size) +{ + *stack = (char *) attr->stack - attr->s_size; + *size = attr->s_size; + return 0; +} + +int +pthread_attr_setstack (pthread_attr_t *attr, void *stack, size_t size) +{ + attr->s_size = size; + attr->stack = (char *) stack + size; + return 0; +} + +int +pthread_attr_getstackaddr (const pthread_attr_t *attr, void **stack) +{ + *stack = attr->stack; + return 0; +} + +int +pthread_attr_setstackaddr (pthread_attr_t *attr, void *stack) +{ + attr->stack = stack; + return 0; +} + +int +pthread_attr_getstacksize (const pthread_attr_t *attr, size_t *size) +{ + *size = attr->s_size; + return 0; +} + +int +pthread_attr_setstacksize (pthread_attr_t *attr, size_t size) +{ + attr->s_size = size; + return 0; +} + +static void +test_cancel_locked (pthread_t t) +{ + struct _pthread_v *tv = __pth_gpointer_locked (t); + + if (!tv || tv->in_cancel || tv->ended != 0 || (tv->p_state & PTHREAD_CANCEL_ENABLE) == 0) + return; + if ((tv->p_state & PTHREAD_CANCEL_ASYNCHRONOUS) == 0) + return; + if (WaitForSingleObject(tv->evStart, 0) != WAIT_OBJECT_0) + return; + pthread_mutex_unlock (&tv->p_clock); + _pthread_invoke_cancel(); +} + +int +pthread_setcancelstate (int state, int *oldstate) +{ + _pthread_v *t = __pthread_self_lite (); + + if (!t || (state & PTHREAD_CANCEL_ENABLE) != state) + return EINVAL; + + pthread_mutex_lock (&t->p_clock); + if (oldstate) + *oldstate = t->p_state & PTHREAD_CANCEL_ENABLE; + t->p_state &= ~PTHREAD_CANCEL_ENABLE; + t->p_state |= state; + test_cancel_locked (t->x); + pthread_mutex_unlock (&t->p_clock); + + return 0; +} + +int +pthread_setcanceltype (int type, int *oldtype) +{ + _pthread_v *t = __pthread_self_lite (); + + if (!t || (type & PTHREAD_CANCEL_ASYNCHRONOUS) != type) + return EINVAL; + + pthread_mutex_lock (&t->p_clock); + if (oldtype) + *oldtype = t->p_state & PTHREAD_CANCEL_ASYNCHRONOUS; + t->p_state &= ~PTHREAD_CANCEL_ASYNCHRONOUS; + t->p_state |= type; + test_cancel_locked (t->x); + pthread_mutex_unlock (&t->p_clock); + + return 0; +} + +void _fpreset (void); + +#if defined(__i386__) +/* Align ESP on 16-byte boundaries. */ +__attribute__((force_align_arg_pointer)) +#endif +unsigned __stdcall +pthread_create_wrapper (void *args) +{ + unsigned rslt = 0; + struct _pthread_v *tv = (struct _pthread_v *)args; + + _fpreset(); + + pthread_mutex_lock (&mtx_pthr_locked); + pthread_mutex_lock (&tv->p_clock); + _pthread_once_raw(&_pthread_tls_once, pthread_tls_init); + TlsSetValue(_pthread_tls, tv); + tv->tid = GetCurrentThreadId(); + pthread_mutex_unlock (&tv->p_clock); + + + if (!setjmp(tv->jb)) + { + intptr_t trslt = (intptr_t) 128; + pthread_mutex_unlock (&mtx_pthr_locked); + /* Call function and save return value */ + if (tv->func) + trslt = (intptr_t) tv->func(tv->ret_arg); + pthread_mutex_lock (&mtx_pthr_locked); + tv->ret_arg = (void*) trslt; + /* Clean up destructors */ + _pthread_cleanup_dest(tv->x); + } + else + pthread_mutex_lock (&mtx_pthr_locked); + + pthread_mutex_lock (&tv->p_clock); + rslt = (unsigned) (size_t) tv->ret_arg; + /* Make sure we free ourselves if we are detached */ + if (tv->evStart) + CloseHandle (tv->evStart); + tv->evStart = NULL; + if (!tv->h) + { + tv->valid = DEAD_THREAD; + pthread_mutex_unlock (&tv->p_clock); + pthread_mutex_destroy (&tv->p_clock); + push_pthread_mem (tv); + tv = NULL; + TlsSetValue (_pthread_tls, tv); + } + else + { + pthread_mutex_unlock (&tv->p_clock); + pthread_mutex_destroy (&tv->p_clock); + /* Reinitialise p_clock, since there may be attempts at + destroying it again in __dyn_tls_thread later on. */ + tv->p_clock = PTHREAD_MUTEX_INITIALIZER; + tv->ended = 1; + } + while (pthread_mutex_unlock (&mtx_pthr_locked) == 0) + Sleep (0); + _endthreadex (rslt); + return rslt; +} + +int +pthread_create (pthread_t *th, const pthread_attr_t *attr, void *(* func)(void *), void *arg) +{ + HANDLE thrd = NULL; + int redo = 0; + struct _pthread_v *tv; + unsigned int ssize = 0; + pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER; + unsigned thrAddr; /* Dummy variable to pass a valid location to _beginthreadex (Win98). */ + + if (attr && attr->s_size > UINT_MAX) + return EINVAL; + + if ((tv = pop_pthread_mem ()) == NULL) + return EAGAIN; + + if (th) + *th = tv->x; + + /* Save data in pthread_t */ + tv->ended = 0; + tv->ret_arg = arg; + tv->func = func; + tv->p_state = PTHREAD_DEFAULT_ATTR; + tv->h = INVALID_HANDLE_VALUE; + /* We retry it here a few times, as events are a limited resource ... */ + do + { + tv->evStart = CreateEvent (NULL, 1, 0, NULL); + if (tv->evStart != NULL) + break; + Sleep ((!redo ? 0 : 20)); + } + while (++redo <= 4); + + tv->p_clock = PTHREAD_MUTEX_INITIALIZER; + replace_spin_keys (&tv->spin_keys, new_spin_keys); + tv->valid = LIFE_THREAD; + tv->sched.sched_priority = THREAD_PRIORITY_NORMAL; + tv->sched_pol = SCHED_OTHER; + if (tv->evStart == NULL) + { + if (th) + memset (th, 0, sizeof (pthread_t)); + push_pthread_mem (tv); + return EAGAIN; + } + + if (attr) + { + int inh = 0; + tv->p_state = attr->p_state; + ssize = (unsigned int)attr->s_size; + pthread_attr_getinheritsched (attr, &inh); + if (inh) + { + tv->sched.sched_priority = __pthread_self_lite ()->sched.sched_priority; + } + else + tv->sched.sched_priority = attr->param.sched_priority; + } + + /* Make sure tv->h has value of INVALID_HANDLE_VALUE */ + _ReadWriteBarrier(); + + thrd = (HANDLE) _beginthreadex(NULL, ssize, pthread_create_wrapper, tv, 0x4/*CREATE_SUSPEND*/, &thrAddr); + if (thrd == INVALID_HANDLE_VALUE) + thrd = 0; + /* Failed */ + if (!thrd) + { + if (tv->evStart) + CloseHandle (tv->evStart); + pthread_mutex_destroy (&tv->p_clock); + replace_spin_keys (&tv->spin_keys, new_spin_keys); + tv->evStart = NULL; + tv->h = 0; + if (th) + memset (th, 0, sizeof (pthread_t)); + push_pthread_mem (tv); + return EAGAIN; + } + { + int pr = tv->sched.sched_priority; + if (pr <= THREAD_PRIORITY_IDLE) { + pr = THREAD_PRIORITY_IDLE; + } else if (pr <= THREAD_PRIORITY_LOWEST) { + pr = THREAD_PRIORITY_LOWEST; + } else if (pr >= THREAD_PRIORITY_TIME_CRITICAL) { + pr = THREAD_PRIORITY_TIME_CRITICAL; + } else if (pr >= THREAD_PRIORITY_HIGHEST) { + pr = THREAD_PRIORITY_HIGHEST; + } + SetThreadPriority (thrd, pr); + } + ResetEvent (tv->evStart); + if ((tv->p_state & PTHREAD_CREATE_DETACHED) != 0) + { + tv->h = 0; + ResumeThread (thrd); + CloseHandle (thrd); + } + else + { + tv->h = thrd; + ResumeThread (thrd); + } + Sleep (0); + return 0; +} + +int +pthread_join (pthread_t t, void **res) +{ + struct _pthread_v *tv = __pth_gpointer_locked (t); + pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER; + + CHECK_OBJECT(tv, ESRCH); + + if ((tv->p_state & PTHREAD_CREATE_DETACHED) != 0) + return EINVAL; + if (pthread_equal(pthread_self(), t)) + return EDEADLK; + + /* pthread_testcancel (); */ + if (tv->ended == 0 || (tv->h != NULL && tv->h != INVALID_HANDLE_VALUE)) + WaitForSingleObject (tv->h, INFINITE); + CloseHandle (tv->h); + if (tv->evStart) + CloseHandle (tv->evStart); + tv->evStart = NULL; + /* Obtain return value */ + if (res) + *res = tv->ret_arg; + pthread_mutex_destroy (&tv->p_clock); + replace_spin_keys (&tv->spin_keys, new_spin_keys); + push_pthread_mem (tv); + + return 0; +} + +int +_pthread_tryjoin (pthread_t t, void **res) +{ + struct _pthread_v *tv; + pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER; + + pthread_mutex_lock (&mtx_pthr_locked); + tv = __pthread_get_pointer (t); + + if (!tv || !TEST_HANDLE(tv->h)) + { + pthread_mutex_unlock (&mtx_pthr_locked); + return ESRCH; + } + + if ((tv->p_state & PTHREAD_CREATE_DETACHED) != 0) + { + pthread_mutex_unlock (&mtx_pthr_locked); + return EINVAL; + } + if (pthread_equal(pthread_self(), t)) + { + pthread_mutex_unlock (&mtx_pthr_locked); + return EDEADLK; + } + if(tv->ended == 0 && WaitForSingleObject(tv->h, 0)) + { + if (tv->ended == 0) + { + pthread_mutex_unlock (&mtx_pthr_locked); + /* pthread_testcancel (); */ + return EBUSY; + } + } + CloseHandle (tv->h); + if (tv->evStart) + CloseHandle (tv->evStart); + tv->evStart = NULL; + + /* Obtain return value */ + if (res) + *res = tv->ret_arg; + pthread_mutex_destroy (&tv->p_clock); + replace_spin_keys (&tv->spin_keys, new_spin_keys); + + push_pthread_mem (tv); + + pthread_mutex_unlock (&mtx_pthr_locked); + /* pthread_testcancel (); */ + return 0; +} + +int +pthread_detach (pthread_t t) +{ + int r = 0; + struct _pthread_v *tv = __pth_gpointer_locked (t); + HANDLE dw; + pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER; + + pthread_mutex_lock (&mtx_pthr_locked); + if (!tv || !TEST_HANDLE(tv->h)) + { + pthread_mutex_unlock (&mtx_pthr_locked); + return ESRCH; + } + if ((tv->p_state & PTHREAD_CREATE_DETACHED) != 0) + { + pthread_mutex_unlock (&mtx_pthr_locked); + return EINVAL; + } + /* if (tv->ended) r = ESRCH; */ + dw = tv->h; + tv->h = 0; + tv->p_state |= PTHREAD_CREATE_DETACHED; + _ReadWriteBarrier(); + if (dw) + { + CloseHandle (dw); + if (tv->ended) + { + if (tv->evStart) + CloseHandle (tv->evStart); + tv->evStart = NULL; + pthread_mutex_destroy (&tv->p_clock); + replace_spin_keys (&tv->spin_keys, new_spin_keys); + push_pthread_mem (tv); + } + } + pthread_mutex_unlock (&mtx_pthr_locked); + + return r; +} + +static int dummy_concurrency_level = 0; + +int +pthread_getconcurrency (void) +{ + return dummy_concurrency_level; +} + +int +pthread_setconcurrency (int new_level) +{ + dummy_concurrency_level = new_level; + return 0; +} + +int +pthread_setname_np (pthread_t thread, const char *name) +{ + struct _pthread_v *tv; + char *stored_name; + + if (name == NULL) + return EINVAL; + + tv = __pth_gpointer_locked (thread); + if (!tv || thread != tv->x || tv->in_cancel || tv->ended || tv->h == NULL + || tv->h == INVALID_HANDLE_VALUE) + return ESRCH; + + stored_name = strdup (name); + if (stored_name == NULL) + return ENOMEM; + + if (tv->thread_name != NULL) + free (tv->thread_name); + + tv->thread_name = stored_name; + SetThreadName (tv->tid, name); + + if (_pthread_set_thread_description != NULL) + { + mbstate_t mbs = {0}; + size_t required_size = mbsrtowcs(NULL, &name, 0, &mbs); + if (required_size != (size_t)-1) + { + wchar_t *wname = malloc((required_size + 1) * sizeof(wchar_t)); + if (wname != NULL) + { + mbsrtowcs(wname, &name, required_size + 1, &mbs); + _pthread_set_thread_description(tv->h, wname); + free(wname); + } + } + } + return 0; +} + +int +pthread_getname_np (pthread_t thread, char *name, size_t len) +{ + struct _pthread_v *tv; + size_t thread_name_len; + + if (name == NULL) + return EINVAL; + + tv = __pth_gpointer_locked (thread); + if (!tv || thread != tv->x || tv->in_cancel || tv->ended || tv->h == NULL + || tv->h == INVALID_HANDLE_VALUE) + return ESRCH; + + if (len < 1) + return ERANGE; + + if (tv->thread_name == NULL) + { + name[0] = '\0'; + return 0; + } + + thread_name_len = strlen (tv->thread_name); + if (thread_name_len >= len) + return ERANGE; + + memcpy (name, tv->thread_name, thread_name_len + 1); + return 0; +} diff --git a/vendor/winpthreads/src/thread.h b/vendor/winpthreads/src/thread.h new file mode 100644 index 00000000..90f9cd39 --- /dev/null +++ b/vendor/winpthreads/src/thread.h @@ -0,0 +1,76 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifndef WIN_PTHREAD_H +#define WIN_PTHREAD_H + +#include +/* internal header files */ +#include "rwlock.h" + +#define LIFE_THREAD 0xBAB1F00D +#define DEAD_THREAD 0xDEADBEEF +#define EXCEPTION_SET_THREAD_NAME ((DWORD) 0x406D1388) + +typedef struct _pthread_v _pthread_v; +struct _pthread_v +{ + unsigned int valid; + void *ret_arg; + void *(* func)(void *); + _pthread_cleanup *clean; + int nobreak; + HANDLE h; + HANDLE evStart; + pthread_mutex_t p_clock; + int cancelled : 2; + int in_cancel : 2; + int thread_noposix : 2; + unsigned int p_state; + unsigned int keymax; + void **keyval; + unsigned char *keyval_set; + char *thread_name; + pthread_spinlock_t spin_keys; + DWORD tid; + int rwlc; + pthread_rwlock_t rwlq[RWLS_PER_THREAD]; + int sched_pol; + int ended; + struct sched_param sched; + jmp_buf jb; + struct _pthread_v *next; + pthread_t x; /* Internal posix handle. */ +}; + +typedef struct __pthread_idlist { + struct _pthread_v *ptr; + pthread_t id; +} __pthread_idlist; + +int _pthread_tryjoin(pthread_t t, void **res); +void _pthread_setnobreak(int); +int __pthread_shallcancel(void); +WINPTHREAD_API struct _pthread_v * __pth_gpointer_locked (pthread_t id); +int _pthread_delay_np_ms (DWORD to); + +#endif diff --git a/vendor/winpthreads/src/version.rc b/vendor/winpthreads/src/version.rc new file mode 100644 index 00000000..19c2d1e7 --- /dev/null +++ b/vendor/winpthreads/src/version.rc @@ -0,0 +1,67 @@ +/* + Copyright (c) 2011 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include "wpth_ver.h" + +#if defined(__MINGW64__) +# define WPTH_VERSIONINFO_COMMENT "GNU C build -- MinGW-w64 64-bit" +#elif defined(__MINGW32__) +# define WPTH_VERSIONINFO_COMMENT "GNU C build -- MinGW-w64 32-bit" +#else +# define WPTH_VERSIONINFO_COMMENT "MSVC build" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION WPTH_VERSION + PRODUCTVERSION WPTH_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0 +#endif + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_DLL +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "POSIX WinThreads for Windows" + VALUE "ProductVersion", WPTH_VERSION_STRING + VALUE "FileVersion", WPTH_VERSION_STRING + VALUE "InternalName", "libwinpthread-" WPTH_VERSION_MAJOR_STRING ".dll" + VALUE "OriginalFilename", "libwinpthread-" WPTH_VERSION_MAJOR_STRING ".dll" + VALUE "CompanyName", "MinGW-W64 Project. All rights reserved." + VALUE "LegalCopyright", "Copyright (C) MinGW-W64 Project Members 2010-2023" + VALUE "Licence", "MIT AND BSD-3-Clause" + VALUE "Info", "https://www.mingw-w64.org/" + VALUE "Comment", WPTH_VERSIONINFO_COMMENT + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + diff --git a/vendor/winpthreads/src/wpth_ver.h b/vendor/winpthreads/src/wpth_ver.h new file mode 100644 index 00000000..ea36c1e1 --- /dev/null +++ b/vendor/winpthreads/src/wpth_ver.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __WPTHREADS_VERSION__ +#define __WPTHREADS_VERSION__ + +#define WPTH_VERSION 1,0,0,0 +#define WPTH_VERSION_STRING "1, 0, 0, 0" +#define WPTH_VERSION_MAJOR_STRING "1" + +#endif