return NULL;
}
+int ossl_crypto_thread_native_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval)
+{
+ uint64_t req_state_mask;
+
+ if (thread == NULL)
+ return 0;
+
+ ossl_crypto_mutex_lock(thread->statelock);
+ req_state_mask = CRYPTO_THREAD_TERMINATED | CRYPTO_THREAD_FINISHED \
+ | CRYPTO_THREAD_JOINED;
+ while (!CRYPTO_THREAD_GET_STATE(thread, req_state_mask))
+ ossl_crypto_condvar_wait(thread->condvar, thread->statelock);
+
+ if (CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_TERMINATED)) {
+ ossl_crypto_mutex_unlock(thread->statelock);
+ return 0;
+ }
+
+ if (CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_JOINED))
+ goto pass;
+
+ /* Await concurrent join completion, if any. */
+ while (CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_JOIN_AWAIT)) {
+ if (!CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_JOINED))
+ ossl_crypto_condvar_wait(thread->condvar, thread->statelock);
+ if (CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_JOINED))
+ goto pass;
+ }
+ CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_JOIN_AWAIT);
+ ossl_crypto_mutex_unlock(thread->statelock);
+
+ if (ossl_crypto_thread_native_perform_join(thread, retval) == 0)
+ goto fail;
+
+ ossl_crypto_mutex_lock(thread->statelock);
+pass:
+ CRYPTO_THREAD_UNSET_ERROR(thread, CRYPTO_THREAD_JOINED);
+ CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_JOINED);
+
+ /*
+ * Broadcast join completion. It is important to broadcast even if
+ * we haven't performed an actual join. Multiple threads could be
+ * awaiting the CRYPTO_THREAD_JOIN_AWAIT -> CRYPTO_THREAD_JOINED
+ * transition, but broadcast on actual join would wake only one.
+ * Broadcasing here will always wake one.
+ */
+ ossl_crypto_condvar_broadcast(thread->condvar);
+ ossl_crypto_mutex_unlock(thread->statelock);
+
+ if (retval != NULL)
+ *retval = thread->retval;
+ return 1;
+
+fail:
+ ossl_crypto_mutex_lock(thread->statelock);
+ CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_JOINED);
+
+ /* Have another thread that's awaiting join retry to avoid that
+ * thread deadlock. */
+ CRYPTO_THREAD_UNSET_STATE(thread, CRYPTO_THREAD_JOIN_AWAIT);
+ ossl_crypto_condvar_broadcast(thread->condvar);
+
+ ossl_crypto_mutex_unlock(thread->statelock);
+ return 0;
+}
+
int ossl_crypto_thread_native_clean(CRYPTO_THREAD *handle)
{
uint64_t req_state_mask;
return 0;
}
-int ossl_crypto_thread_native_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval)
+int ossl_crypto_thread_native_perform_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval)
{
return 0;
}
return 0;
}
-int ossl_crypto_thread_native_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval)
+int ossl_crypto_thread_native_perform_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval)
{
void *thread_retval;
pthread_t *handle;
- uint64_t req_state_mask;
- if (thread == NULL)
+ if (thread == NULL || thread->handle == NULL)
return 0;
- req_state_mask = CRYPTO_THREAD_TERMINATED | CRYPTO_THREAD_JOINED;
-
- ossl_crypto_mutex_lock(thread->statelock);
- if (CRYPTO_THREAD_GET_STATE(thread, req_state_mask)) {
- ossl_crypto_mutex_unlock(thread->statelock);
- goto pass;
- }
- while (!CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_FINISHED))
- ossl_crypto_condvar_wait(thread->condvar, thread->statelock);
- ossl_crypto_mutex_unlock(thread->statelock);
-
handle = (pthread_t *) thread->handle;
- if (handle == NULL)
- goto fail;
-
if (pthread_join(*handle, &thread_retval) != 0)
- goto fail;
+ return 0;
/*
* Join return value may be non-NULL when the thread has been cancelled,
* as indicated by thread_retval set to PTHREAD_CANCELLED.
*/
if (thread_retval != NULL)
- goto fail;
-
-pass:
- if (retval != NULL)
- *retval = thread->retval;
+ return 0;
- ossl_crypto_mutex_lock(thread->statelock);
- CRYPTO_THREAD_UNSET_ERROR(thread, CRYPTO_THREAD_JOINED);
- CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_JOINED);
- ossl_crypto_mutex_unlock(thread->statelock);
return 1;
-
-fail:
- ossl_crypto_mutex_lock(thread->statelock);
- CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_JOINED);
- ossl_crypto_mutex_unlock(thread->statelock);
- return 0;
}
int ossl_crypto_thread_native_terminate(CRYPTO_THREAD *thread)
ossl_crypto_mutex_lock(thread->statelock);
if (thread->handle == NULL || CRYPTO_THREAD_GET_STATE(thread, mask))
goto terminated;
+ /* Do not fail when there's a join in progress. Do not block. */
+ if (CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_JOIN_AWAIT))
+ goto fail;
ossl_crypto_mutex_unlock(thread->statelock);
handle = thread->handle;
if (pthread_cancel(*handle) != 0) {
ossl_crypto_mutex_lock(thread->statelock);
- CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_TERMINATED);
- ossl_crypto_mutex_unlock(thread->statelock);
- return 0;
+ goto fail;
}
if (pthread_join(*handle, &res) != 0)
return 0;
CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_TERMINATED);
ossl_crypto_mutex_unlock(thread->statelock);
return 1;
+fail:
+ CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_TERMINATED);
+ ossl_crypto_mutex_unlock(thread->statelock);
+ return 0;
}
int ossl_crypto_thread_native_exit(void)
return 0;
}
-int ossl_crypto_thread_native_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval)
+int ossl_crypto_thread_native_perform_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval)
{
- int req_state_mask;
DWORD thread_retval;
HANDLE *handle;
- if (thread == NULL)
+ if (thread == NULL || thread->handle == NULL)
return 0;
- req_state_mask = CRYPTO_THREAD_TERMINATED | CRYPTO_THREAD_JOINED;
-
- ossl_crypto_mutex_lock(thread->statelock);
- if (CRYPTO_THREAD_GET_STATE(thread, req_state_mask))
- goto pass;
- while (!CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_FINISHED))
- ossl_crypto_condvar_wait(thread->condvar, thread->statelock);
-
handle = (HANDLE *) thread->handle;
- if (handle == NULL)
- goto fail;
-
if (WaitForSingleObject(*handle, INFINITE) != WAIT_OBJECT_0)
- goto fail;
+ return 0;
if (GetExitCodeThread(*handle, &thread_retval) == 0)
- goto fail;
+ return 0;
/*
* GetExitCodeThread call followed by this check is to make sure that
* if the thread is still active (returns STILL_ACTIVE (259)).
*/
if (thread_retval != 0)
- goto fail;
+ return 0;
if (CloseHandle(*handle) == 0)
- goto fail;
-
-pass:
- if (retval != NULL)
- *retval = thread->retval;
+ return 0;
- CRYPTO_THREAD_UNSET_ERROR(thread, CRYPTO_THREAD_JOINED);
- CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_JOINED);
- ossl_crypto_mutex_unlock(thread->statelock);
return 1;
-
-fail:
- CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_JOINED);
- ossl_crypto_mutex_unlock(thread->statelock);
- return 0;
}
int ossl_crypto_thread_native_terminate(CRYPTO_THREAD *thread)
void **);
# define CRYPTO_THREAD_NO_STATE 0UL
-# define CRYPTO_THREAD_FINISHED (1UL << 1)
+# define CRYPTO_THREAD_FINISHED (1UL << 0)
+# define CRYPTO_THREAD_JOIN_AWAIT (1UL << 1)
# define CRYPTO_THREAD_JOINED (1UL << 2)
# define CRYPTO_THREAD_TERMINATED (1UL << 3)
int ossl_crypto_thread_native_spawn(CRYPTO_THREAD *thread);
int ossl_crypto_thread_native_join(CRYPTO_THREAD *thread,
CRYPTO_THREAD_RETVAL *retval);
+int ossl_crypto_thread_native_perform_join(CRYPTO_THREAD *thread,
+ CRYPTO_THREAD_RETVAL *retval);
int ossl_crypto_thread_native_terminate(CRYPTO_THREAD *thread);
int ossl_crypto_thread_native_exit(void);
int ossl_crypto_thread_native_is_self(CRYPTO_THREAD *thread);
if (!TEST_int_eq(ossl_crypto_thread_native_terminate(t), 1))
return 0;
- if (!TEST_int_eq(ossl_crypto_thread_native_join(t, &retval), 1))
+ if (!TEST_int_eq(ossl_crypto_thread_native_join(t, &retval), 0))
return 0;
if (!TEST_int_eq(ossl_crypto_thread_native_clean(t), 1))