MacOS and Windows ports.

This commit is contained in:
Scott Duensing 2026-07-05 00:46:03 -05:00
parent efc1c17d4a
commit fa51d577ab
43 changed files with 8732 additions and 129 deletions

3
.gitignore vendored
View file

@ -3,6 +3,9 @@
/bin/ /bin/
/lib/ /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 # 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. # trees (generated .c/.h, the mrbc tool, CMake cache) under their own build/ dirs.
/vendor/mruby/build/ /vendor/mruby/build/

View file

@ -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 > permissive binary can build without the MySQL backend -- drop
> `-DCALOG_WITH_MYSQL` and the `$(MYSQLARCH)` archive from the `bin/calog` link in > `-DCALOG_WITH_MYSQL` and the `$(MYSQLARCH)` archive from the `bin/calog` link in
> the Makefile -- and keep SQLite + PostgreSQL. > 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`.

View file

@ -50,21 +50,44 @@ LUAINC = -I$(LUADIR)/src
# is Unix-only anyway -- pthreads, sanitizers, setarch -- so the Windows branch just # 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.) # keeps the Lua define correct, it does not by itself make a Windows build work.)
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
PLATFORM = windows
LUAPLAT = LUAPLAT =
LUAPLATLIBS = LUAPLATLIBS =
DLLIB =
SOCKETLIBS = -lws2_32 -lwinmm
else else
UNAMES := $(shell uname -s) UNAMES := $(shell uname -s)
ifeq ($(UNAMES),Linux) ifeq ($(UNAMES),Linux)
PLATFORM = linux
LUAPLAT = -DLUA_USE_LINUX LUAPLAT = -DLUA_USE_LINUX
LUAPLATLIBS = -ldl LUAPLATLIBS = -ldl
DLLIB = -ldl
SOCKETLIBS =
else ifeq ($(UNAMES),Darwin) else ifeq ($(UNAMES),Darwin)
PLATFORM = darwin
LUAPLAT = -DLUA_USE_MACOSX LUAPLAT = -DLUA_USE_MACOSX
LUAPLATLIBS = LUAPLATLIBS =
DLLIB =
SOCKETLIBS =
else else
PLATFORM = posix
LUAPLAT = -DLUA_USE_POSIX LUAPLAT = -DLUA_USE_POSIX
LUAPLATLIBS = LUAPLATLIBS =
DLLIB = -ldl
SOCKETLIBS =
endif endif
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) LUAFLAGS = -std=c99 -w -g -O2 $(LUAPLAT)
LUASRC = $(filter-out $(LUADIR)/src/lua.c $(LUADIR)/src/luac.c, $(wildcard $(LUADIR)/src/*.c)) LUASRC = $(filter-out $(LUADIR)/src/lua.c $(LUADIR)/src/luac.c, $(wildcard $(LUADIR)/src/*.c))
LUAOBJ = $(patsubst $(LUADIR)/src/%.c,obj/%.o,$(LUASRC)) LUAOBJ = $(patsubst $(LUADIR)/src/%.c,obj/%.o,$(LUASRC))
@ -108,7 +131,7 @@ S7DIR = vendor/s7
S7INC = -I$(S7DIR) S7INC = -I$(S7DIR)
S7FLAGS = -std=c99 -w -g -O1 -D_GNU_SOURCE S7FLAGS = -std=c99 -w -g -O1 -D_GNU_SOURCE
S7OBJ = obj/s7.o S7OBJ = obj/s7.o
S7LIBS = -ldl -lm S7LIBS = $(DLLIB) -lm
# --- vendored Wren (C, amalgamated single file wren.c + wren.h; regenerate via # --- vendored Wren (C, amalgamated single file wren.c + wren.h; regenerate via
# util/generate_amalgamation.py from upstream). Links -lm. --- # util/generate_amalgamation.py from upstream). Links -lm. ---
@ -144,7 +167,7 @@ $(MRUBYLIB): $(MRUBYCONF)
TCLDIR = vendor/tcl TCLDIR = vendor/tcl
TCLINC = -I$(TCLDIR)/generic -I$(TCLDIR)/unix TCLINC = -I$(TCLDIR)/generic -I$(TCLDIR)/unix
TCLLIB = $(TCLDIR)/build/libtcl9.0.a TCLLIB = $(TCLDIR)/build/libtcl9.0.a
TCLLIBS = -ldl -lz -lpthread -lm TCLLIBS = $(DLLIB) -lz -lpthread -lm
$(TCLLIB): $(TCLLIB):
mkdir -p $(TCLDIR)/build && cd $(TCLDIR)/build && ../unix/configure --disable-shared >/dev/null && $(MAKE) libtcl9.0.a 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 ENETDIR = vendor/enet
ENETINC = -I$(ENETDIR)/include 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 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) 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 \ BINS = bin/testBroker bin/testLua bin/testMyBasic bin/testPolyglot bin/testActor \
bin/testEngineLua bin/testEngineMyBasic bin/testSquirrel bin/testEngineSquirrel bin/testJs bin/testEngineJs \ 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 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) $(CC) $(LDFLAGS) -o $@ $^ $(LUALIBS)
bin/testSquirrel: obj/testSquirrel.o lib/libcalog.a lib/libsquirrel.a | bin 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 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 bin/testJs: obj/testJs.o lib/libcalog.a lib/libquickjs.a | bin
$(CC) $(LDFLAGS) -o $@ $^ -lm $(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 \ 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/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 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 -------------------------------------------------------------- # ---- fully-static build --------------------------------------------------------------
# A self-contained calog executable with NO shared-library dependencies at runtime. The # 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 $@ $< $(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 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. # 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 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. # 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 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 bin/testJson: obj/testJson.o obj/calogJson.o lib/libcalog.a lib/liblua.a | bin
$(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) $(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) $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS)
bin/testCrypto: obj/testCrypto.o obj/calogCrypto.o lib/libcalog.a lib/liblua.a $(SSLARCH) | bin 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 bin/testKv: obj/testKv.o obj/calogKv.o lib/libcalog.a lib/liblua.a | bin
$(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS) $(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) $(CC) $(LDFLAGS) -pthread -o $@ $^ $(LUALIBS)
bin/testHttp: obj/testHttp.o obj/calogHttp.o lib/libcalog.a lib/liblua.a $(SSLARCH) | bin 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 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). # 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 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 # The SSH test spawns a throwaway unprivileged sshd (needs system sshd + ssh-keygen), so it is
# NOT part of `make test`. Run it explicitly. # NOT part of `make test`. Run it explicitly.
@ -559,7 +613,7 @@ tsansq: $(SQSRC) | bin obj
$(CC) $(TSANFLAGS) $(INC) $(SQDEF) $(SQINC) -o bin/testEngineSquirrelTsan \ $(CC) $(TSANFLAGS) $(INC) $(SQDEF) $(SQINC) -o bin/testEngineSquirrelTsan \
tests/testEngineSquirrel.c src/squirrel/squirrelEngine.c src/squirrel/squirrelAdapter.c \ tests/testEngineSquirrel.c src/squirrel/squirrelEngine.c src/squirrel/squirrelAdapter.c \
$(TSANCORE) \ $(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 setarch -R ./bin/testEngineSquirrelTsan
rm -f $(patsubst $(SQDIR)/squirrel/%.cpp,obj/%.tsan.o,$(SQSRC)) rm -f $(patsubst $(SQDIR)/squirrel/%.cpp,obj/%.tsan.o,$(SQSRC))
@ -651,4 +705,4 @@ clean:
-include $(wildcard obj/*.d) -include $(wildcard obj/*.d)
-include $(wildcard obj/rel/*.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

149
PORTING.md Normal file
View file

@ -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-<arch>-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.

View file

@ -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 - **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 (routed back to the engine that owns it); and you can hand a native to a script as a
callable value with `calogFnFromNative`. 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 - **Many runtimes.** Independent `CalogT` runtimes coexist in one process; one host
thread can drive several. thread can drive several.
- **Load by filename.** `calogContextLoad(calog, "config")` finds `config.lua` / - **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 packages). The core is warning-clean on gcc **and** clang (`-Wall -Wextra -Werror
-Wconversion -Wsign-conversion`) and runs clean under AddressSanitizer, UBSan, and -Wconversion -Wsign-conversion`) and runs clean under AddressSanitizer, UBSan, and
ThreadSanitizer. 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 ## Engines

View file

@ -11,21 +11,15 @@
#include "calogHttp.h" #include "calogHttp.h"
#include "calogInternal.h" #include "calogInternal.h"
#include "calogPlatform.h"
#include <errno.h>
#include <fcntl.h>
#include <limits.h> #include <limits.h>
#include <netdb.h>
#include <netinet/in.h>
#include <poll.h>
#include <signal.h> #include <signal.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <sys/socket.h>
#include <sys/time.h> #include <sys/time.h>
#include <unistd.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <openssl/x509v3.h> #include <openssl/x509v3.h>
@ -69,7 +63,7 @@ typedef struct HttpUrlT {
// requests certificate + hostname authentication for an https connection (default; an https // requests certificate + hostname authentication for an https connection (default; an https
// request can opt out with insecure=true). // request can opt out with insecure=true).
typedef struct HttpConnT { typedef struct HttpConnT {
int fd; CalogSocketT fd;
SSL *ssl; SSL *ssl;
SSL_CTX *ctx; SSL_CTX *ctx;
bool verify; bool verify;
@ -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 httpBufPutStr(HttpBufT *buf, const char *s);
static int32_t httpBuildResult(const char *raw, size_t rawLen, CalogValueT *result); static int32_t httpBuildResult(const char *raw, size_t rawLen, CalogValueT *result);
static void httpConnClose(HttpConnT *conn); 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 int32_t httpConnOpen(HttpConnT *conn, const HttpUrlT *url, CalogValueT *result);
static ssize_t httpConnRead(HttpConnT *conn, char *buf, size_t length); static ssize_t httpConnRead(HttpConnT *conn, char *buf, size_t length);
static int32_t httpConnWriteAll(HttpConnT *conn, const char *bytes, 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 const CalogValueT *httpResultHeader(const CalogValueT *result, const char *name);
static int32_t httpResultStatus(const CalogValueT *result); static int32_t httpResultStatus(const CalogValueT *result);
static bool httpSameOrigin(const HttpUrlT *a, const HttpUrlT *b); 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 int32_t httpTlsHandshake(HttpConnT *conn, const HttpUrlT *url, CalogValueT *result);
static int httpWriteAuthority(char *out, size_t cap, const char *scheme, const HttpUrlT *base); 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; conn->ctx = NULL;
} }
// SSL_set_fd installs a BIO_NOCLOSE socket BIO, so SSL_free never closes the fd -- we own it. // SSL_set_fd installs a BIO_NOCLOSE socket BIO, so SSL_free never closes the fd -- we own it.
if (conn->fd >= 0) { if (conn->fd != CALOG_INVALID_SOCKET) {
close(conn->fd); calogSockClose(conn->fd);
conn->fd = -1; conn->fd = CALOG_INVALID_SOCKET;
} }
} }
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) {
int flags;
int rc; int rc;
int err; int err;
socklen_t errLen; socklen_t errLen;
struct pollfd pfd; struct pollfd pfd;
flags = fcntl(fd, F_GETFL, 0); // A fresh socket starts blocking; drive a bounded connect by flipping it non-blocking, polling
if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { // 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 return connect(fd, addr, addrLen) == 0; // best effort: no bounded-connect fallback
} }
rc = connect(fd, addr, addrLen); rc = connect(fd, addr, addrLen);
if (rc == 0) { if (rc == 0) {
fcntl(fd, F_SETFL, flags); calogSockSetNonblock(fd, 0);
return true; return true;
} }
if (errno != EINPROGRESS) { if (!calogSockInProgress(calogSockErrno())) {
fcntl(fd, F_SETFL, flags); calogSockSetNonblock(fd, 0);
return false; return false;
} }
pfd.fd = fd; pfd.fd = fd;
pfd.events = POLLOUT; pfd.events = POLLOUT;
rc = poll(&pfd, 1, timeoutSec * 1000); rc = calogPoll(&pfd, 1, timeoutSec * 1000);
if (rc <= 0) { if (rc <= 0) {
fcntl(fd, F_SETFL, flags); calogSockSetNonblock(fd, 0);
return false; // timeout or poll error return false; // timeout or poll error
} }
errLen = sizeof(err); errLen = sizeof(err);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errLen) < 0 || err != 0) { if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errLen) < 0 || err != 0) {
fcntl(fd, F_SETFL, flags); calogSockSetNonblock(fd, 0);
return false; return false;
} }
fcntl(fd, F_SETFL, flags); calogSockSetNonblock(fd, 0);
return true; return true;
} }
@ -467,10 +461,10 @@ static int32_t httpConnOpen(HttpConnT *conn, const HttpUrlT *url, CalogValueT *r
struct addrinfo hints; struct addrinfo hints;
struct addrinfo *res; struct addrinfo *res;
struct addrinfo *rp; struct addrinfo *rp;
int fd; CalogSocketT fd;
int rc; int rc;
conn->fd = -1; conn->fd = CALOG_INVALID_SOCKET;
conn->ssl = NULL; conn->ssl = NULL;
conn->ctx = NULL; conn->ctx = NULL;
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
@ -480,21 +474,21 @@ static int32_t httpConnOpen(HttpConnT *conn, const HttpUrlT *url, CalogValueT *r
if (rc != 0) { if (rc != 0) {
return calogFail(result, calogErrArgE, gai_strerror(rc)); return calogFail(result, calogErrArgE, gai_strerror(rc));
} }
fd = -1; fd = CALOG_INVALID_SOCKET;
for (rp = res; rp != NULL; rp = rp->ai_next) { for (rp = res; rp != NULL; rp = rp->ai_next) {
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (fd < 0) { if (fd == CALOG_INVALID_SOCKET) {
continue; continue;
} }
httpSetTimeouts(fd, HTTP_TIMEOUT_SEC); 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; break;
} }
close(fd); calogSockClose(fd);
fd = -1; fd = CALOG_INVALID_SOCKET;
} }
freeaddrinfo(res); freeaddrinfo(res);
if (fd < 0) { if (fd == CALOG_INVALID_SOCKET) {
return calogFail(result, calogErrArgE, "http: could not connect to host"); return calogFail(result, calogErrArgE, "http: could not connect to host");
} }
conn->fd = fd; conn->fd = fd;
@ -532,7 +526,7 @@ static ssize_t httpConnRead(HttpConnT *conn, char *buf, size_t length) {
for (;;) { for (;;) {
ssize_t r; ssize_t r;
r = recv(conn->fd, buf, length, 0); r = recv(conn->fd, buf, length, 0);
if (r < 0 && errno == EINTR) { if (r < 0 && calogSockErrno() == EINTR) {
continue; continue;
} }
return r; return r;
@ -556,9 +550,9 @@ static int32_t httpConnWriteAll(HttpConnT *conn, const char *bytes, size_t lengt
sent += (size_t)w; sent += (size_t)w;
} else { } else {
ssize_t w; 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 (w < 0) {
if (errno == EINTR) { if (calogSockErrno() == EINTR) {
continue; continue;
} }
return calogErrArgE; 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; struct timeval tv;
tv.tv_sec = timeoutSec; tv.tv_sec = timeoutSec;
tv.tv_usec = 0; tv.tv_usec = 0;
// Best effort: if the kernel rejects these (unusual), the connection just falls back to no (void)setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));
// read/write deadline rather than failing the request outright. (void)setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof(tv));
(void)setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); #endif
(void)setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
} }

View file

@ -8,18 +8,13 @@
#include "calogHandle.h" #include "calogHandle.h"
#include "calogInternal.h" #include "calogInternal.h"
#include "calogPlatform.h"
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h> #include <pthread.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <enet/enet.h> #include <enet/enet.h>
@ -38,7 +33,7 @@
#define NET_PORT_MAX 65535 #define NET_PORT_MAX 65535
typedef struct NetSocketT { typedef struct NetSocketT {
int fd; CalogSocketT fd;
} NetSocketT; } NetSocketT;
// Process-wide network library state shared by every runtime that registers the natives. // 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 enetSend(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData);
static int32_t enetService(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 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 bool netPortOk(int64_t port);
static int netResolve(const char *host, uint16_t port, int socktype, bool passive, struct addrinfo **out); 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 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 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 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 tcpClose(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData);
static int32_t tcpConnect(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); pthread_mutex_lock(&gNetLibMutex);
if (gNetLib == NULL) { if (gNetLib == NULL) {
NetLibT *newLib; 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)); newLib = (NetLibT *)calloc(1, sizeof(*newLib));
if (newLib == NULL) { if (newLib == NULL) {
calogPlatformNetShutdown();
pthread_mutex_unlock(&gNetLibMutex); pthread_mutex_unlock(&gNetLibMutex);
return calogErrOomE; return calogErrOomE;
} }
newLib->handles = calogHandleTableCreate(); newLib->handles = calogHandleTableCreate();
if (newLib->handles == NULL) { if (newLib->handles == NULL) {
free(newLib); free(newLib);
calogPlatformNetShutdown();
pthread_mutex_unlock(&gNetLibMutex); pthread_mutex_unlock(&gNetLibMutex);
return calogErrOomE; return calogErrOomE;
} }
if (enet_initialize() != 0) { if (enet_initialize() != 0) {
calogHandleTableDestroy(newLib->handles, NULL); calogHandleTableDestroy(newLib->handles, NULL);
free(newLib); free(newLib);
calogPlatformNetShutdown();
pthread_mutex_unlock(&gNetLibMutex); pthread_mutex_unlock(&gNetLibMutex);
return calogErrOomE; return calogErrOomE;
} }
@ -159,6 +162,7 @@ void calogNetShutdown(void) {
if (gNetLib->refCount <= 0) { if (gNetLib->refCount <= 0) {
calogHandleTableDestroy(gNetLib->handles, netCloser); calogHandleTableDestroy(gNetLib->handles, netCloser);
enet_deinitialize(); enet_deinitialize();
calogPlatformNetShutdown();
free(gNetLib); free(gNetLib);
gNetLib = NULL; 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. // Create a socket bound to the given local port (0 = ephemeral), optionally listening.
// Returns calogOkE with *fdOut set, or an error with result populated. // 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 *res;
struct addrinfo *rp; struct addrinfo *rp;
int fd; CalogSocketT fd;
int rc; int rc;
int yes; int yes;
*fdOut = -1; *fdOut = CALOG_INVALID_SOCKET;
yes = 1; yes = 1;
rc = netResolve(NULL, port, socktype, true, &res); rc = netResolve(NULL, port, socktype, true, &res);
if (rc != 0) { if (rc != 0) {
return calogFail(result, calogErrArgE, gai_strerror(rc)); return calogFail(result, calogErrArgE, gai_strerror(rc));
} }
fd = -1; fd = CALOG_INVALID_SOCKET;
for (rp = res; rp != NULL; rp = rp->ai_next) { for (rp = res; rp != NULL; rp = rp->ai_next) {
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (fd < 0) { if (fd == CALOG_INVALID_SOCKET) {
continue; continue;
} }
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes));
if (bind(fd, rp->ai_addr, rp->ai_addrlen) == 0) { if (bind(fd, rp->ai_addr, (socklen_t)rp->ai_addrlen) == 0) {
break; break;
} }
close(fd); calogSockClose(fd);
fd = -1; fd = CALOG_INVALID_SOCKET;
} }
freeaddrinfo(res); freeaddrinfo(res);
if (fd < 0) { if (fd == CALOG_INVALID_SOCKET) {
return calogFail(result, calogErrArgE, "could not bind the requested port"); return calogFail(result, calogErrArgE, "could not bind the requested port");
} }
if (doListen && listen(fd, SOMAXCONN) != 0) { if (doListen && listen(fd, SOMAXCONN) != 0) {
int32_t status; int32_t status;
status = calogFail(result, calogErrArgE, strerror(errno)); status = calogFail(result, calogErrArgE, calogSockErrStr());
close(fd); calogSockClose(fd);
return status; return status;
} }
*fdOut = fd; *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 // Close the fd and release the NetSocketT. Shared by netSocketClose and the handle-table
// teardown path (netCloser). // teardown path (netCloser).
static void netSocketFree(NetSocketT *sock) { static void netSocketFree(NetSocketT *sock) {
close(sock->fd); calogSockClose(sock->fd);
free(sock); free(sock);
} }
// Wrap an open fd in a handle-table entry, transferring ownership. On failure the fd is // 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. // 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; NetSocketT *sock;
int64_t handle; int64_t handle;
sock = (NetSocketT *)malloc(sizeof(*sock)); sock = (NetSocketT *)malloc(sizeof(*sock));
if (sock == NULL) { if (sock == NULL) {
close(fd); calogSockClose(fd);
return calogFail(result, calogErrOomE, "out of memory"); return calogFail(result, calogErrOomE, "out of memory");
} }
sock->fd = fd; sock->fd = fd;
handle = calogHandleAdd(lib->handles, type, sock); handle = calogHandleAdd(lib->handles, type, sock);
if (handle == 0) { if (handle == 0) {
close(fd); calogSockClose(fd);
free(sock); free(sock);
return calogFail(result, calogErrOomE, "out of memory"); return calogFail(result, calogErrOomE, "out of memory");
} }
@ -301,7 +305,7 @@ 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) { static int32_t tcpAccept(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) {
NetLibT *lib; NetLibT *lib;
NetSocketT *listener; NetSocketT *listener;
int fd; CalogSocketT fd;
lib = (NetLibT *)userData; lib = (NetLibT *)userData;
calogValueNil(result); 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"); return calogFail(result, calogErrArgE, "tcpAccept: invalid listener handle");
} }
fd = accept(listener->fd, NULL, NULL); fd = accept(listener->fd, NULL, NULL);
if (fd < 0) { if (fd == CALOG_INVALID_SOCKET) {
return calogFail(result, calogErrArgE, strerror(errno)); return calogFail(result, calogErrArgE, calogSockErrStr());
} }
return netStore(lib, fd, NET_TYPE_TCP, result); 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; NetLibT *lib;
struct addrinfo *res; struct addrinfo *res;
struct addrinfo *rp; struct addrinfo *rp;
int fd; CalogSocketT fd;
int rc; int rc;
lib = (NetLibT *)userData; lib = (NetLibT *)userData;
@ -351,20 +355,20 @@ static int32_t tcpConnect(CalogValueT *args, int32_t argCount, CalogValueT *resu
if (rc != 0) { if (rc != 0) {
return calogFail(result, calogErrArgE, gai_strerror(rc)); return calogFail(result, calogErrArgE, gai_strerror(rc));
} }
fd = -1; fd = CALOG_INVALID_SOCKET;
for (rp = res; rp != NULL; rp = rp->ai_next) { for (rp = res; rp != NULL; rp = rp->ai_next) {
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (fd < 0) { if (fd == CALOG_INVALID_SOCKET) {
continue; continue;
} }
if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0) { if (connect(fd, rp->ai_addr, (socklen_t)rp->ai_addrlen) == 0) {
break; break;
} }
close(fd); calogSockClose(fd);
fd = -1; fd = CALOG_INVALID_SOCKET;
} }
freeaddrinfo(res); freeaddrinfo(res);
if (fd < 0) { if (fd == CALOG_INVALID_SOCKET) {
return calogFail(result, calogErrArgE, "tcpConnect: could not connect"); return calogFail(result, calogErrArgE, "tcpConnect: could not connect");
} }
return netStore(lib, fd, NET_TYPE_TCP, result); return netStore(lib, fd, NET_TYPE_TCP, result);
@ -373,7 +377,7 @@ static int32_t tcpConnect(CalogValueT *args, int32_t argCount, CalogValueT *resu
static int32_t tcpListen(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) { static int32_t tcpListen(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) {
NetLibT *lib; NetLibT *lib;
int fd; CalogSocketT fd;
int32_t status; int32_t status;
lib = (NetLibT *)userData; lib = (NetLibT *)userData;
@ -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); received = recv(sock->fd, buffer, (size_t)args[1].as.i, 0);
if (received < 0) { if (received < 0) {
status = calogFail(result, calogErrArgE, strerror(errno)); status = calogFail(result, calogErrArgE, calogSockErrStr());
free(buffer); free(buffer);
return status; return status;
} }
@ -449,9 +453,9 @@ static int32_t tcpSend(CalogValueT *args, int32_t argCount, CalogValueT *result,
total = 0; total = 0;
while (total < args[1].as.s.length) { while (total < args[1].as.s.length) {
ssize_t sent; 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) { if (sent < 0) {
return calogFail(result, calogErrArgE, strerror(errno)); return calogFail(result, calogErrArgE, calogSockErrStr());
} }
total += sent; total += sent;
} }
@ -474,7 +478,7 @@ static int32_t udpClose(CalogValueT *args, int32_t argCount, CalogValueT *result
static int32_t udpOpen(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) { static int32_t udpOpen(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) {
NetLibT *lib; NetLibT *lib;
int fd; CalogSocketT fd;
int32_t status; int32_t status;
lib = (NetLibT *)userData; lib = (NetLibT *)userData;
@ -523,7 +527,7 @@ static int32_t udpRecvFrom(CalogValueT *args, int32_t argCount, CalogValueT *res
fromLength = sizeof(from); fromLength = sizeof(from);
received = recvfrom(sock->fd, buffer, (size_t)args[1].as.i, 0, (struct sockaddr *)&from, &fromLength); received = recvfrom(sock->fd, buffer, (size_t)args[1].as.i, 0, (struct sockaddr *)&from, &fromLength);
if (received < 0) { if (received < 0) {
status = calogFail(result, calogErrArgE, strerror(errno)); status = calogFail(result, calogErrArgE, calogSockErrStr());
free(buffer); free(buffer);
return status; 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); 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); freeaddrinfo(res);
if (sent < 0) { if (sent < 0) {
return calogFail(result, calogErrArgE, strerror(errno)); return calogFail(result, calogErrArgE, calogSockErrStr());
} }
calogValueInt(result, (int64_t)sent); calogValueInt(result, (int64_t)sent);
return calogOkE; return calogOkE;

View file

@ -10,17 +10,17 @@
#include "calogHandle.h" #include "calogHandle.h"
#include "calogInternal.h" #include "calogInternal.h"
#include "calogPlatform.h"
#include <netdb.h>
#include <pthread.h> #include <pthread.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/select.h> #ifndef _WIN32
#include <sys/socket.h> #include <sys/select.h> // fd_set/select come from winsock2.h on Windows (via calogPlatform.h)
#include <unistd.h> #endif
#include <libssh2.h> #include <libssh2.h>
#include <libssh2_sftp.h> #include <libssh2_sftp.h>
@ -46,7 +46,7 @@
// SFTP subsystem. The handle is a plain process-wide entry with no owning context: any context // 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). // it is passed to may operate on it (see the threading notes in calogSsh.h).
typedef struct SshConnT { typedef struct SshConnT {
int sock; CalogSocketT sock;
LIBSSH2_SESSION *session; LIBSSH2_SESSION *session;
LIBSSH2_SFTP *sftp; LIBSSH2_SFTP *sftp;
} SshConnT; } 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 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 sshEnsureSftp(SshConnT *conn, CalogValueT *result);
static int32_t sshExec(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData); 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 sshReadChannel(void *handle, int streamId, char *buffer, size_t length);
static ssize_t sshReadSftpFile(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 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 sshSessionDestroy(LIBSSH2_SESSION *session, CalogSocketT sock, const char *reason);
static void sshWaitSocket(int sock, LIBSSH2_SESSION *session); static void sshWaitSocket(CalogSocketT sock, LIBSSH2_SESSION *session);
int32_t calogSshRegister(CalogT *calog) { int32_t calogSshRegister(CalogT *calog) {
pthread_mutex_lock(&gSshLibMutex); pthread_mutex_lock(&gSshLibMutex);
if (gSshLib == NULL) { if (gSshLib == NULL) {
SshLibT *lib; 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)); lib = (SshLibT *)calloc(1, sizeof(*lib));
if (lib == NULL) { if (lib == NULL) {
calogPlatformNetShutdown();
pthread_mutex_unlock(&gSshLibMutex); pthread_mutex_unlock(&gSshLibMutex);
return calogErrOomE; return calogErrOomE;
} }
lib->handles = calogHandleTableCreate(); lib->handles = calogHandleTableCreate();
if (lib->handles == NULL) { if (lib->handles == NULL) {
free(lib); free(lib);
calogPlatformNetShutdown();
pthread_mutex_unlock(&gSshLibMutex); pthread_mutex_unlock(&gSshLibMutex);
return calogErrOomE; return calogErrOomE;
} }
@ -115,6 +122,7 @@ int32_t calogSshRegister(CalogT *calog) {
if (libssh2_init(0) != 0) { if (libssh2_init(0) != 0) {
calogHandleTableDestroy(lib->handles, NULL); calogHandleTableDestroy(lib->handles, NULL);
free(lib); free(lib);
calogPlatformNetShutdown();
pthread_mutex_unlock(&gSshLibMutex); pthread_mutex_unlock(&gSshLibMutex);
return calogErrUnsupportedE; return calogErrUnsupportedE;
} }
@ -147,6 +155,7 @@ void calogSshShutdown(void) {
if (gSshLib->refCount <= 0) { if (gSshLib->refCount <= 0) {
calogHandleTableDestroy(gSshLib->handles, sshCloser); calogHandleTableDestroy(gSshLib->handles, sshCloser);
libssh2_exit(); libssh2_exit();
calogPlatformNetShutdown();
free(gSshLib); free(gSshLib);
gSshLib = NULL; gSshLib = NULL;
} }
@ -546,7 +555,7 @@ static int32_t sshConnect(CalogValueT *args, int32_t argCount, CalogValueT *resu
char portBuffer[8]; char portBuffer[8];
int64_t port; int64_t port;
int64_t handle; int64_t handle;
int fd; CalogSocketT fd;
int rc; int rc;
lib = (SshLibT *)userData; lib = (SshLibT *)userData;
@ -572,31 +581,31 @@ static int32_t sshConnect(CalogValueT *args, int32_t argCount, CalogValueT *resu
if (rc != 0) { if (rc != 0) {
return calogFail(result, calogErrArgE, gai_strerror(rc)); return calogFail(result, calogErrArgE, gai_strerror(rc));
} }
fd = -1; fd = CALOG_INVALID_SOCKET;
for (rp = res; rp != NULL; rp = rp->ai_next) { for (rp = res; rp != NULL; rp = rp->ai_next) {
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (fd < 0) { if (fd == CALOG_INVALID_SOCKET) {
continue; continue;
} }
if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0) { if (connect(fd, rp->ai_addr, (socklen_t)rp->ai_addrlen) == 0) {
break; break;
} }
close(fd); calogSockClose(fd);
fd = -1; fd = CALOG_INVALID_SOCKET;
} }
freeaddrinfo(res); freeaddrinfo(res);
if (fd < 0) { if (fd == CALOG_INVALID_SOCKET) {
return calogFail(result, calogErrArgE, "sshConnect: could not connect"); return calogFail(result, calogErrArgE, "sshConnect: could not connect");
} }
session = libssh2_session_init(); session = libssh2_session_init();
if (session == NULL) { if (session == NULL) {
close(fd); calogSockClose(fd);
return calogFail(result, calogErrOomE, "sshConnect: could not create a session"); return calogFail(result, calogErrOomE, "sshConnect: could not create a session");
} }
libssh2_session_set_blocking(session, 1); libssh2_session_set_blocking(session, 1);
if (libssh2_session_handshake(session, fd) != 0) { if (libssh2_session_handshake(session, fd) != 0) {
libssh2_session_free(session); libssh2_session_free(session);
close(fd); calogSockClose(fd);
return calogFail(result, calogErrArgE, "sshConnect: SSH handshake failed"); return calogFail(result, calogErrArgE, "sshConnect: SSH handshake failed");
} }
conn = (SshConnT *)calloc(1, sizeof(*conn)); 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 // 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 // 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. // 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 *outBuf;
char *errBuf; char *errBuf;
int64_t outCap; 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 // 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 // by sshConnDestroy and by sshConnect's post-handshake failure paths, before a SshConnT
// exists to own the session. // 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_disconnect(session, reason);
libssh2_session_free(session); libssh2_session_free(session);
close(sock); calogSockClose(sock);
} }
// Block the calling thread until the socket is ready in whatever direction libssh2 is // 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 // 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. // 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 fdSet;
fd_set *readSet; fd_set *readSet;
fd_set *writeSet; fd_set *writeSet;

View file

@ -470,6 +470,11 @@ int main(int argc, char **argv) {
signal(SIGINT, onSignal); signal(SIGINT, onSignal);
signal(SIGTERM, 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 // 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 // later error can retire exactly that context. calogContextEval keeps its own copy of the

100
src/calogPlatform.h Normal file
View file

@ -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 <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <stdio.h>
// 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 <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <poll.h>
#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

109
tools/crossBuild.sh Executable file
View file

@ -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 ]

57
vendor/winpthreads/COPYING vendored Normal file
View file

@ -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.
*/

26
vendor/winpthreads/NOTICE.calog vendored Normal file
View file

@ -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.

18
vendor/winpthreads/README vendored Normal file
View file

@ -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.

691
vendor/winpthreads/include/pthread.h vendored Normal file
View file

@ -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 <stddef.h>
#include <errno.h>
#include <sys/types.h>
#include <process.h>
#include <limits.h>
#include <signal.h>
#include <time.h>
#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 <pthread_unistd.h>
#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 */

View file

@ -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

View file

@ -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 */

View file

@ -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 <sys/timeb.h>
#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 */

View file

@ -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 <semaphore.h> 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 */

65
vendor/winpthreads/include/sched.h vendored Normal file
View file

@ -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 <stddef.h>
#include <errno.h>
#include <sys/types.h>
#include <process.h>
#include <limits.h>
#include <signal.h>
#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 */

75
vendor/winpthreads/include/semaphore.h vendored Normal file
View file

@ -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 <sys/timeb.h>
#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 */

254
vendor/winpthreads/src/barrier.c vendored Normal file
View file

@ -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 <assert.h>
#include <malloc.h>
#include <stdio.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
/* 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;
}

50
vendor/winpthreads/src/barrier.h vendored Normal file
View file

@ -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

354
vendor/winpthreads/src/clock.c vendored Normal file
View file

@ -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 <assert.h>
#include <errno.h>
#include <stdint.h>
#include <time.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#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:
* <pre>
* 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.
* </pre>
* @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:
* <pre>
* 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.
* </pre>
* @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;
}

717
vendor/winpthreads/src/cond.c vendored Normal file
View file

@ -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 <malloc.h>
#include <stdio.h>
#include <time.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#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;
}

57
vendor/winpthreads/src/cond.h vendored Normal file
View file

@ -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 <waiters_count_>. */
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

5
vendor/winpthreads/src/config.h vendored Normal file
View file

@ -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

View file

@ -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

574
vendor/winpthreads/src/libgcc/dll_math.c vendored Normal file
View file

@ -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 <sys/cdefs.h>
#include <sys/types.h>
#include <sys/limits.h>
#include <sys/syslimits.h>
*/
#include <limits.h>
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__)*/

234
vendor/winpthreads/src/misc.c vendored Normal file
View file

@ -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 <windows.h>
/* 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 (&timestamp))
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;
}

112
vendor/winpthreads/src/misc.h vendored Normal file
View file

@ -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 <limits.h>
/* 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

403
vendor/winpthreads/src/mutex.c vendored Normal file
View file

@ -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 <malloc.h>
#include <stdio.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#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;
}

109
vendor/winpthreads/src/nanosleep.c vendored Normal file
View file

@ -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 <assert.h>
#include <errno.h>
#include <time.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#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;
}

546
vendor/winpthreads/src/rwlock.c vendored Normal file
View file

@ -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 <assert.h>
#include <malloc.h>
#include <stdio.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#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;
}

46
vendor/winpthreads/src/rwlock.h vendored Normal file
View file

@ -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

226
vendor/winpthreads/src/sched.c vendored Normal file
View file

@ -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 <stdio.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
/* 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;
}

381
vendor/winpthreads/src/sem.c vendored Normal file
View file

@ -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 <malloc.h>
#include <stdio.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
/* 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;
}

38
vendor/winpthreads/src/sem.h vendored Normal file
View file

@ -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 */

371
vendor/winpthreads/src/spinlock.c vendored Normal file
View file

@ -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 <stdlib.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
/* public header files */
#include "pthread.h"
/* internal header files */
#include "misc.h"
/**
* Reference:
*
* pthread_spin_init(), pthread_spin_destroy():
* <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_spin_destroy.html>
*
* pthread_spin_lock(), pthread_spin_trylock():
* <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_spin_lock.html>
*
* pthread_spin_unlock():
* <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_spin_unlock.html>
*/
/**
* 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;
}

1910
vendor/winpthreads/src/thread.c vendored Normal file

File diff suppressed because it is too large Load diff

76
vendor/winpthreads/src/thread.h vendored Normal file
View file

@ -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 <setjmp.h>
/* 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

67
vendor/winpthreads/src/version.rc vendored Normal file
View file

@ -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 <winver.h>
#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

30
vendor/winpthreads/src/wpth_ver.h vendored Normal file
View file

@ -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