Implement support for POSIX thread cancellations

This change makes some miracle modifications to the System Five system
call support, which lets us have safe, correct, and atomic handling of
thread cancellations. It all turned out to be cheaper than anticipated
because it wasn't necessary to modify the system call veneers. We were
able to encode the cancellability of each system call into the magnums
found in libc/sysv/syscalls.sh. Since cancellations are so waq, we are
also supporting a lovely Musl Libc mask feature for raising ECANCELED.
This commit is contained in:
Justine Tunney 2022-11-04 01:04:43 -07:00
parent 37d40e087f
commit 2278327eba
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
145 changed files with 715 additions and 265 deletions

View file

@ -32,10 +32,10 @@
textwindows int sys_accept_nt(struct Fd *fd, void *addr, uint32_t *addrsize,
int flags) {
int64_t h;
int client, oflags;
int rc, client, oflags;
struct SockFd *sockfd, *sockfd2;
sockfd = (struct SockFd *)fd->extra;
if (_check_interrupts(true, g_fds.p)) return eintr();
if (_check_interrupts(true, g_fds.p)) return -1;
for (;;) {
if (!WSAPoll(&(struct sys_pollfd_nt){fd->handle, POLLIN}, 1,
__SIG_POLLING_INTERVAL_MS)) {

View file

@ -26,6 +26,7 @@
* @param out_addr will receive the remote address
* @param inout_addrsize provides and receives addr's byte length
* @return client fd which needs close(), or -1 w/ errno
* @cancellationpoint
* @asyncsignalsafe
* @restartable (unless SO_RCVTIMEO)
*/

View file

@ -36,6 +36,7 @@
* @param flags can have SOCK_{CLOEXEC,NONBLOCK}, which may apply to
* both the newly created socket and the server one
* @return client fd which needs close(), or -1 w/ errno
* @cancellationpoint
* @asyncsignalsafe
* @restartable (unless SO_RCVTIMEO)
*/

View file

@ -35,6 +35,7 @@
* also means getsockname() can be called to retrieve routing details.
*
* @return 0 on success or -1 w/ errno
* @cancellationpoint
* @asyncsignalsafe
* @restartable (unless SO_RCVTIMEO)
*/

View file

@ -1499,6 +1499,7 @@ int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev) {
* @param maxevents is array length of events
* @param timeoutms is milliseconds, 0 to not block, or -1 for forever
* @return number of events stored, 0 on timeout, or -1 w/ errno
* @cancellationpoint
* @norestart
*/
int epoll_wait(int epfd, struct epoll_event *events, int maxevents,

View file

@ -16,12 +16,12 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/strace.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timeval.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sock/internal.h"
#include "libc/sock/select.h"
#include "libc/sysv/errfuns.h"
@ -44,6 +44,11 @@
*
* This system call is supported on all platforms. It's like select()
* except that it atomically changes the sigprocmask() during the op.
*
* @cancellationpoint
* @asyncsignalsafe
* @threadsafe
* @norestart
*/
int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
const struct timespec *timeout, const sigset_t *sigmask) {

View file

@ -34,7 +34,7 @@ textwindows ssize_t sys_recv_nt(struct Fd *fd, const struct iovec *iov,
struct SockFd *sockfd;
struct NtIovec iovnt[16];
struct NtOverlapped overlapped = {.hEvent = WSACreateEvent()};
if (_check_interrupts(true, g_fds.p)) return eintr();
if (_check_interrupts(true, g_fds.p)) return -1;
err = errno;
if (!WSARecv(fd->handle, iovnt, __iovec2nt(iovnt, iov, iovlen), &got, &flags,
&overlapped, NULL)) {

View file

@ -35,7 +35,7 @@ textwindows ssize_t sys_recvfrom_nt(struct Fd *fd, const struct iovec *iov,
struct SockFd *sockfd;
struct NtIovec iovnt[16];
struct NtOverlapped overlapped = {.hEvent = WSACreateEvent()};
if (_check_interrupts(true, g_fds.p)) return eintr();
if (_check_interrupts(true, g_fds.p)) return -1;
err = errno;
if (!WSARecvFrom(fd->handle, iovnt, __iovec2nt(iovnt, iov, iovlen), &got,
&flags, opt_out_srcaddr, opt_inout_srcaddrsize, &overlapped,

View file

@ -44,6 +44,7 @@
* @return number of bytes received, 0 on remote close, or -1 w/ errno
* @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable),
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
* @cancellationpoint
* @asyncsignalsafe
* @restartable (unless SO_RCVTIMEO)
*/

View file

@ -41,6 +41,7 @@
* @return number of bytes received, or -1 w/ errno
* @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable),
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
* @cancellationpoint
* @asyncsignalsafe
* @restartable (unless SO_RCVTIMEO)
*/

View file

@ -16,11 +16,11 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/strace.internal.h"
#include "libc/calls/struct/timeval.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sock/internal.h"
#include "libc/sock/select.h"
#include "libc/sysv/errfuns.h"
@ -31,6 +31,11 @@
* This system call is supported on all platforms. However, on Windows,
* this is polyfilled to translate into poll(). So it's recommended that
* poll() be used instead.
*
* @cancellationpoint
* @asyncsignalsafe
* @threadsafe
* @norestart
*/
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout) {

View file

@ -30,7 +30,7 @@ textwindows ssize_t sys_send_nt(int fd, const struct iovec *iov, size_t iovlen,
struct SockFd *sockfd;
struct NtIovec iovnt[16];
struct NtOverlapped overlapped = {.hEvent = WSACreateEvent()};
if (_check_interrupts(true, g_fds.p)) return eintr();
if (_check_interrupts(true, g_fds.p)) return -1;
if (!WSASend(g_fds.p[fd].handle, iovnt, __iovec2nt(iovnt, iov, iovlen), &sent,
flags, &overlapped, NULL)) {
rc = sent;

View file

@ -43,6 +43,7 @@
// sendfile() isn't specified as raising eintr
static textwindows int SendfileBlock(int64_t handle,
struct NtOverlapped *overlapped) {
int rc;
uint32_t i, got, flags = 0;
if (WSAGetLastError() != kNtErrorIoPending &&
WSAGetLastError() != WSAEINPROGRESS) {
@ -56,7 +57,7 @@ static textwindows int SendfileBlock(int64_t handle,
NTTRACE("WSAWaitForMultipleEvents failed %lm");
return __winsockerr();
} else if (i == kNtWaitTimeout || i == kNtWaitIoCompletion) {
_check_interrupts(true, g_fds.p);
if (_check_interrupts(true, g_fds.p)) return -1;
#if _NTTRACE
POLLTRACE("WSAWaitForMultipleEvents...");
#endif

View file

@ -43,6 +43,7 @@
* @return number of bytes transmitted, or -1 w/ errno
* @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable),
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
* @cancellationpoint
* @asyncsignalsafe
* @restartable (unless SO_RCVTIMEO)
*/

View file

@ -31,7 +31,7 @@ textwindows ssize_t sys_sendto_nt(int fd, const struct iovec *iov,
struct SockFd *sockfd;
struct NtIovec iovnt[16];
struct NtOverlapped overlapped = {.hEvent = WSACreateEvent()};
if (_check_interrupts(true, g_fds.p)) return eintr();
if (_check_interrupts(true, g_fds.p)) return -1;
if (!WSASendTo(g_fds.p[fd].handle, iovnt, __iovec2nt(iovnt, iov, iovlen),
&sent, flags, opt_in_addr, in_addrsize, &overlapped, NULL)) {
rc = sent;

View file

@ -49,6 +49,7 @@
* @return number of bytes transmitted, or -1 w/ errno
* @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable),
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
* @cancellationpoint
* @asyncsignalsafe
* @restartable (unless SO_RCVTIMEO)
*/

View file

@ -28,6 +28,7 @@
textwindows int __wsablock(int64_t handle, struct NtOverlapped *overlapped,
uint32_t *flags, bool restartable,
uint32_t timeout) {
int rc;
uint32_t i, got;
if (WSAGetLastError() != kNtErrorIoPending) {
NTTRACE("sock i/o failed %lm");
@ -41,7 +42,7 @@ textwindows int __wsablock(int64_t handle, struct NtOverlapped *overlapped,
return __winsockerr();
} else if (i == kNtWaitTimeout || i == kNtWaitIoCompletion) {
if (_check_interrupts(restartable, g_fds.p)) {
return eintr();
return -1;
}
if (timeout) {
if (timeout <= __SIG_POLLING_INTERVAL_MS) {