crypto: add mutexes.
Prior to this, BoringSSL was using OpenSSL's technique of having users register a callback for locking operation. This change adds native mutex support. Since mutexes often need to be in objects that are exposed via public headers, the non-static mutexes are defined in thread.h. However, on Windows we don't want to #include windows.h for CRITICAL_SECTION and, on Linux, pthread.h doesn't define pthread_rwlock_t unless the feature flags are set correctly—something that we can't control in general for public header files. Thus, on both platforms, the mutex is defined as a uint8_t[] of equal or greater size and we depend on static asserts to ensure that everything works out ok. Change-Id: Iafec17ae7e3422325e587878a5384107ec6647ab Reviewed-on: https://boringssl-review.googlesource.com/4321 Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
parent
9bde6aeb76
commit
df1f5e796c
@ -110,9 +110,14 @@
|
||||
#define OPENSSL_HEADER_CRYPTO_INTERNAL_H
|
||||
|
||||
#include <openssl/ex_data.h>
|
||||
#include <openssl/thread.h>
|
||||
|
||||
#if !defined(OPENSSL_WINDOWS)
|
||||
#include <pthread.h>
|
||||
#else
|
||||
#pragma warning(push, 3)
|
||||
#include <windows.h>
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
@ -360,6 +365,69 @@ typedef int32_t CRYPTO_once_t;
|
||||
OPENSSL_EXPORT void CRYPTO_once(CRYPTO_once_t *once, void (*init)(void));
|
||||
|
||||
|
||||
/* Locks.
|
||||
*
|
||||
* Two types of locks are defined: |CRYPTO_MUTEX|, which can be used in
|
||||
* structures as normal, and |struct CRYPTO_STATIC_MUTEX|, which can be used as
|
||||
* a global lock. A global lock must be initialised to the value
|
||||
* |CRYPTO_STATIC_MUTEX_INIT|.
|
||||
*
|
||||
* |CRYPTO_MUTEX| can appear in public structures and so is defined in
|
||||
* thread.h.
|
||||
*
|
||||
* The global lock is a different type because there's no static initialiser
|
||||
* value on Windows for locks, so global locks have to be coupled with a
|
||||
* |CRYPTO_once_t| to ensure that the lock is setup before use. This is done
|
||||
* automatically by |CRYPTO_STATIC_MUTEX_lock_*|. */
|
||||
|
||||
#if !defined(OPENSSL_WINDOWS)
|
||||
struct CRYPTO_STATIC_MUTEX {
|
||||
pthread_rwlock_t lock;
|
||||
};
|
||||
#define CRYPTO_STATIC_MUTEX_INIT { PTHREAD_RWLOCK_INITIALIZER }
|
||||
#else
|
||||
struct CRYPTO_STATIC_MUTEX {
|
||||
CRYPTO_once_t once;
|
||||
CRITICAL_SECTION lock;
|
||||
};
|
||||
#define CRYPTO_STATIC_MUTEX_INIT { CRYPTO_ONCE_INIT, { 0 } }
|
||||
#endif
|
||||
|
||||
/* CRYPTO_MUTEX_init initialises |lock|. If |lock| is a static variable, use a
|
||||
* |CRYPTO_STATIC_MUTEX|. */
|
||||
void CRYPTO_MUTEX_init(CRYPTO_MUTEX *lock);
|
||||
|
||||
/* CRYPTO_MUTEX_lock_read locks |lock| such that other threads may also have a
|
||||
* read lock, but none may have a write lock. (On Windows, read locks are
|
||||
* actually fully exclusive.) */
|
||||
void CRYPTO_MUTEX_lock_read(CRYPTO_MUTEX *lock);
|
||||
|
||||
/* CRYPTO_MUTEX_lock_write locks |lock| such that no other thread has any type
|
||||
* of lock on it. */
|
||||
void CRYPTO_MUTEX_lock_write(CRYPTO_MUTEX *lock);
|
||||
|
||||
/* CRYPTO_MUTEX_unlock unlocks |lock|. */
|
||||
void CRYPTO_MUTEX_unlock(CRYPTO_MUTEX *lock);
|
||||
|
||||
/* CRYPTO_MUTEX_cleanup releases all resources held by |lock|. */
|
||||
void CRYPTO_MUTEX_cleanup(CRYPTO_MUTEX *lock);
|
||||
|
||||
/* CRYPTO_STATIC_MUTEX_lock_read locks |lock| such that other threads may also
|
||||
* have a read lock, but none may have a write lock. The |lock| variable does
|
||||
* not need to be initialised by any function, but must have been statically
|
||||
* initialised with |CRYPTO_STATIC_MUTEX_INIT|. */
|
||||
void CRYPTO_STATIC_MUTEX_lock_read(struct CRYPTO_STATIC_MUTEX *lock);
|
||||
|
||||
/* CRYPTO_STATIC_MUTEX_lock_write locks |lock| such that no other thread has
|
||||
* any type of lock on it. The |lock| variable does not need to be initialised
|
||||
* by any function, but must have been statically initialised with
|
||||
* |CRYPTO_STATIC_MUTEX_INIT|. */
|
||||
void CRYPTO_STATIC_MUTEX_lock_write(struct CRYPTO_STATIC_MUTEX *lock);
|
||||
|
||||
/* CRYPTO_STATIC_MUTEX_unlock unlocks |lock|. */
|
||||
void CRYPTO_STATIC_MUTEX_unlock(struct CRYPTO_STATIC_MUTEX *lock);
|
||||
|
||||
|
||||
/* Thread local storage. */
|
||||
|
||||
/* thread_local_data_t enumerates the types of thread-local data that can be
|
||||
|
@ -17,11 +17,62 @@
|
||||
#if !defined(OPENSSL_WINDOWS)
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <openssl/mem.h>
|
||||
#include <openssl/type_check.h>
|
||||
|
||||
|
||||
OPENSSL_COMPILE_ASSERT(sizeof(CRYPTO_MUTEX) >= sizeof(pthread_rwlock_t),
|
||||
CRYPTO_MUTEX_too_small);
|
||||
|
||||
void CRYPTO_MUTEX_init(CRYPTO_MUTEX *lock) {
|
||||
if (pthread_rwlock_init((pthread_rwlock_t *) lock, NULL) != 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void CRYPTO_MUTEX_lock_read(CRYPTO_MUTEX *lock) {
|
||||
if (pthread_rwlock_rdlock((pthread_rwlock_t *) lock) != 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void CRYPTO_MUTEX_lock_write(CRYPTO_MUTEX *lock) {
|
||||
if (pthread_rwlock_wrlock((pthread_rwlock_t *) lock) != 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void CRYPTO_MUTEX_unlock(CRYPTO_MUTEX *lock) {
|
||||
if (pthread_rwlock_unlock((pthread_rwlock_t *) lock) != 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void CRYPTO_MUTEX_cleanup(CRYPTO_MUTEX *lock) {
|
||||
pthread_rwlock_destroy((pthread_rwlock_t *) lock);
|
||||
}
|
||||
|
||||
void CRYPTO_STATIC_MUTEX_lock_read(struct CRYPTO_STATIC_MUTEX *lock) {
|
||||
if (pthread_rwlock_rdlock(&lock->lock) != 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void CRYPTO_STATIC_MUTEX_lock_write(struct CRYPTO_STATIC_MUTEX *lock) {
|
||||
if (pthread_rwlock_wrlock(&lock->lock) != 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void CRYPTO_STATIC_MUTEX_unlock(struct CRYPTO_STATIC_MUTEX *lock) {
|
||||
if (pthread_rwlock_unlock(&lock->lock) != 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void CRYPTO_once(CRYPTO_once_t *once, void (*init)(void)) {
|
||||
pthread_once(once, init);
|
||||
}
|
||||
|
@ -21,12 +21,17 @@
|
||||
#pragma warning(pop)
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <openssl/mem.h>
|
||||
#include <openssl/type_check.h>
|
||||
|
||||
|
||||
void CRYPTO_once(CRYPTO_once_t *in_once, void (*init)(void)) {
|
||||
OPENSSL_COMPILE_ASSERT(sizeof(CRYPTO_MUTEX) >= sizeof(CRITICAL_SECTION),
|
||||
CRYPTO_MUTEX_too_small);
|
||||
|
||||
static void run_once(CRYPTO_once_t *in_once, void (*init)(void *), void *arg) {
|
||||
volatile LONG *once = (LONG*) in_once;
|
||||
|
||||
assert(sizeof(LONG) == sizeof(CRYPTO_once_t));
|
||||
@ -48,7 +53,7 @@ void CRYPTO_once(CRYPTO_once_t *in_once, void (*init)(void)) {
|
||||
case 0:
|
||||
/* The value was zero so we are the first thread to call |CRYPTO_once|
|
||||
* on it. */
|
||||
init();
|
||||
init(arg);
|
||||
/* Write one to indicate that initialisation is complete. */
|
||||
InterlockedExchange(once, 1);
|
||||
return;
|
||||
@ -70,6 +75,64 @@ void CRYPTO_once(CRYPTO_once_t *in_once, void (*init)(void)) {
|
||||
}
|
||||
}
|
||||
|
||||
static void call_once_init(void *arg) {
|
||||
void (*init_func)(void);
|
||||
/* MSVC does not like casting between data and function pointers. */
|
||||
memcpy(&init_func, &arg, sizeof(void *));
|
||||
init_func();
|
||||
}
|
||||
|
||||
void CRYPTO_once(CRYPTO_once_t *in_once, void (*init)(void)) {
|
||||
void *arg;
|
||||
/* MSVC does not like casting between data and function pointers. */
|
||||
memcpy(&arg, &init, sizeof(void *));
|
||||
run_once(in_once, call_once_init, arg);
|
||||
}
|
||||
|
||||
void CRYPTO_MUTEX_init(CRYPTO_MUTEX *lock) {
|
||||
if (!InitializeCriticalSectionAndSpinCount((CRITICAL_SECTION *) lock, 0x400)) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void CRYPTO_MUTEX_lock_read(CRYPTO_MUTEX *lock) {
|
||||
/* Since we have to support Windows XP, read locks are actually exclusive. */
|
||||
EnterCriticalSection((CRITICAL_SECTION *) lock);
|
||||
}
|
||||
|
||||
void CRYPTO_MUTEX_lock_write(CRYPTO_MUTEX *lock) {
|
||||
EnterCriticalSection((CRITICAL_SECTION *) lock);
|
||||
}
|
||||
|
||||
void CRYPTO_MUTEX_unlock(CRYPTO_MUTEX *lock) {
|
||||
LeaveCriticalSection((CRITICAL_SECTION *) lock);
|
||||
}
|
||||
|
||||
void CRYPTO_MUTEX_cleanup(CRYPTO_MUTEX *lock) {
|
||||
DeleteCriticalSection((CRITICAL_SECTION *) lock);
|
||||
}
|
||||
|
||||
static void static_lock_init(void *arg) {
|
||||
struct CRYPTO_STATIC_MUTEX *lock = arg;
|
||||
if (!InitializeCriticalSectionAndSpinCount(&lock->lock, 0x400)) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void CRYPTO_STATIC_MUTEX_lock_read(struct CRYPTO_STATIC_MUTEX *lock) {
|
||||
/* Since we have to support Windows XP, read locks are actually exclusive. */
|
||||
run_once(&lock->once, static_lock_init, lock);
|
||||
EnterCriticalSection(&lock->lock);
|
||||
}
|
||||
|
||||
void CRYPTO_STATIC_MUTEX_lock_write(struct CRYPTO_STATIC_MUTEX *lock) {
|
||||
CRYPTO_STATIC_MUTEX_lock_read(lock);
|
||||
}
|
||||
|
||||
void CRYPTO_STATIC_MUTEX_unlock(struct CRYPTO_STATIC_MUTEX *lock) {
|
||||
LeaveCriticalSection(&lock->lock);
|
||||
}
|
||||
|
||||
static CRITICAL_SECTION g_destructors_lock;
|
||||
static thread_local_destructor_t g_destructors[NUM_OPENSSL_THREAD_LOCALS];
|
||||
|
||||
|
@ -64,6 +64,27 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(OPENSSL_WINDOWS)
|
||||
/* CRYPTO_MUTEX can appear in public header files so we really don't want to
|
||||
* pull in windows.h. It's statically asserted that this structure is large
|
||||
* enough to contain a Windows CRITICAL_SECTION by thread_win.c. */
|
||||
typedef union crypto_mutex_st {
|
||||
double alignment;
|
||||
uint8_t padding[4*sizeof(void*) + 2*sizeof(int)];
|
||||
} CRYPTO_MUTEX;
|
||||
#else
|
||||
/* It is reasonable to include pthread.h on non-Windows systems, however the
|
||||
* |pthread_rwlock_t| that we need is hidden under feature flags, and we can't
|
||||
* ensure that we'll be able to get it. It's statically asserted that this
|
||||
* structure is large enough to contain a |pthread_rwlock_t| by
|
||||
* thread_pthread.c. */
|
||||
typedef union crypto_mutex_st {
|
||||
double alignment;
|
||||
uint8_t padding[3*sizeof(int) + 5*sizeof(unsigned) + 16 + 8];
|
||||
} CRYPTO_MUTEX;
|
||||
#endif
|
||||
|
||||
|
||||
/* Functions to support multithreading.
|
||||
*
|
||||
* OpenSSL can safely be used in multi-threaded applications provided that at
|
||||
|
Loading…
Reference in New Issue
Block a user