ex_data: convert to new-style locking.

Change-Id: Id81f986f433805837b58a6ddd13ec146ff62c30b
Reviewed-on: https://boringssl-review.googlesource.com/4323
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/ex_data.c b/crypto/ex_data.c
index d399b83..7002eec 100644
--- a/crypto/ex_data.c
+++ b/crypto/ex_data.c
@@ -122,37 +122,32 @@
 /* global_impl is the implementation that we use at runtime. */
 static const CRYPTO_EX_DATA_IMPL *global_impl = NULL;
 
+/* global_impl_once protects the call to |global_impl_init|. */
+static CRYPTO_once_t global_impl_once = CRYPTO_ONCE_INIT;
+
+/* global_user_set_impl is a pointer to a user provided implementation. If this
+ * is non-NULL at the point of the first use of ex_data, |global_impl| will be
+ * set to this value. */
+static const CRYPTO_EX_DATA_IMPL *global_user_set_impl = NULL;
+
 /* ex_data_default_impl is a the default implementation, defined in
  * ex_data_impl.c. */
 extern const CRYPTO_EX_DATA_IMPL ex_data_default_impl;
 
-/* get_impl returns the current ex_data implementatation. */
-static const CRYPTO_EX_DATA_IMPL *get_impl(void) {
-  const CRYPTO_EX_DATA_IMPL *impl;
-
-  CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA);
-  impl = global_impl;
-  CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA);
-
-  if (impl != NULL) {
-    return impl;
-  }
-
-  CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
+/* global_impl_init is called once in order to set |global_impl|. */
+static void global_impl_init(void) {
+  global_impl = global_user_set_impl;
   if (global_impl == NULL) {
     global_impl = &ex_data_default_impl;
   }
-  impl = global_impl;
-  CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
-  return impl;
 }
 
 int CRYPTO_get_ex_new_index(int class_value, long argl, void *argp,
                             CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
                             CRYPTO_EX_free *free_func) {
-  const CRYPTO_EX_DATA_IMPL *const impl = get_impl();
-  return impl->get_new_index(class_value, argl, argp, new_func, dup_func,
-                             free_func);
+  CRYPTO_once(&global_impl_once, global_impl_init);
+  return global_impl->get_new_index(class_value, argl, argp, new_func, dup_func,
+                                    free_func);
 }
 
 int CRYPTO_set_ex_data(CRYPTO_EX_DATA *ad, int index, void *val) {
@@ -188,44 +183,38 @@
 }
 
 int CRYPTO_ex_data_new_class(void) {
-  const CRYPTO_EX_DATA_IMPL *const impl = get_impl();
-  return impl->new_class();
+  CRYPTO_once(&global_impl_once, global_impl_init);
+  return global_impl->new_class();
 }
 
 int CRYPTO_new_ex_data(int class_value, void *obj, CRYPTO_EX_DATA *ad) {
-  const CRYPTO_EX_DATA_IMPL *const impl = get_impl();
-  return impl->new_ex_data(class_value, obj, ad);
+  CRYPTO_once(&global_impl_once, global_impl_init);
+  return global_impl->new_ex_data(class_value, obj, ad);
 }
 
 int CRYPTO_dup_ex_data(int class_value, CRYPTO_EX_DATA *to,
                        const CRYPTO_EX_DATA *from) {
-  const CRYPTO_EX_DATA_IMPL *const impl = get_impl();
-  return impl->dup_ex_data(class_value, to, from);
+  CRYPTO_once(&global_impl_once, global_impl_init);
+  return global_impl->dup_ex_data(class_value, to, from);
 }
 
 void CRYPTO_free_ex_data(int class_value, void *obj, CRYPTO_EX_DATA *ad) {
-  const CRYPTO_EX_DATA_IMPL *const impl = get_impl();
-  impl->free_ex_data(class_value, obj, ad);
+  CRYPTO_once(&global_impl_once, global_impl_init);
+  global_impl->free_ex_data(class_value, obj, ad);
 }
 
 const CRYPTO_EX_DATA_IMPL *CRYPTO_get_ex_data_implementation(void) {
-  return get_impl();
+  CRYPTO_once(&global_impl_once, global_impl_init);
+  return global_impl;
 }
 
 int CRYPTO_set_ex_data_implementation(const CRYPTO_EX_DATA_IMPL *impl) {
-  int ret = 0;
-
-  CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
-  if (global_impl == NULL) {
-    ret = 1;
-    global_impl = impl;
-  }
-  CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
-
-  return ret;
+  global_user_set_impl = impl;
+  CRYPTO_once(&global_impl_once, global_impl_init);
+  return (global_impl == impl);
 }
 
 void CRYPTO_cleanup_all_ex_data(void) {
-  const CRYPTO_EX_DATA_IMPL *const impl = get_impl();
-  impl->cleanup();
+  CRYPTO_once(&global_impl_once, global_impl_init);
+  global_impl->cleanup();
 }
diff --git a/crypto/ex_data_impl.c b/crypto/ex_data_impl.c
index f55b369..09295df 100644
--- a/crypto/ex_data_impl.c
+++ b/crypto/ex_data_impl.c
@@ -132,15 +132,19 @@
   int class_value;
 } EX_CLASS_ITEM;
 
+static CRYPTO_once_t global_classes_once = CRYPTO_ONCE_INIT;
+static struct CRYPTO_STATIC_MUTEX global_classes_lock =
+    CRYPTO_STATIC_MUTEX_INIT;
 static LHASH_OF(EX_CLASS_ITEM) *global_classes = NULL;
 
+static struct CRYPTO_STATIC_MUTEX global_next_class_lock =
+    CRYPTO_STATIC_MUTEX_INIT;
 static int global_next_class = 100;
 
 static int new_class(void) {
-  int ret;
-  CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
-  ret = global_next_class++;
-  CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
+  CRYPTO_STATIC_MUTEX_lock_write(&global_next_class_lock);
+  const int ret = global_next_class++;
+  CRYPTO_STATIC_MUTEX_unlock(&global_next_class_lock);
 
   return ret;
 }
@@ -170,50 +174,34 @@
   OPENSSL_free(item);
 }
 
-static LHASH_OF(EX_CLASS_ITEM) *get_classes(void) {
-  LHASH_OF(EX_CLASS_ITEM) *ret;
-
-  CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA);
-  ret = global_classes;
-  CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA);
-
-  if (ret != NULL) {
-    return ret;
-  }
-
-  CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
-  if (global_classes == NULL) {
-    global_classes = lh_EX_CLASS_ITEM_new(class_hash, class_cmp);
-  }
-  ret = global_classes;
-  CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
-
-  return ret;
+static void global_classes_init(void) {
+  global_classes = lh_EX_CLASS_ITEM_new(class_hash, class_cmp);
 }
 
 static void cleanup(void) {
-  LHASH_OF(EX_CLASS_ITEM) *classes = get_classes();
+  CRYPTO_once(&global_classes_once, global_classes_init);
 
-  if (classes != NULL) {
-    lh_EX_CLASS_ITEM_doall(classes, class_free);
-    lh_EX_CLASS_ITEM_free(classes);
+  if (global_classes != NULL) {
+    lh_EX_CLASS_ITEM_doall(global_classes, class_free);
+    lh_EX_CLASS_ITEM_free(global_classes);
   }
 
   global_classes = NULL;
 }
 
 static EX_CLASS_ITEM *get_class(int class_value) {
-  LHASH_OF(EX_CLASS_ITEM) *const classes = get_classes();
   EX_CLASS_ITEM template, *class_item;
   int ok = 0;
 
-  if (classes == NULL) {
+  CRYPTO_once(&global_classes_once, global_classes_init);
+
+  if (global_classes == NULL) {
     return NULL;
   }
 
-  CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
+  CRYPTO_STATIC_MUTEX_lock_write(&global_classes_lock);
   template.class_value = class_value;
-  class_item = lh_EX_CLASS_ITEM_retrieve(classes, &template);
+  class_item = lh_EX_CLASS_ITEM_retrieve(global_classes, &template);
   if (class_item != NULL) {
     ok = 1;
   } else {
@@ -223,12 +211,12 @@
       class_item->meth = sk_CRYPTO_EX_DATA_FUNCS_new_null();
       if (class_item->meth != NULL) {
         EX_CLASS_ITEM *old_data;
-        ok = lh_EX_CLASS_ITEM_insert(classes, &old_data, class_item);
+        ok = lh_EX_CLASS_ITEM_insert(global_classes, &old_data, class_item);
         assert(old_data == NULL);
       }
     }
   }
-  CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
+  CRYPTO_STATIC_MUTEX_unlock(&global_classes_lock);
 
   if (!ok) {
     if (class_item) {
@@ -268,7 +256,7 @@
   funcs->dup_func = dup_func;
   funcs->free_func = free_func;
 
-  CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
+  CRYPTO_STATIC_MUTEX_lock_write(&global_classes_lock);
 
   if (!sk_CRYPTO_EX_DATA_FUNCS_push(item->meth, funcs)) {
     OPENSSL_PUT_ERROR(CRYPTO, get_new_index, ERR_R_MALLOC_FAILURE);
@@ -279,7 +267,7 @@
   ret = sk_CRYPTO_EX_DATA_FUNCS_num(item->meth) - 1;
 
 err:
-  CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
+  CRYPTO_STATIC_MUTEX_unlock(&global_classes_lock);
   return ret;
 }
 
@@ -301,12 +289,12 @@
   /* CRYPTO_EX_DATA_FUNCS structures are static once set, so we can take a
    * shallow copy of the list under lock and then use the structures without
    * the lock held. */
-  CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA);
+  CRYPTO_STATIC_MUTEX_lock_read(&global_classes_lock);
   n = sk_CRYPTO_EX_DATA_FUNCS_num(item->meth);
   if (n > 0) {
     *out = sk_CRYPTO_EX_DATA_FUNCS_dup(item->meth);
   }
-  CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA);
+  CRYPTO_STATIC_MUTEX_unlock(&global_classes_lock);
 
   if (n > 0 && *out == NULL) {
     OPENSSL_PUT_ERROR(CRYPTO, get_func_pointers, ERR_R_MALLOC_FAILURE);