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>
diff --git a/crypto/internal.h b/crypto/internal.h
index e8f4ef7..6a8d5b2 100644
--- a/crypto/internal.h
+++ b/crypto/internal.h
@@ -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 @@
 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
diff --git a/crypto/thread_pthread.c b/crypto/thread_pthread.c
index 1516ea1..4188c4c 100644
--- a/crypto/thread_pthread.c
+++ b/crypto/thread_pthread.c
@@ -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);
 }
diff --git a/crypto/thread_win.c b/crypto/thread_win.c
index 240444f..7a4f9bc 100644
--- a/crypto/thread_win.c
+++ b/crypto/thread_win.c
@@ -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 @@
       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 @@
   }
 }
 
+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];