Improve threading and i/o routines

- On Windows connect() can now be interrupted by a signal; connect() w/
  O_NONBLOCK will now raise EINPROGRESS; and connect() with SO_SNDTIMEO
  will raise ETIMEDOUT after the interval has elapsed.

- We now get the AcceptEx(), ConnectEx(), and TransmitFile() functions
  from the WIN32 API the officially blessed way, using WSAIoctl().

- Do nothing on Windows when fsync() is called on a directory handle.
  This was raising EACCES earlier becaues GENERIC_WRITE is required on
  the handle. It's possible to FlushFileBuffers() a directory handle if
  it's opened with write access but MSDN doesn't document what it does.
  If you have any idea, please let us know!

- Prefer manual reset event objects for read() and write() on Windows.

- Do some code cleanup on our dlmalloc customizations.

- Fix errno type error in Windows blocking routines.

- Make the futex polyfill simpler and faster.
This commit is contained in:
Justine Tunney 2023-10-12 18:53:17 -07:00
parent f7343319cc
commit 49b0eaa69f
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
43 changed files with 528 additions and 425 deletions

View file

@ -1,14 +1,17 @@
#include "third_party/dlmalloc/dlmalloc.h"
#include "libc/assert.h"
#include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/bsr.h"
#include "libc/intrin/likely.h"
#include "libc/intrin/weaken.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/rdtsc.h"
#include "libc/nexgen32e/yield.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/sysconf.h"
@ -19,7 +22,9 @@
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#include "third_party/dlmalloc/vespene.internal.h"
#include "third_party/nsync/mu.h"
// clang-format off
#define FOOTERS 0
@ -45,7 +50,11 @@
#endif
#undef assert
#define assert(x) npassert(x)
#if IsTiny()
#define assert(x) if(!(x)) __builtin_unreachable()
#else
#define assert(x) if(!(x)) ABORT
#endif
#include "third_party/dlmalloc/platform.inc"
#include "third_party/dlmalloc/locks.inc"

View file

@ -507,7 +507,7 @@ void mspace_inspect_all(mspace msp,
void* arg);
void dlmalloc_atfork(void);
void dlmalloc_abort(void);
void dlmalloc_abort(void) relegated wontreturn;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -17,16 +17,16 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/intrin/weaken.h"
#include "libc/log/log.h"
#include "libc/errno.h"
#include "libc/intrin/describebacktrace.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "third_party/dlmalloc/dlmalloc.h"
#define MESSAGE "dlmalloc_abort()\n"
void dlmalloc_abort(void) {
write(2, MESSAGE, strlen(MESSAGE));
if (_weaken(__die)) _weaken(__die)();
tinyprint(2,
"error: dlmalloc aborted\n"
"cosmoaddr2line ",
program_invocation_name, " ",
DescribeBacktrace(__builtin_frame_address(0)), "\n", NULL);
_Exit(44);
}

View file

@ -1,9 +1,4 @@
// clang-format off
#include "third_party/nsync/mu.h"
#include "libc/atomic.h"
#include "libc/intrin/atomic.h"
#include "libc/calls/calls.h"
#include "libc/thread/tls.h"
/* --------------------------- Lock preliminaries ------------------------ */
@ -35,52 +30,48 @@
*/
static int malloc_lock(atomic_int *lk) {
if (!__threaded) return 0;
while (atomic_exchange_explicit(lk, 1, memory_order_acquire)) {
donothing;
}
return 0;
}
static int malloc_trylock(atomic_int *lk) {
if (!__threaded) return 1;
return !atomic_exchange_explicit(lk, 1, memory_order_acquire);
}
static inline int malloc_unlock(atomic_int *lk) {
atomic_store_explicit(lk, 0, memory_order_release);
return 0;
}
#if !USE_LOCKS
#define USE_LOCK_BIT (0U)
#define INITIAL_LOCK(l) (0)
#define DESTROY_LOCK(l) (0)
#define ACQUIRE_MALLOC_GLOBAL_LOCK()
#define RELEASE_MALLOC_GLOBAL_LOCK()
#elif defined(TINY)
#define MLOCK_T atomic_int
#define ACQUIRE_LOCK(lk) malloc_lock(lk)
#define RELEASE_LOCK(lk) malloc_unlock(lk)
#define TRY_LOCK(lk) malloc_trylock(lk)
#define INITIAL_LOCK(lk) (atomic_store_explicit(lk, 0, memory_order_relaxed), 0)
#define DESTROY_LOCK(lk)
#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex);
#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex);
static MLOCK_T malloc_global_mutex;
#ifdef TINY
#define MLOCK_T atomic_uint
#else
#define MLOCK_T nsync_mu
#define ACQUIRE_LOCK(lk) (__threaded && (nsync_mu_lock(lk), 0))
#define RELEASE_LOCK(lk) (__threaded && (nsync_mu_unlock(lk), 0))
#define TRY_LOCK(lk) (__threaded ? nsync_mu_trylock(lk) : 1)
#define INITIAL_LOCK(lk) memset(lk, 0, sizeof(*lk))
#define DESTROY_LOCK(lk) memset(lk, -1, sizeof(*lk))
#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex);
#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex);
static MLOCK_T malloc_global_mutex;
#define MLOCK_T nsync_mu
#endif
static int malloc_wipe(MLOCK_T *lk) {
bzero(lk, sizeof(*lk));
return 0;
}
static int malloc_lock(MLOCK_T *lk) {
if (!__threaded) return 0;
#ifdef TINY
while (atomic_exchange_explicit(lk, 1, memory_order_acquire)) {
spin_yield();
}
#else
nsync_mu_lock(lk);
#endif
return 0;
}
static int malloc_unlock(MLOCK_T *lk) {
if (!__threaded) return 0;
#ifdef TINY
atomic_store_explicit(lk, 0, memory_order_release);
#else
nsync_mu_unlock(lk);
#endif
return 0;
}
#define ACQUIRE_LOCK(lk) malloc_lock(lk)
#define RELEASE_LOCK(lk) malloc_unlock(lk)
#define INITIAL_LOCK(lk) malloc_wipe(lk)
#define DESTROY_LOCK(lk)
#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex);
#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex);
static MLOCK_T malloc_global_mutex;
#define USE_LOCK_BIT (2U)
struct malloc_chunk {