From 658804a5220a71bbe3354c51433676e6c2f972d0 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 10 Jul 2025 12:19:46 -0400 Subject: [PATCH] misc: add clang thread safety analysis support Implements declarations for being able to use clang's thread safety analysis in client libraries that utilize uv's mutexes. Also enable them for building libuv itself. This is a static version of the Thread Sanitizer tests that are already in use on Unix. We will need to add a clang CI agent for Windows to enable the testing there. --- CMakeLists.txt | 21 +++ configure.ac | 5 + docs/src/threading.rst | 82 ++++++++++- include/uv.h | 226 ++++++++++++++++++++++++++++-- include/uv/unix.h | 14 +- include/uv/win.h | 8 +- src/threadpool.c | 14 +- src/unix/async.c | 12 +- src/unix/darwin.c | 2 +- src/unix/fs.c | 10 +- src/unix/process.c | 12 +- src/unix/proctitle.c | 4 +- src/unix/random-devurandom.c | 2 +- src/unix/random-getentropy.c | 2 +- src/unix/signal.c | 2 +- src/unix/thread.c | 60 ++++---- src/win/core.c | 12 +- src/win/fs-fd-hash-inl.h | 6 +- src/win/fs.c | 88 ++++++------ src/win/poll.c | 4 +- src/win/process.c | 2 +- src/win/thread.c | 39 +++--- src/win/tty.c | 2 +- test/test-async.c | 10 +- test/test-fs-fd-hash.c | 10 +- test/test-mutexes.c | 10 +- test/test-signal-multiple-loops.c | 33 +++-- test/test-tcp-reuseport.c | 5 +- 28 files changed, 524 insertions(+), 173 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e725bff71..b7bfdd76f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -166,6 +166,27 @@ list(APPEND uv_cflags ${lint-utf8-msvc} ) check_c_compiler_flag(-fno-strict-aliasing UV_F_STRICT_ALIASING) list(APPEND uv_cflags $<$:-fno-strict-aliasing>) +# Thread safety analysis - only enabled for Clang +if(CMAKE_C_COMPILER_ID MATCHES "Clang") + check_c_compiler_flag(-Wthread-safety UV_THREAD_SAFETY) + check_c_compiler_flag(-Wthread-safety-negative UV_THREAD_SAFETY_NEGATIVE) + check_c_compiler_flag(-Wthread-safety-beta UV_THREAD_SAFETY_BETA) + check_c_compiler_flag(-Wthread-safety-pointer UV_THREAD_SAFETY_POINTER) + + if(UV_THREAD_SAFETY) + list(APPEND uv_cflags -Wthread-safety) + endif() + if(UV_THREAD_SAFETY_NEGATIVE) + list(APPEND uv_cflags -Wthread-safety-negative) + endif() + if(UV_THREAD_SAFETY_BETA) + list(APPEND uv_cflags -Wthread-safety-beta) + endif() + if(UV_THREAD_SAFETY_POINTER) + list(APPEND uv_cflags -Wthread-safety-pointer) + endif() +endif() + if (MSVC) # Error on calling undeclared functions. list(APPEND uv_cflags "/we4013") diff --git a/configure.ac b/configure.ac index 094a3d2d9..e832c616c 100644 --- a/configure.ac +++ b/configure.ac @@ -39,6 +39,11 @@ CC_CHECK_CFLAGS_APPEND([-Wextra]) CC_CHECK_CFLAGS_APPEND([-Wno-long-long]) CC_CHECK_CFLAGS_APPEND([-Wno-unused-parameter]) CC_CHECK_CFLAGS_APPEND([-Wstrict-prototypes]) +# Thread safety analysis warnings (Clang only) +CC_CHECK_CFLAGS_APPEND([-Wthread-safety]) +CC_CHECK_CFLAGS_APPEND([-Wthread-safety-negative]) +CC_CHECK_CFLAGS_APPEND([-Wthread-safety-beta]) +CC_CHECK_CFLAGS_APPEND([-Wthread-safety-pointer]) # AM_PROG_AR is not available in automake v0.11 but it's essential in v0.12. m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) # autoconf complains if AC_PROG_LIBTOOL precedes AM_PROG_AR. diff --git a/docs/src/threading.rst b/docs/src/threading.rst index 27c1d6ee2..842dfe169 100644 --- a/docs/src/threading.rst +++ b/docs/src/threading.rst @@ -167,8 +167,8 @@ Threads If the function fails, the return value is less than zero. Sets the scheduling priority of the thread specified by tid. It requires elevated privilege to set specific priorities on some platforms. - The priority can be set to the following constants. UV_THREAD_PRIORITY_HIGHEST, - UV_THREAD_PRIORITY_ABOVE_NORMAL, UV_THREAD_PRIORITY_NORMAL, + The priority can be set to the following constants. UV_THREAD_PRIORITY_HIGHEST, + UV_THREAD_PRIORITY_ABOVE_NORMAL, UV_THREAD_PRIORITY_NORMAL, UV_THREAD_PRIORITY_BELOW_NORMAL, UV_THREAD_PRIORITY_LOWEST. .. c:function:: int uv_thread_getpriority(uv_thread_t tid, int* priority) @@ -282,3 +282,81 @@ return type is void, of course). .. c:function:: int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) .. c:function:: void uv_barrier_destroy(uv_barrier_t* barrier) .. c:function:: int uv_barrier_wait(uv_barrier_t* barrier) + +Thread Safety Analysis Helpers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +These functions provide support for Clang's thread safety analysis when enabled. +They are no-op inline functions that exist solely to communicate lock state +information to the static analyzer. They have no runtime functionality and are +compiled away in release builds. For the purposes of this analysis, `uv_mutex_t` +is assumed not to be used with `uv_mutex_init_recursive`. + +.. note:: + These functions are only useful when compiling with Clang and thread safety + analysis enabled (``-Wthread-safety``). They have no effect with other + compilers or when thread safety analysis warnings are disabled. + +**Mutex Helpers:** + +.. c:function:: void uv_mutex_assume_locked(uv_mutex_t* handle) + + Tells the thread safety analyzer to assume that the mutex is currently held + by the calling thread. Use this when a mutex is acquired when the lock state + is established through external means that the analyzer cannot track, such + as conditional logic. + +.. c:function:: void uv_mutex_assume_unlocked(uv_mutex_t* handle) + + Tells the thread safety analyzer to assume that the mutex it thought was + held is no longer held by the calling thread, releases capability tracking + for it. Use this when a mutex is released through external means that the + analyzer could not currently analyze adequately (such as a callback). + +.. c:function:: void uv_mutex_assert_unlocked(uv_mutex_t* handle) + + Asserts to the thread safety analyzer that the mutex must not be held when + this function is called. Use this to document and verify that certain code + paths require a mutex to be unlocked. This is a path-aware version of + annotating the whole function with UV_EXCLUDES. + +**Read-Write Lock Helpers:** + +.. c:function:: void uv_rwlock_assume_rdlocked(uv_rwlock_t* handle) + + Tells the thread safety analyzer to assume that the read-write lock is + currently held for reading by the calling thread. + +.. c:function:: void uv_rwlock_assume_wrlocked(uv_rwlock_t* handle) + + Tells the thread safety analyzer to assume that the read-write lock is + currently held for writing by the calling thread. + +.. c:function:: void uv_rwlock_assume_rdunlocked(uv_rwlock_t* handle) + + Tells the thread safety analyzer to assume that the read-write lock is + released for reading by the calling thread. + +.. c:function:: void uv_rwlock_assume_wrunlocked(uv_rwlock_t* handle) + + Tells the thread safety analyzer to assume that the read-write lock is + released for writing by the calling thread. + +.. c:function:: void uv_rwlock_assert_unlocked(uv_rwlock_t* handle) + + Asserts to the thread safety analyzer that the read-write lock must not be + held (for reading or writing) when this function is called. + +**Once Helpers:** + +.. c:function:: void uv_once_assume_ran(uv_once_t* handle) + + Tell the thread safety analyzer to assume that :c:func:`uv_once` has already + been called for the given ``handle`` and the initialization callback has completed. + + This function is used in scenarios where the analyzer cannot determine that + the once-initialization has already occurred through other means (such as when + the initialization happens in a different compilation unit or through + external guarantees). It also can be called to entirely bypass running the + `once` function while still satisfying the analyzer, for example, if known + to be guaranteed to be single-threaded at the time of initialization. diff --git a/include/uv.h b/include/uv.h index f788c040f..c83fd24b1 100644 --- a/include/uv.h +++ b/include/uv.h @@ -60,6 +60,174 @@ extern "C" { #include #include +/* Clang thread safety analysis annotations */ +#if defined(__clang__) +# define UV__THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +# define UV__THREAD_ANNOTATION_ATTRIBUTE__(x) /* no-op */ +#endif + +#define UV_CAPABILITY(x) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(capability(x)) + +#define UV_ACQUIRE(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__)) + +#define UV_RELEASE(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__)) + +#define UV_TRY_ACQUIRE(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__)) + +#define UV_REQUIRES(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__)) + +#define UV_RELOCKS(...) \ + UV_REQUIRES(__VA_ARGS__) UV_RELEASE(__VA_ARGS__) UV_ACQUIRE(__VA_ARGS__); + +/* Macro trickery needed to negate each __VA_ARGS__ argument (up to 5). */ +#define UV__NEGATE_ARGS_1(a) !a +#define UV__NEGATE_ARGS_2(a, b) !a, !b +#define UV__NEGATE_ARGS_3(a, b, c) !a, !b, !c +#define UV__NEGATE_ARGS_4(a, b, c, d) !a, !b, !c, !d +#define UV__NEGATE_ARGS_5(a, b, c, d, e) !a, !b, !c, !d, !e +#define UV__COUNT_ARGS_IMPL(_1, _2, _3, _4, _5, N, ...) N +#define UV__COUNT_ARGS(...) UV__COUNT_ARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1, 0) +#define UV__NEGATE_DISPATCH(N) UV__NEGATE_ARGS_##N +#define UV__NEGATE_ARGS_APPLY(N, ...) UV__NEGATE_DISPATCH(N)(__VA_ARGS__) +#define UV__NEGATE_ALL_ARGS(...) UV__NEGATE_ARGS_APPLY(UV__COUNT_ARGS(__VA_ARGS__), __VA_ARGS__) + +/* UV_EXCLUDES is intentionally mapped to requires_capability(!x) instead of + * locks_excluded(x) because negative capabilities are more precise and integrate + * better with the capability analysis system. While locks_excluded is an older + * annotation that simply checks a lock isn't held, requires_capability(!x) + * participates in the full capability analysis, allowing for better + * composability and more accurate tracking of lock states. */ +#define UV_EXCLUDES(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(UV__NEGATE_ALL_ARGS(__VA_ARGS__))) + +#define UV_ACQUIRE_SHARED(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__)) + +#define UV_RELEASE_SHARED(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__)) + +#define UV_TRY_ACQUIRE_SHARED(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__)) + +#define UV_REQUIRES_SHARED(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__)) + +/* Data annotations - specify which locks protect which data members */ +#define UV_GUARDED_BY(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(__VA_ARGS__)) + +#define UV_PT_GUARDED_BY(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(__VA_ARGS__)) + +/* Lock ordering annotations - prevent deadlock by specifying acquisition order */ +#define UV_ACQUIRED_BEFORE(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) + +#define UV_ACQUIRED_AFTER(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) + +/* Use this assert where it is not possible to use UV_REQUIRES, such as at the + * start of a callback. */ +#define UV_ASSERT_CAPABILITY(x) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x)) + +#define UV_ASSERT_SHARED_CAPABILITY(x) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x)) + +/* Disable analysis for specific functions. */ +#define UV_NO_THREAD_SAFETY_ANALYSIS \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) + +/* Clang thread safety analysis annotations */ +#if defined(__clang__) +# define UV__THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +# define UV__THREAD_ANNOTATION_ATTRIBUTE__(x) /* no-op */ +#endif + +#define UV_CAPABILITY(x) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(capability(x)) + +#define UV_ACQUIRE(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__)) + +#define UV_RELEASE(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__)) + +#define UV_TRY_ACQUIRE(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__)) + +#define UV_REQUIRES(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__)) + +#define UV_RELOCKS(...) \ + UV_REQUIRES(__VA_ARGS__) UV_RELEASE(__VA_ARGS__) UV_ACQUIRE(__VA_ARGS__); + +/* Macro trickery needed to negate each __VA_ARGS__ argument (up to 5). */ +#define UV__NEGATE_ARGS_1(a) !a +#define UV__NEGATE_ARGS_2(a, b) !a, !b +#define UV__NEGATE_ARGS_3(a, b, c) !a, !b, !c +#define UV__NEGATE_ARGS_4(a, b, c, d) !a, !b, !c, !d +#define UV__NEGATE_ARGS_5(a, b, c, d, e) !a, !b, !c, !d, !e +#define UV__COUNT_ARGS_IMPL(_1, _2, _3, _4, _5, N, ...) N +#define UV__COUNT_ARGS(...) UV__COUNT_ARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1, 0) +#define UV__NEGATE_DISPATCH(N) UV__NEGATE_ARGS_##N +#define UV__NEGATE_ARGS_APPLY(N, ...) UV__NEGATE_DISPATCH(N)(__VA_ARGS__) +#define UV__NEGATE_ALL_ARGS(...) UV__NEGATE_ARGS_APPLY(UV__COUNT_ARGS(__VA_ARGS__), __VA_ARGS__) + +/* UV_EXCLUDES is intentionally mapped to requires_capability(!x) instead of + * locks_excluded(x) because negative capabilities are more precise and integrate + * better with the capability analysis system. While locks_excluded is an older + * annotation that simply checks a lock isn't held, requires_capability(!x) + * participates in the full capability analysis, allowing for better + * composability and more accurate tracking of lock states. */ +#define UV_EXCLUDES(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(UV__NEGATE_ALL_ARGS(__VA_ARGS__))) + +#define UV_ACQUIRE_SHARED(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__)) + +#define UV_RELEASE_SHARED(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__)) + +#define UV_TRY_ACQUIRE_SHARED(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__)) + +#define UV_REQUIRES_SHARED(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__)) + +/* Data annotations - specify which locks protect which data members */ +#define UV_GUARDED_BY(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(__VA_ARGS__)) + +#define UV_PT_GUARDED_BY(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(__VA_ARGS__)) + +/* Lock ordering annotations - prevent deadlock by specifying acquisition order */ +#define UV_ACQUIRED_BEFORE(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) + +#define UV_ACQUIRED_AFTER(...) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) + +/* Use this assert where it is not possible to use UV_REQUIRES, such as at the + * start of a callback. */ +#define UV_ASSERT_CAPABILITY(x) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x)) + +#define UV_ASSERT_SHARED_CAPABILITY(x) \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x)) + +/* Disable analysis for specific functions. */ +#define UV_NO_THREAD_SAFETY_ANALYSIS \ + UV__THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) + /* Internal type, do not use. */ struct uv__queue { struct uv__queue* next; @@ -512,6 +680,14 @@ UV_EXTERN int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd); UV_EXTERN uv_buf_t uv_buf_init(char* base, unsigned int len); +#if defined(_MSC_VER) +# define INLINE __inline +#elif defined(__GNUC__) || defined(__MVS__) +# define INLINE __inline__ +#else +# define INLINE inline +#endif + UV_EXTERN int uv_pipe(uv_file fds[2], int read_flags, int write_flags); UV_EXTERN int uv_socketpair(int type, int protocol, @@ -1842,21 +2018,38 @@ UV_EXTERN void uv_dlclose(uv_lib_t* lib); UV_EXTERN int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr); UV_EXTERN const char* uv_dlerror(const uv_lib_t* lib); + UV_EXTERN int uv_mutex_init(uv_mutex_t* handle); UV_EXTERN int uv_mutex_init_recursive(uv_mutex_t* handle); UV_EXTERN void uv_mutex_destroy(uv_mutex_t* handle); -UV_EXTERN void uv_mutex_lock(uv_mutex_t* handle); -UV_EXTERN int uv_mutex_trylock(uv_mutex_t* handle); -UV_EXTERN void uv_mutex_unlock(uv_mutex_t* handle); +UV_EXTERN void uv_mutex_lock(uv_mutex_t* handle) UV_ACQUIRE(handle); +UV_EXTERN int uv_mutex_trylock(uv_mutex_t* handle) UV_TRY_ACQUIRE(0, handle); +UV_EXTERN void uv_mutex_unlock(uv_mutex_t* handle) UV_RELEASE(handle); +static INLINE void uv_mutex_assume_locked(uv_mutex_t* handle) +UV_ASSERT_CAPABILITY(handle) {}; +static INLINE void uv_mutex_assume_unlocked(uv_mutex_t* handle) +UV_RELEASE(handle) UV_NO_THREAD_SAFETY_ANALYSIS {}; +static INLINE void uv_mutex_assert_unlocked(uv_mutex_t* handle) +UV_EXCLUDES(handle) {}; UV_EXTERN int uv_rwlock_init(uv_rwlock_t* rwlock); UV_EXTERN void uv_rwlock_destroy(uv_rwlock_t* rwlock); -UV_EXTERN void uv_rwlock_rdlock(uv_rwlock_t* rwlock); -UV_EXTERN int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock); -UV_EXTERN void uv_rwlock_rdunlock(uv_rwlock_t* rwlock); -UV_EXTERN void uv_rwlock_wrlock(uv_rwlock_t* rwlock); -UV_EXTERN int uv_rwlock_trywrlock(uv_rwlock_t* rwlock); -UV_EXTERN void uv_rwlock_wrunlock(uv_rwlock_t* rwlock); +UV_EXTERN void uv_rwlock_rdlock(uv_rwlock_t* rwlock) UV_ACQUIRE_SHARED(rwlock); +UV_EXTERN int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) UV_TRY_ACQUIRE_SHARED(0, rwlock); +UV_EXTERN void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) UV_RELEASE_SHARED(rwlock); +UV_EXTERN void uv_rwlock_wrlock(uv_rwlock_t* rwlock) UV_ACQUIRE(rwlock); +UV_EXTERN int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) UV_TRY_ACQUIRE(0, rwlock); +UV_EXTERN void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) UV_RELEASE(rwlock); +static INLINE void uv_rwlock_assume_rdlocked(uv_rwlock_t* handle) +UV_ASSERT_SHARED_CAPABILITY(handle) {}; +static INLINE void uv_rwlock_assume_wrlocked(uv_rwlock_t* handle) +UV_ASSERT_CAPABILITY(handle) {}; +static INLINE void uv_rwlock_assume_rdunlocked(uv_rwlock_t* handle) +UV_RELEASE_SHARED(handle) UV_NO_THREAD_SAFETY_ANALYSIS {}; +static INLINE void uv_rwlock_assume_wrunlocked(uv_rwlock_t* handle) +UV_RELEASE(handle) UV_NO_THREAD_SAFETY_ANALYSIS {}; +static INLINE void uv_rwlock_assert_unlocked(uv_rwlock_t* handle) +UV_EXCLUDES(handle) {}; UV_EXTERN int uv_sem_init(uv_sem_t* sem, unsigned int value); UV_EXTERN void uv_sem_destroy(uv_sem_t* sem); @@ -1873,12 +2066,19 @@ UV_EXTERN int uv_barrier_init(uv_barrier_t* barrier, unsigned int count); UV_EXTERN void uv_barrier_destroy(uv_barrier_t* barrier); UV_EXTERN int uv_barrier_wait(uv_barrier_t* barrier); -UV_EXTERN void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex); +UV_EXTERN void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) UV_RELOCKS(mutex); UV_EXTERN int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, - uint64_t timeout); + uint64_t timeout) UV_RELOCKS(mutex); -UV_EXTERN void uv_once(uv_once_t* guard, void (*callback)(void)); +/* Annotate this function for thread safety analysis purposes such that uv_once + * can acquire the guard with UV_ACQUIRE internally for the callback to write any + * values, then return with it in the SHARED state for the callee to read any + * values it protects. */ +UV_EXTERN void uv_once(uv_once_t* guard, void (*callback)(void)) +UV_EXCLUDES(guard) UV_ASSERT_SHARED_CAPABILITY(guard); +static INLINE void uv_once_assume_ran(uv_once_t* handle) +UV_ASSERT_SHARED_CAPABILITY(handle) UV_NO_THREAD_SAFETY_ANALYSIS {} UV_EXTERN int uv_key_create(uv_key_t* key); UV_EXTERN void uv_key_delete(uv_key_t* key); @@ -1985,6 +2185,8 @@ UV_EXTERN void uv_wtf8_to_utf16(const char* wtf8, #undef UV_SIGNAL_PRIVATE_FIELDS #undef UV_LOOP_PRIVATE_FIELDS #undef UV__ERR +#undef INLINE + #ifdef __cplusplus } diff --git a/include/uv/unix.h b/include/uv/unix.h index c6ba419da..711777348 100644 --- a/include/uv/unix.h +++ b/include/uv/unix.h @@ -124,12 +124,18 @@ typedef int uv_os_sock_t; typedef int uv_os_fd_t; typedef pid_t uv_pid_t; -#define UV_ONCE_INIT PTHREAD_ONCE_INIT +typedef struct UV_CAPABILITY("uv_once") uv_once_s { + pthread_once_t o; +} uv_once_t; -typedef pthread_once_t uv_once_t; +#define UV_ONCE_INIT { PTHREAD_ONCE_INIT } typedef pthread_t uv_thread_t; -typedef pthread_mutex_t uv_mutex_t; -typedef pthread_rwlock_t uv_rwlock_t; +typedef struct UV_CAPABILITY("uv_mutex") uv_mutex_s { + pthread_mutex_t m; +} uv_mutex_t; +typedef struct UV_CAPABILITY("uv_rwlock") uv_rwlock_s { + pthread_rwlock_t rw; +} uv_rwlock_t; typedef UV_PLATFORM_SEM_T uv_sem_t; typedef pthread_cond_t uv_cond_t; typedef pthread_key_t uv_key_t; diff --git a/include/uv/win.h b/include/uv/win.h index 7b4ebd4b7..53a34677a 100644 --- a/include/uv/win.h +++ b/include/uv/win.h @@ -237,7 +237,9 @@ typedef HANDLE uv_thread_t; typedef HANDLE uv_sem_t; -typedef CRITICAL_SECTION uv_mutex_t; +typedef struct UV_CAPABILITY("uv_mutex") uv_mutex_s { + CRITICAL_SECTION cs; +} uv_mutex_t; /* This condition variable implementation is based on the SetEvent solution * (section 3.2) at http://www.cs.wustl.edu/~schmidt/win32-cv-1.html @@ -256,7 +258,7 @@ typedef union { } unused_; /* TODO: retained for ABI compatibility; remove me in v2.x. */ } uv_cond_t; -typedef struct { +typedef struct UV_CAPABILITY("uv_rwlock") uv_rwlock_s { SRWLOCK read_write_lock_; /* TODO: retained for ABI compatibility; remove me in v2.x */ #ifdef _WIN64 @@ -281,7 +283,7 @@ typedef struct { #define UV_ONCE_INIT { 0, { NULL } } -typedef struct uv_once_s { +typedef struct UV_CAPABILITY("uv_once") uv_once_s { unsigned char unused; INIT_ONCE init_once; } uv_once_t; diff --git a/src/threadpool.c b/src/threadpool.c index 98d81cc7b..afe693074 100644 --- a/src/threadpool.c +++ b/src/threadpool.c @@ -54,7 +54,7 @@ static void uv__cancelled(struct uv__work* w) { /* To avoid deadlock with uv_cancel() it's crucial that the worker * never holds the global mutex and the loop-local mutex at the same time. */ -static void worker(void* arg) { +static void worker(void* arg) UV_EXCLUDES(&mutex) { struct uv__work* w; struct uv__queue* q; int is_slow_work; @@ -140,7 +140,7 @@ static void worker(void* arg) { } -static void post(struct uv__queue* q, enum uv__work_kind kind) { +static void post(struct uv__queue* q, enum uv__work_kind kind) UV_EXCLUDES(&mutex) { uv_mutex_lock(&mutex); if (kind == UV__WORK_SLOW_IO) { /* Insert into a separate queue. */ @@ -165,7 +165,7 @@ static void post(struct uv__queue* q, enum uv__work_kind kind) { /* TODO(itodorov) - zos: revisit when Woz compiler is available. */ __attribute__((destructor)) #endif -void uv__threadpool_cleanup(void) { +void uv__threadpool_cleanup(void) UV_EXCLUDES(&mutex) { unsigned int i; if (nthreads == 0) @@ -267,7 +267,7 @@ void uv__work_submit(uv_loop_t* loop, struct uv__work* w, enum uv__work_kind kind, void (*work)(struct uv__work* w), - void (*done)(struct uv__work* w, int status)) { + void (*done)(struct uv__work* w, int status)) UV_EXCLUDES(&once, &mutex) { uv_once(&once, init_once); w->loop = loop; w->work = work; @@ -279,7 +279,7 @@ void uv__work_submit(uv_loop_t* loop, /* TODO(bnoordhuis) teach libuv how to cancel file operations * that go through io_uring instead of the thread pool. */ -static int uv__work_cancel(uv_loop_t* loop, uv_req_t* req, struct uv__work* w) { +static int uv__work_cancel(uv_loop_t* loop, uv_req_t* req, struct uv__work* w) UV_EXCLUDES(&once, &mutex) { int cancelled; uv_once(&once, init_once); /* Ensure |mutex| is initialized. */ @@ -369,7 +369,7 @@ static void uv__queue_done(struct uv__work* w, int err) { int uv_queue_work(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, - uv_after_work_cb after_work_cb) { + uv_after_work_cb after_work_cb) UV_EXCLUDES(&once, &mutex) { if (work_cb == NULL) return UV_EINVAL; @@ -386,7 +386,7 @@ int uv_queue_work(uv_loop_t* loop, } -int uv_cancel(uv_req_t* req) { +int uv_cancel(uv_req_t* req) UV_EXCLUDES(&once, &mutex) { struct uv__work* wreq; uv_loop_t* loop; diff --git a/src/unix/async.c b/src/unix/async.c index 64cf2d639..e19fca058 100644 --- a/src/unix/async.c +++ b/src/unix/async.c @@ -255,7 +255,11 @@ static void uv__async_send(uv_loop_t* loop) { } -static int uv__async_start(uv_loop_t* loop) { +static int uv__async_start(uv_loop_t* loop) +#if UV__KQUEUE_EVFILT_USER + UV_EXCLUDES(&kqueue_runtime_detection_guard) +#endif +{ int pipefd[2]; int err; #if UV__KQUEUE_EVFILT_USER @@ -363,7 +367,11 @@ void uv__async_stop(uv_loop_t* loop) { } -int uv__async_fork(uv_loop_t* loop) { +int uv__async_fork(uv_loop_t* loop) +#if UV__KQUEUE_EVFILT_USER + UV_EXCLUDES(&kqueue_runtime_detection_guard) +#endif +{ struct uv__queue queue; struct uv__queue* q; uv_async_t* h; diff --git a/src/unix/darwin.c b/src/unix/darwin.c index 009efbefa..2bb35dd36 100644 --- a/src/unix/darwin.c +++ b/src/unix/darwin.c @@ -57,7 +57,7 @@ static void uv__hrtime_init_once(void) { } -uint64_t uv__hrtime(uv_clocktype_t type) { +uint64_t uv__hrtime(uv_clocktype_t type) UV_EXCLUDES(&once) { uv_once(&once, uv__hrtime_init_once); return mach_continuous_time() * timebase.numer / timebase.denom; } diff --git a/src/unix/fs.c b/src/unix/fs.c index bd3f59628..b186727f5 100644 --- a/src/unix/fs.c +++ b/src/unix/fs.c @@ -341,8 +341,11 @@ static int uv__fs_mkstemp(uv_fs_t* req) { } #endif /* O_CLOEXEC */ - if (req->cb != NULL) + if (req->cb != NULL) { uv_rwlock_rdlock(&req->loop->cloexec_lock); + /* Tell analyzer we will later release the read lock */ + uv_rwlock_assume_rdunlocked(&req->loop->cloexec_lock); + } r = mkstemp(path); @@ -356,8 +359,11 @@ static int uv__fs_mkstemp(uv_fs_t* req) { r = -1; } - if (req->cb != NULL) + if (req->cb != NULL) { + /* Tell analyzer we earlier acquired the read lock */ + uv_rwlock_assume_rdlocked(&req->loop->cloexec_lock); uv_rwlock_rdunlock(&req->loop->cloexec_lock); + } clobber: if (r < 0) diff --git a/src/unix/process.c b/src/unix/process.c index 43e6b7984..20dc3698a 100644 --- a/src/unix/process.c +++ b/src/unix/process.c @@ -862,7 +862,11 @@ static int uv__spawn_and_init_child( const uv_process_options_t* options, int stdio_count, int (*pipes)[2], - pid_t* pid) { + pid_t* pid) +#if defined(__APPLE__) + UV_EXCLUDES(&posix_spawn_init_once) +#endif +{ int signal_pipe[2] = { -1, -1 }; int status; int err; @@ -965,7 +969,11 @@ static int uv__spawn_and_init_child( int uv_spawn(uv_loop_t* loop, uv_process_t* process, - const uv_process_options_t* options) { + const uv_process_options_t* options) +#if defined(__APPLE__) + UV_EXCLUDES(&posix_spawn_init_once) +#endif +{ #if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH) /* fork is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED. */ return UV_ENOSYS; diff --git a/src/unix/proctitle.c b/src/unix/proctitle.c index 9d1f00ddf..6a92356ae 100644 --- a/src/unix/proctitle.c +++ b/src/unix/proctitle.c @@ -93,7 +93,7 @@ char** uv_setup_args(int argc, char** argv) { } -int uv_set_process_title(const char* title) { +int uv_set_process_title(const char* title) UV_EXCLUDES(&process_title_mutex_once, &process_title_mutex) { struct uv__process_title* pt; size_t len; @@ -124,7 +124,7 @@ int uv_set_process_title(const char* title) { } -int uv_get_process_title(char* buffer, size_t size) { +int uv_get_process_title(char* buffer, size_t size) UV_EXCLUDES(&process_title_mutex_once, &process_title_mutex) { if (buffer == NULL || size == 0) return UV_EINVAL; diff --git a/src/unix/random-devurandom.c b/src/unix/random-devurandom.c index d6336f2c9..c7e001a5e 100644 --- a/src/unix/random-devurandom.c +++ b/src/unix/random-devurandom.c @@ -83,7 +83,7 @@ static void uv__random_devurandom_init(void) { } -int uv__random_devurandom(void* buf, size_t buflen) { +int uv__random_devurandom(void* buf, size_t buflen) UV_EXCLUDES(&once) { uv_once(&once, uv__random_devurandom_init); if (status != 0) diff --git a/src/unix/random-getentropy.c b/src/unix/random-getentropy.c index c45d9fd4a..b83835a8a 100644 --- a/src/unix/random-getentropy.c +++ b/src/unix/random-getentropy.c @@ -36,7 +36,7 @@ static void uv__random_getentropy_init(void) { } -int uv__random_getentropy(void* buf, size_t buflen) { +int uv__random_getentropy(void* buf, size_t buflen) UV_EXCLUDES(&once) { size_t pos; size_t stride; diff --git a/src/unix/signal.c b/src/unix/signal.c index 91fb14946..e9be86a83 100644 --- a/src/unix/signal.c +++ b/src/unix/signal.c @@ -107,7 +107,7 @@ static void uv__signal_global_reinit(void) { } -void uv__signal_global_once_init(void) { +void uv__signal_global_once_init(void) UV_EXCLUDES(&uv__signal_global_init_guard) { uv_once(&uv__signal_global_init_guard, uv__signal_global_init); } diff --git a/src/unix/thread.c b/src/unix/thread.c index 34fea364a..a3de22a00 100644 --- a/src/unix/thread.c +++ b/src/unix/thread.c @@ -315,7 +315,7 @@ int uv_thread_getname(uv_thread_t* tid, char* name, size_t size) { int uv_mutex_init(uv_mutex_t* mutex) { #if defined(NDEBUG) || !defined(PTHREAD_MUTEX_ERRORCHECK) - return UV__ERR(pthread_mutex_init(mutex, NULL)); + return UV__ERR(pthread_mutex_init(&mutex->m, NULL)); #else pthread_mutexattr_t attr; int err; @@ -326,7 +326,7 @@ int uv_mutex_init(uv_mutex_t* mutex) { if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK)) abort(); - err = pthread_mutex_init(mutex, &attr); + err = pthread_mutex_init(&mutex->m, &attr); if (pthread_mutexattr_destroy(&attr)) abort(); @@ -346,7 +346,7 @@ int uv_mutex_init_recursive(uv_mutex_t* mutex) { if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) abort(); - err = pthread_mutex_init(mutex, &attr); + err = pthread_mutex_init(&mutex->m, &attr); if (pthread_mutexattr_destroy(&attr)) abort(); @@ -356,21 +356,21 @@ int uv_mutex_init_recursive(uv_mutex_t* mutex) { void uv_mutex_destroy(uv_mutex_t* mutex) { - if (pthread_mutex_destroy(mutex)) + if (pthread_mutex_destroy(&mutex->m)) abort(); } -void uv_mutex_lock(uv_mutex_t* mutex) { - if (pthread_mutex_lock(mutex)) +void uv_mutex_lock(uv_mutex_t* mutex) UV_NO_THREAD_SAFETY_ANALYSIS { + if (pthread_mutex_lock(&mutex->m)) abort(); } -int uv_mutex_trylock(uv_mutex_t* mutex) { +int uv_mutex_trylock(uv_mutex_t* mutex) UV_NO_THREAD_SAFETY_ANALYSIS { int err; - err = pthread_mutex_trylock(mutex); + err = pthread_mutex_trylock(&mutex->m); if (err) { if (err != EBUSY && err != EAGAIN) abort(); @@ -381,33 +381,33 @@ int uv_mutex_trylock(uv_mutex_t* mutex) { } -void uv_mutex_unlock(uv_mutex_t* mutex) { - if (pthread_mutex_unlock(mutex)) +void uv_mutex_unlock(uv_mutex_t* mutex) UV_NO_THREAD_SAFETY_ANALYSIS { + if (pthread_mutex_unlock(&mutex->m)) abort(); } int uv_rwlock_init(uv_rwlock_t* rwlock) { - return UV__ERR(pthread_rwlock_init(rwlock, NULL)); + return UV__ERR(pthread_rwlock_init(&rwlock->rw, NULL)); } void uv_rwlock_destroy(uv_rwlock_t* rwlock) { - if (pthread_rwlock_destroy(rwlock)) + if (pthread_rwlock_destroy(&rwlock->rw)) abort(); } -void uv_rwlock_rdlock(uv_rwlock_t* rwlock) { - if (pthread_rwlock_rdlock(rwlock)) +void uv_rwlock_rdlock(uv_rwlock_t* rwlock) UV_NO_THREAD_SAFETY_ANALYSIS { + if (pthread_rwlock_rdlock(&rwlock->rw)) abort(); } -int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) { +int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) UV_NO_THREAD_SAFETY_ANALYSIS { int err; - err = pthread_rwlock_tryrdlock(rwlock); + err = pthread_rwlock_tryrdlock(&rwlock->rw); if (err) { if (err != EBUSY && err != EAGAIN) abort(); @@ -418,22 +418,22 @@ int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) { } -void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) { - if (pthread_rwlock_unlock(rwlock)) +void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) UV_NO_THREAD_SAFETY_ANALYSIS { + if (pthread_rwlock_unlock(&rwlock->rw)) abort(); } -void uv_rwlock_wrlock(uv_rwlock_t* rwlock) { - if (pthread_rwlock_wrlock(rwlock)) +void uv_rwlock_wrlock(uv_rwlock_t* rwlock) UV_NO_THREAD_SAFETY_ANALYSIS { + if (pthread_rwlock_wrlock(&rwlock->rw)) abort(); } -int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) { +int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) UV_NO_THREAD_SAFETY_ANALYSIS { int err; - err = pthread_rwlock_trywrlock(rwlock); + err = pthread_rwlock_trywrlock(&rwlock->rw); if (err) { if (err != EBUSY && err != EAGAIN) abort(); @@ -444,14 +444,14 @@ int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) { } -void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) { - if (pthread_rwlock_unlock(rwlock)) +void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) UV_NO_THREAD_SAFETY_ANALYSIS { + if (pthread_rwlock_unlock(&rwlock->rw)) abort(); } -void uv_once(uv_once_t* guard, void (*callback)(void)) { - if (pthread_once(guard, callback)) +void uv_once(uv_once_t* guard, void (*callback)(void)) UV_NO_THREAD_SAFETY_ANALYSIS { + if (pthread_once(&guard->o, callback)) abort(); } @@ -816,7 +816,7 @@ void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) { int r; errno = 0; - r = pthread_cond_wait(cond, mutex); + r = pthread_cond_wait(cond, &mutex->m); /* Workaround for a bug in OS X at least up to 13.6 * See https://github.com/libuv/libuv/issues/4165 @@ -832,7 +832,7 @@ void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) { #else /* !(defined(__APPLE__) && defined(__MACH__)) */ void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) { - if (pthread_cond_wait(cond, mutex)) + if (pthread_cond_wait(cond, &mutex->m)) abort(); } @@ -848,7 +848,7 @@ int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { #if defined(__APPLE__) && defined(__MACH__) ts.tv_sec = timeout / NANOSEC; ts.tv_nsec = timeout % NANOSEC; - r = pthread_cond_timedwait_relative_np(cond, mutex, &ts); + r = pthread_cond_timedwait_relative_np(cond, &mutex->m, &ts); #else #if defined(__MVS__) if (gettimeofday(&tv, NULL)) @@ -859,7 +859,7 @@ int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { #endif ts.tv_sec = timeout / NANOSEC; ts.tv_nsec = timeout % NANOSEC; - r = pthread_cond_timedwait(cond, mutex, &ts); + r = pthread_cond_timedwait(cond, &mutex->m, &ts); #endif diff --git a/src/win/core.c b/src/win/core.c index 317238fd2..0aa7c116a 100644 --- a/src/win/core.c +++ b/src/win/core.c @@ -90,7 +90,7 @@ static void uv__loops_init(void) { } -static int uv__loops_add(uv_loop_t* loop) { +static int uv__loops_add(uv_loop_t* loop) UV_EXCLUDES(&uv__loops_lock) { uv_loop_t** new_loops; int new_capacity, i; @@ -118,7 +118,7 @@ failed_loops_realloc: } -static void uv__loops_remove(uv_loop_t* loop) { +static void uv__loops_remove(uv_loop_t* loop) UV_EXCLUDES(&uv__loops_lock) { int loop_index; int smaller_capacity; uv_loop_t** new_loops; @@ -162,7 +162,7 @@ loop_removed: uv_mutex_unlock(&uv__loops_lock); } -void uv__wake_all_loops(void) { +void uv__wake_all_loops(void) UV_EXCLUDES(&uv__loops_lock) { int i; uv_loop_t* loop; @@ -224,7 +224,7 @@ static void uv__init(void) { } -int uv_loop_init(uv_loop_t* loop) { +int uv_loop_init(uv_loop_t* loop) UV_EXCLUDES(&uv__loops_lock) { uv__loop_internal_fields_t* lfields; struct heap* timer_heap; int err; @@ -329,12 +329,12 @@ void uv_update_time(uv_loop_t* loop) { } -void uv__once_init(void) { +void uv__once_init(void) UV_EXCLUDES(&uv_init_guard_) { uv_once(&uv_init_guard_, uv__init); } -void uv__loop_close(uv_loop_t* loop) { +void uv__loop_close(uv_loop_t* loop) UV_EXCLUDES(&uv__loops_lock) { uv__loop_internal_fields_t* lfields; size_t i; diff --git a/src/win/fs-fd-hash-inl.h b/src/win/fs-fd-hash-inl.h index 94b4c3cc4..5c08d200e 100644 --- a/src/win/fs-fd-hash-inl.h +++ b/src/win/fs-fd-hash-inl.h @@ -118,7 +118,7 @@ static void uv__fd_hash_init(void) { FIND_IN_GROUP_PTR(UV__FD_HASH_GROUP_SIZE); \ } while (0) -static int uv__fd_hash_get(int fd, struct uv__fd_info_s* info) { +static int uv__fd_hash_get(int fd, struct uv__fd_info_s* info) UV_EXCLUDES(&uv__fd_hash_mutex) { FIND_COMMON_VARIABLES uv_mutex_lock(&uv__fd_hash_mutex); @@ -133,7 +133,7 @@ static int uv__fd_hash_get(int fd, struct uv__fd_info_s* info) { return entry_ptr != NULL; } -static void uv__fd_hash_add(int fd, struct uv__fd_info_s* info) { +static void uv__fd_hash_add(int fd, struct uv__fd_info_s* info) UV_EXCLUDES(&uv__fd_hash_mutex) { FIND_COMMON_VARIABLES uv_mutex_lock(&uv__fd_hash_mutex); @@ -163,7 +163,7 @@ static void uv__fd_hash_add(int fd, struct uv__fd_info_s* info) { uv_mutex_unlock(&uv__fd_hash_mutex); } -static int uv__fd_hash_remove(int fd, struct uv__fd_info_s* info) { +static int uv__fd_hash_remove(int fd, struct uv__fd_info_s* info) UV_EXCLUDES(&uv__fd_hash_mutex) { FIND_COMMON_VARIABLES uv_mutex_lock(&uv__fd_hash_mutex); diff --git a/src/win/fs.c b/src/win/fs.c index cc2e8db94..4c76614d5 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -415,7 +415,7 @@ static void uv__fs_req_init(uv_loop_t* loop, } -void fs__open(uv_fs_t* req) { +void fs__open(uv_fs_t* req) UV_EXCLUDES(&uv__fd_hash_mutex) { DWORD access; DWORD share; DWORD disposition; @@ -670,7 +670,7 @@ void fs__open(uv_fs_t* req) { SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); } -void fs__close(uv_fs_t* req) { +void fs__close(uv_fs_t* req) UV_EXCLUDES(&uv__fd_hash_mutex) { int fd = req->file.fd; int result; struct uv__fd_info_s fd_info; @@ -720,7 +720,7 @@ LONG fs__filemap_ex_filter(LONG excode, PEXCEPTION_POINTERS pep, } -void fs__read_filemap(uv_fs_t* req, struct uv__fd_info_s* fd_info) { +void fs__read_filemap(uv_fs_t* req, struct uv__fd_info_s* fd_info) UV_EXCLUDES(&uv__fd_hash_mutex) { int fd = req->file.fd; /* VERIFY_FD done in fs__read */ int rw_flags = fd_info->flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); @@ -817,7 +817,7 @@ void fs__read_filemap(uv_fs_t* req, struct uv__fd_info_s* fd_info) { return; } -void fs__read(uv_fs_t* req) { +void fs__read(uv_fs_t* req) UV_EXCLUDES(&uv__fd_hash_mutex) { int fd = req->file.fd; int64_t offset = req->fs.info.offset; HANDLE handle; @@ -900,7 +900,7 @@ void fs__read(uv_fs_t* req) { void fs__write_filemap(uv_fs_t* req, HANDLE file, - struct uv__fd_info_s* fd_info) { + struct uv__fd_info_s* fd_info) UV_EXCLUDES(&uv__fd_hash_mutex) { int fd = req->file.fd; /* VERIFY_FD done in fs__write */ int force_append = fd_info->flags & UV_FS_O_APPEND; int rw_flags = fd_info->flags & @@ -1023,7 +1023,7 @@ void fs__write_filemap(uv_fs_t* req, HANDLE file, SET_REQ_RESULT(req, done_write); } -void fs__write(uv_fs_t* req) { +void fs__write(uv_fs_t* req) UV_EXCLUDES(&uv__fd_hash_mutex) { int fd = req->file.fd; int64_t offset = req->fs.info.offset; HANDLE handle; @@ -2324,7 +2324,7 @@ static void fs__fdatasync(uv_fs_t* req) { } -static void fs__ftruncate(uv_fs_t* req) { +static void fs__ftruncate(uv_fs_t* req) UV_EXCLUDES(&uv__fd_hash_mutex) { int fd = req->file.fd; HANDLE handle; struct uv__fd_info_s fd_info = { 0 }; @@ -3150,7 +3150,7 @@ retry_get_full_path_name: } -static void uv__fs_work(struct uv__work* w) { +static void uv__fs_work(struct uv__work* w) UV_EXCLUDES(&uv__fd_hash_mutex) { uv_fs_t* req; req = container_of(w, uv_fs_t, work_req); @@ -3248,7 +3248,7 @@ void uv_fs_req_cleanup(uv_fs_t* req) { int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, - int mode, uv_fs_cb cb) { + int mode, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_OPEN); @@ -3264,7 +3264,7 @@ int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, } -int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { +int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { INIT(UV_FS_CLOSE); req->file.fd = fd; POST; @@ -3277,7 +3277,7 @@ int uv_fs_read(uv_loop_t* loop, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, - uv_fs_cb cb) { + uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { INIT(UV_FS_READ); if (bufs == NULL || nbufs == 0) { @@ -3310,7 +3310,7 @@ int uv_fs_write(uv_loop_t* loop, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, - uv_fs_cb cb) { + uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { INIT(UV_FS_WRITE); if (bufs == NULL || nbufs == 0) { @@ -3338,7 +3338,7 @@ int uv_fs_write(uv_loop_t* loop, int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, - uv_fs_cb cb) { + uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_UNLINK); @@ -3353,7 +3353,7 @@ int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, - uv_fs_cb cb) { + uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_MKDIR); @@ -3371,7 +3371,7 @@ int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, - uv_fs_cb cb) { + uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_MKDTEMP); @@ -3388,7 +3388,7 @@ int uv_fs_mkdtemp(uv_loop_t* loop, int uv_fs_mkstemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, - uv_fs_cb cb) { + uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_MKSTEMP); @@ -3402,7 +3402,7 @@ int uv_fs_mkstemp(uv_loop_t* loop, } -int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { +int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_RMDIR); @@ -3417,7 +3417,7 @@ int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, - uv_fs_cb cb) { + uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_SCANDIR); @@ -3434,7 +3434,7 @@ int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int uv_fs_opendir(uv_loop_t* loop, uv_fs_t* req, const char* path, - uv_fs_cb cb) { + uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_OPENDIR); @@ -3449,7 +3449,7 @@ int uv_fs_opendir(uv_loop_t* loop, int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, - uv_fs_cb cb) { + uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { INIT(UV_FS_READDIR); if (dir == NULL || @@ -3466,7 +3466,7 @@ int uv_fs_readdir(uv_loop_t* loop, int uv_fs_closedir(uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, - uv_fs_cb cb) { + uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { INIT(UV_FS_CLOSEDIR); if (dir == NULL) { SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); @@ -3477,7 +3477,7 @@ int uv_fs_closedir(uv_loop_t* loop, } int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, - const char* new_path, uv_fs_cb cb) { + const char* new_path, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_LINK); @@ -3492,7 +3492,7 @@ int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, - const char* new_path, int flags, uv_fs_cb cb) { + const char* new_path, int flags, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_SYMLINK); @@ -3508,7 +3508,7 @@ int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, - uv_fs_cb cb) { + uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_READLINK); @@ -3523,7 +3523,7 @@ int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, - uv_fs_cb cb) { + uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_REALPATH); @@ -3544,7 +3544,7 @@ int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, - uv_gid_t gid, uv_fs_cb cb) { + uv_gid_t gid, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_CHOWN); @@ -3559,14 +3559,14 @@ int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_uid_t uid, - uv_gid_t gid, uv_fs_cb cb) { + uv_gid_t gid, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { INIT(UV_FS_FCHOWN); POST; } int uv_fs_lchown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, - uv_gid_t gid, uv_fs_cb cb) { + uv_gid_t gid, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_LCHOWN); @@ -3580,7 +3580,7 @@ int uv_fs_lchown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, } -int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { +int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_STAT); @@ -3594,7 +3594,7 @@ int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { } -int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { +int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_LSTAT); @@ -3608,7 +3608,7 @@ int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { } -int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { +int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { INIT(UV_FS_FSTAT); req->file.fd = fd; POST; @@ -3616,7 +3616,7 @@ int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, - const char* new_path, uv_fs_cb cb) { + const char* new_path, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_RENAME); @@ -3630,14 +3630,14 @@ int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, } -int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { +int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { INIT(UV_FS_FSYNC); req->file.fd = fd; POST; } -int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { +int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { INIT(UV_FS_FDATASYNC); req->file.fd = fd; POST; @@ -3645,7 +3645,7 @@ int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file fd, - int64_t offset, uv_fs_cb cb) { + int64_t offset, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { INIT(UV_FS_FTRUNCATE); req->file.fd = fd; req->fs.info.offset = offset; @@ -3658,7 +3658,7 @@ int uv_fs_copyfile(uv_loop_t* loop, const char* path, const char* new_path, int flags, - uv_fs_cb cb) { + uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_COPYFILE); @@ -3682,7 +3682,7 @@ int uv_fs_copyfile(uv_loop_t* loop, int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file fd_out, - uv_file fd_in, int64_t in_offset, size_t length, uv_fs_cb cb) { + uv_file fd_in, int64_t in_offset, size_t length, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { INIT(UV_FS_SENDFILE); req->file.fd = fd_in; req->fs.info.fd_out = fd_out; @@ -3696,7 +3696,7 @@ int uv_fs_access(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, - uv_fs_cb cb) { + uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_ACCESS); @@ -3712,7 +3712,7 @@ int uv_fs_access(uv_loop_t* loop, int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, - uv_fs_cb cb) { + uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_CHMOD); @@ -3728,7 +3728,7 @@ int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file fd, int mode, - uv_fs_cb cb) { + uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { INIT(UV_FS_FCHMOD); req->file.fd = fd; req->fs.info.mode = mode; @@ -3737,7 +3737,7 @@ int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file fd, int mode, int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, - double mtime, uv_fs_cb cb) { + double mtime, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_UTIME); @@ -3754,7 +3754,7 @@ int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file fd, double atime, - double mtime, uv_fs_cb cb) { + double mtime, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { INIT(UV_FS_FUTIME); req->file.fd = fd; req->fs.time.atime = atime; @@ -3763,7 +3763,7 @@ int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file fd, double atime, } int uv_fs_lutime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, - double mtime, uv_fs_cb cb) { + double mtime, uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_LUTIME); @@ -3782,7 +3782,7 @@ int uv_fs_lutime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, int uv_fs_statfs(uv_loop_t* loop, uv_fs_t* req, const char* path, - uv_fs_cb cb) { + uv_fs_cb cb) UV_EXCLUDES(&uv__fd_hash_mutex) { int err; INIT(UV_FS_STATFS); diff --git a/src/win/poll.c b/src/win/poll.c index a20867a99..68dd57310 100644 --- a/src/win/poll.c +++ b/src/win/poll.c @@ -63,7 +63,7 @@ static void uv__init_overlapped_dummy(void) { } -static OVERLAPPED* uv__get_overlapped_dummy(void) { +static OVERLAPPED* uv__get_overlapped_dummy(void) UV_EXCLUDES(&overlapped_dummy_init_guard_) { uv_once(&overlapped_dummy_init_guard_, uv__init_overlapped_dummy); return &overlapped_dummy_; } @@ -530,7 +530,7 @@ void uv__process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) { } -int uv__poll_close(uv_loop_t* loop, uv_poll_t* handle) { +int uv__poll_close(uv_loop_t* loop, uv_poll_t* handle) UV_EXCLUDES(&overlapped_dummy_init_guard_) { AFD_POLL_INFO afd_poll_info; DWORD error; int result; diff --git a/src/win/process.c b/src/win/process.c index 27605ca36..17283be6d 100644 --- a/src/win/process.c +++ b/src/win/process.c @@ -889,7 +889,7 @@ void uv__process_endgame(uv_loop_t* loop, uv_process_t* handle) { int uv_spawn(uv_loop_t* loop, uv_process_t* process, - const uv_process_options_t* options) { + const uv_process_options_t* options) UV_EXCLUDES(&uv_global_job_handle_init_guard_) { int i; int err = 0; WCHAR* path = NULL, *alloc_path = NULL; diff --git a/src/win/thread.c b/src/win/thread.c index 9c38e3d8b..38261e3f2 100644 --- a/src/win/thread.c +++ b/src/win/thread.c @@ -46,7 +46,7 @@ static BOOL WINAPI uv__once_inner(INIT_ONCE *once, void* param, void** context) return TRUE; } -void uv_once(uv_once_t* guard, uv__once_cb callback) { +void uv_once(uv_once_t* guard, uv__once_cb callback) UV_NO_THREAD_SAFETY_ANALYSIS { uv__once_data_t data = { .callback = callback }; InitOnceExecuteOnce(&guard->init_once, uv__once_inner, (void*) &data, NULL); } @@ -75,7 +75,7 @@ struct thread_ctx { }; -static UINT __stdcall uv__thread_start(void* arg) { +static UINT __stdcall uv__thread_start(void* arg) UV_EXCLUDES(&uv__current_thread_init_guard) { struct thread_ctx *ctx_p; struct thread_ctx ctx; @@ -246,7 +246,8 @@ int uv_thread_getcpu(void) { return GetCurrentProcessorNumber(); } -uv_thread_t uv_thread_self(void) { + +uv_thread_t uv_thread_self(void) UV_EXCLUDES(&uv__current_thread_init_guard) { uv_thread_t key; uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key); key = uv_key_get(&uv__current_thread_key); @@ -368,7 +369,7 @@ int uv_thread_getname(uv_thread_t* tid, char* name, size_t size) { int uv_mutex_init(uv_mutex_t* mutex) { - InitializeCriticalSection(mutex); + InitializeCriticalSection(&mutex->cs); return 0; } @@ -379,25 +380,25 @@ int uv_mutex_init_recursive(uv_mutex_t* mutex) { void uv_mutex_destroy(uv_mutex_t* mutex) { - DeleteCriticalSection(mutex); + DeleteCriticalSection(&mutex->cs); } -void uv_mutex_lock(uv_mutex_t* mutex) { - EnterCriticalSection(mutex); +void uv_mutex_lock(uv_mutex_t* mutex) UV_NO_THREAD_SAFETY_ANALYSIS { + EnterCriticalSection(&mutex->cs); } -int uv_mutex_trylock(uv_mutex_t* mutex) { - if (TryEnterCriticalSection(mutex)) +int uv_mutex_trylock(uv_mutex_t* mutex) UV_NO_THREAD_SAFETY_ANALYSIS { + if (TryEnterCriticalSection(&mutex->cs)) return 0; else return UV_EBUSY; } -void uv_mutex_unlock(uv_mutex_t* mutex) { - LeaveCriticalSection(mutex); +void uv_mutex_unlock(uv_mutex_t* mutex) UV_NO_THREAD_SAFETY_ANALYSIS { + LeaveCriticalSection(&mutex->cs); } /* Ensure that the ABI for this type remains stable in v1.x */ @@ -421,12 +422,12 @@ void uv_rwlock_destroy(uv_rwlock_t* rwlock) { } -void uv_rwlock_rdlock(uv_rwlock_t* rwlock) { +void uv_rwlock_rdlock(uv_rwlock_t* rwlock) UV_NO_THREAD_SAFETY_ANALYSIS { AcquireSRWLockShared(&rwlock->read_write_lock_); } -int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) { +int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) UV_NO_THREAD_SAFETY_ANALYSIS { if (!TryAcquireSRWLockShared(&rwlock->read_write_lock_)) return UV_EBUSY; @@ -434,17 +435,17 @@ int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) { } -void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) { +void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) UV_NO_THREAD_SAFETY_ANALYSIS { ReleaseSRWLockShared(&rwlock->read_write_lock_); } -void uv_rwlock_wrlock(uv_rwlock_t* rwlock) { +void uv_rwlock_wrlock(uv_rwlock_t* rwlock) UV_NO_THREAD_SAFETY_ANALYSIS { AcquireSRWLockExclusive(&rwlock->read_write_lock_); } -int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) { +int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) UV_NO_THREAD_SAFETY_ANALYSIS { if (!TryAcquireSRWLockExclusive(&rwlock->read_write_lock_)) return UV_EBUSY; @@ -452,7 +453,7 @@ int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) { } -void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) { +void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) UV_NO_THREAD_SAFETY_ANALYSIS { ReleaseSRWLockExclusive(&rwlock->read_write_lock_); } @@ -521,13 +522,13 @@ void uv_cond_broadcast(uv_cond_t* cond) { void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) { - if (!SleepConditionVariableCS(&cond->cond_var, mutex, INFINITE)) + if (!SleepConditionVariableCS(&cond->cond_var, &mutex->cs, INFINITE)) abort(); } int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { - if (SleepConditionVariableCS(&cond->cond_var, mutex, (DWORD)(timeout / 1e6))) + if (SleepConditionVariableCS(&cond->cond_var, &mutex->cs, (DWORD)(timeout / 1e6))) return 0; if (GetLastError() != ERROR_TIMEOUT) abort(); diff --git a/src/win/tty.c b/src/win/tty.c index 66ca99cda..3d95eaa7d 100644 --- a/src/win/tty.c +++ b/src/win/tty.c @@ -2424,7 +2424,7 @@ static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param) { return 0; } -static void uv__tty_console_signal_resize(void) { +static void uv__tty_console_signal_resize(void) UV_EXCLUDES(&uv__tty_console_resize_mutex) { CONSOLE_SCREEN_BUFFER_INFO sb_info; int width, height; diff --git a/test/test-async.c b/test/test-async.c index 935436ec0..0d70d2a94 100644 --- a/test/test-async.c +++ b/test/test-async.c @@ -35,7 +35,7 @@ static int prepare_cb_called; static int close_cb_called; -static void thread_cb(void *arg) { +static void thread_cb(void *arg) UV_EXCLUDES(&mutex) { int n; int r; @@ -75,7 +75,7 @@ static void close_cb(uv_handle_t* handle) { } -static void async_cb(uv_async_t* handle) { +static void async_cb(uv_async_t* handle) UV_EXCLUDES(&mutex) { int n; ASSERT_PTR_EQ(handle, &async); @@ -99,13 +99,14 @@ static void prepare_cb(uv_prepare_t* handle) { if (prepare_cb_called++) return; + uv_mutex_assume_locked(&mutex); r = uv_thread_create(&thread, thread_cb, NULL); ASSERT_OK(r); uv_mutex_unlock(&mutex); } -TEST_IMPL(async) { +TEST_IMPL(async) UV_EXCLUDES(&mutex) { int r; r = uv_mutex_init(&mutex); @@ -115,8 +116,11 @@ TEST_IMPL(async) { r = uv_prepare_init(uv_default_loop(), &prepare); ASSERT_OK(r); r = uv_prepare_start(&prepare, prepare_cb); + /* This is true since prepare_cb must run at least once before async_cb. */ + uv_mutex_assume_unlocked(&mutex); ASSERT_OK(r); + uv_mutex_assert_unlocked(&mutex); r = uv_async_init(uv_default_loop(), &async, async_cb); ASSERT_OK(r); diff --git a/test/test-fs-fd-hash.c b/test/test-fs-fd-hash.c index a9ae25223..b97b788e7 100644 --- a/test/test-fs-fd-hash.c +++ b/test/test-fs-fd-hash.c @@ -34,19 +34,19 @@ #define FD_DIFF 9 -void assert_nonexistent(int fd) { +void assert_nonexistent(int fd) UV_EXCLUDES(&uv__fd_hash_mutex) { struct uv__fd_info_s info = { 0 }; ASSERT(!uv__fd_hash_get(fd, &info)); ASSERT(!uv__fd_hash_remove(fd, &info)); } -void assert_existent(int fd) { +void assert_existent(int fd) UV_EXCLUDES(&uv__fd_hash_mutex) { struct uv__fd_info_s info = { 0 }; ASSERT(uv__fd_hash_get(fd, &info)); ASSERT_EQ(info.flags, fd + FD_DIFF); } -void assert_insertion(int fd) { +void assert_insertion(int fd) UV_EXCLUDES(&uv__fd_hash_mutex) { struct uv__fd_info_s info = { 0 }; assert_nonexistent(fd); info.flags = fd + FD_DIFF; @@ -54,7 +54,7 @@ void assert_insertion(int fd) { assert_existent(fd); } -void assert_removal(int fd) { +void assert_removal(int fd) UV_EXCLUDES(&uv__fd_hash_mutex) { struct uv__fd_info_s info = { 0 }; assert_existent(fd); uv__fd_hash_remove(fd, &info); @@ -88,7 +88,7 @@ void assert_removal(int fd) { } while (0) -TEST_IMPL(fs_fd_hash) { +TEST_IMPL(fs_fd_hash) UV_EXCLUDES(&uv__fd_hash_mutex) { int fd; uv__fd_hash_init(); diff --git a/test/test-mutexes.c b/test/test-mutexes.c index ca3377324..a214971da 100644 --- a/test/test-mutexes.c +++ b/test/test-mutexes.c @@ -50,7 +50,7 @@ TEST_IMPL(thread_mutex) { } -TEST_IMPL(thread_mutex_recursive) { +TEST_IMPL(thread_mutex_recursive) UV_NO_THREAD_SAFETY_ANALYSIS { uv_mutex_t mutex; int r; @@ -88,14 +88,14 @@ TEST_IMPL(thread_rwlock) { /* Call when holding |mutex|. */ -static void synchronize_nowait(void) { +static void synchronize_nowait(void) UV_REQUIRES(&mutex) { step += 1; uv_cond_signal(&condvar); } /* Call when holding |mutex|. */ -static void synchronize(void) { +static void synchronize(void) UV_REQUIRES(&mutex) { int current; synchronize_nowait(); @@ -105,7 +105,7 @@ static void synchronize(void) { } -static void thread_rwlock_trylock_peer(void* unused) { +static void thread_rwlock_trylock_peer(void* unused) UV_EXCLUDES(&mutex, &rwlock) { (void) &unused; uv_mutex_lock(&mutex); @@ -136,7 +136,7 @@ static void thread_rwlock_trylock_peer(void* unused) { } -TEST_IMPL(thread_rwlock_trylock) { +TEST_IMPL(thread_rwlock_trylock) UV_EXCLUDES(&mutex, &rwlock) { uv_thread_t thread; ASSERT_OK(uv_cond_init(&condvar)); diff --git a/test/test-signal-multiple-loops.c b/test/test-signal-multiple-loops.c index e68eabf6f..6eb996ed6 100644 --- a/test/test-signal-multiple-loops.c +++ b/test/test-signal-multiple-loops.c @@ -51,35 +51,43 @@ enum signal_action { static uv_sem_t sem; static uv_mutex_t lock; -static int stop = 0; +static int stop UV_GUARDED_BY(&lock) = 0; -static int signal1_cb_counter = 0; -static int signal2_cb_counter = 0; -static int loop_creation_counter = 0; +static int signal1_cb_counter UV_GUARDED_BY(&lock) = 0; +static int signal2_cb_counter UV_GUARDED_BY(&lock) = 0; +static int loop_creation_counter UV_GUARDED_BY(&lock) = 0; -static void increment_counter(int* counter) { +/* Since clang analyzer cannot handle the implications of UV_PT_GUARDED_BY on a + * parameter, define this using a macro instead for now: +static void increment_counter(int* counter UV_PT_GUARDED_BY(&lock)) UV_EXCLUDES(&lock) { uv_mutex_lock(&lock); ++(*counter); uv_mutex_unlock(&lock); } +*/ + +#define increment_counter(counter) \ + uv_mutex_lock(&lock); \ + ++(*counter); \ + uv_mutex_unlock(&lock); -static void signal1_cb(uv_signal_t* handle, int signum) { +static void signal1_cb(uv_signal_t* handle, int signum) UV_EXCLUDES(&lock) { ASSERT_EQ(signum, SIGUSR1); increment_counter(&signal1_cb_counter); uv_signal_stop(handle); } -static void signal2_cb(uv_signal_t* handle, int signum) { +static void signal2_cb(uv_signal_t* handle, int signum) UV_EXCLUDES(&lock) { ASSERT_EQ(signum, SIGUSR2); increment_counter(&signal2_cb_counter); uv_signal_stop(handle); } -static void signal_handling_worker(void* context) { +static void signal_handling_worker(void* context) UV_EXCLUDES(&lock) { enum signal_action action; uv_signal_t signal1a; uv_signal_t signal1b; @@ -156,12 +164,12 @@ static void signal_handling_worker(void* context) { } -static void signal_unexpected_cb(uv_signal_t* handle, int signum) { +static void signal_unexpected_cb(uv_signal_t* handle, int signum) UV_EXCLUDES(&lock) { ASSERT(0 && "signal_unexpected_cb should never be called"); } -static void loop_creating_worker(void* context) { +static void loop_creating_worker(void* context) UV_EXCLUDES(&lock) { int done; (void) context; @@ -198,7 +206,7 @@ static void loop_creating_worker(void* context) { } -TEST_IMPL(signal_multiple_loops) { +TEST_IMPL(signal_multiple_loops) UV_EXCLUDES(&lock) { #if defined(__CYGWIN__) || defined(__MSYS__) /* FIXME: This test needs more investigation. Somehow the `read` in uv__signal_lock fails spuriously with EACCES or even EAGAIN even @@ -299,6 +307,8 @@ TEST_IMPL(signal_multiple_loops) { } uv_sem_destroy(&sem); + + uv_mutex_lock(&lock); /* Unnecessary but makes static analysis happy. */ printf("signal1_cb calls: %d\n", signal1_cb_counter); printf("signal2_cb calls: %d\n", signal2_cb_counter); printf("loops created and destroyed: %d\n", loop_creation_counter); @@ -313,6 +323,7 @@ TEST_IMPL(signal_multiple_loops) { * least there should be 1 for every loop creating thread. */ ASSERT_GE(loop_creation_counter, NUM_LOOP_CREATING_THREADS); + uv_mutex_unlock(&lock); MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; diff --git a/test/test-tcp-reuseport.c b/test/test-tcp-reuseport.c index f108b9bbe..e8b6bc737 100644 --- a/test/test-tcp-reuseport.c +++ b/test/test-tcp-reuseport.c @@ -80,7 +80,7 @@ static void on_close(uv_handle_t* handle) { free(handle); } -static void ticktack(uv_timer_t* timer) { +static void ticktack(uv_timer_t* timer) UV_EXCLUDES(&mutex) { ASSERT(timer == &thread_timer_handle1 || timer == &thread_timer_handle2); int done = 0; @@ -99,8 +99,7 @@ static void ticktack(uv_timer_t* timer) { } } -static void on_connection(uv_stream_t* server, int status) -{ +static void on_connection(uv_stream_t* server, int status) UV_EXCLUDES(&mutex) { ASSERT_OK(status); ASSERT(server == (uv_stream_t*) &thread_handle1 || \ server == (uv_stream_t*) &thread_handle2);