Author: brane
Date: Mon Jun 23 10:06:34 2025
New Revision: 1926660

URL: http://svn.apache.org/viewvc?rev=1926660&view=rev
Log:
Add a serialized-one-time-init helper function and use it in the OpenSSL
initialization code. We'll need it in other places, too, so it makes sense
to abstract the spinlock.

* serf_private.h
  (struct serf__init_once_context,
   SERF__INIT_ONCE_NONE,
   SERF__DECLARE_STATIC_INIT_ONCE_CONTEXT,
   serf__init_once_func_t,
   serf__init_once): New declarations.

* src/init_once.c: New; implements one-time initialization logic.
* buckets/ssl_buckets.c
  (apr_atomic_cas32): Remove conditional macro wrapper.
  (enum ssl_init_e): Remove unused enum.
  (do_init_libraries): Rename from init_ssl_libraries. Remove all the
   spinlock logic. Return an error if the mutex could not be creasted,
   resolving a FIXME comment.
  (init_ssl_libraries): New; calls serf__init_once with do_init_libraries
   as the initialization function.
  (ssl_need_client_cert, serf_ssl_load_cert_file): Fail if library init fails.

* CMakeLists.txt (SOURCES): Add src/init_once.c.

Added:
    serf/trunk/src/init_once.c   (with props)
Modified:
    serf/trunk/CMakeLists.txt
    serf/trunk/buckets/ssl_buckets.c
    serf/trunk/serf_private.h

Modified: serf/trunk/CMakeLists.txt
URL: 
http://svn.apache.org/viewvc/serf/trunk/CMakeLists.txt?rev=1926660&r1=1926659&r2=1926660&view=diff
==============================================================================
--- serf/trunk/CMakeLists.txt (original)
+++ serf/trunk/CMakeLists.txt Mon Jun 23 10:06:34 2025
@@ -131,6 +131,7 @@ list(APPEND SOURCES
     "src/context.c"
     "src/deprecated.c"
     "src/incoming.c"
+    "src/init_once.c"
     "src/logging.c"
     "src/outgoing.c"
     "src/outgoing_request.c"

Modified: serf/trunk/buckets/ssl_buckets.c
URL: 
http://svn.apache.org/viewvc/serf/trunk/buckets/ssl_buckets.c?rev=1926660&r1=1926659&r2=1926660&view=diff
==============================================================================
--- serf/trunk/buckets/ssl_buckets.c (original)
+++ serf/trunk/buckets/ssl_buckets.c Mon Jun 23 10:06:34 2025
@@ -1467,90 +1467,66 @@ static apr_status_t cleanup_ssl(void *da
 
 #endif
 
-#if !APR_VERSION_AT_LEAST(1,0,0)
-#define apr_atomic_cas32(mem, with, cmp) apr_atomic_cas(mem, with, cmp)
-#endif
 
-enum ssl_init_e
+static apr_status_t do_init_libraries(void* baton)
 {
-   INIT_UNINITIALIZED = 0,
-   INIT_BUSY = 1,
-   INIT_DONE = 2
-};
-
-static volatile apr_uint32_t have_init_ssl = INIT_UNINITIALIZED;
-
-static void init_ssl_libraries(void)
-{
-    apr_uint32_t val;
-
-    val = apr_atomic_cas32(&have_init_ssl, INIT_BUSY, INIT_UNINITIALIZED);
-
-    if (!val) {
 #if APR_HAS_THREADS && defined(SERF_HAVE_SSL_LOCKING_CALLBACKS)
-        int i, numlocks;
+    int i, numlocks;
 #endif
 
 #ifdef SERF_LOGGING_ENABLED
-        /* Warn when compile-time and run-time version of OpenSSL differ in
-           major/minor version number. */
+    /* Warn when compile-time and run-time version of OpenSSL differ in
+       major/minor version number. */
 #ifdef SERF_HAVE_OPENSSL_VERSION_NUM
-        unsigned long libver = OpenSSL_version_num();
+    unsigned long libver = OpenSSL_version_num();
 #else
-        long libver = SSLeay();
+    long libver = SSLeay();
 #endif
 
-        if ((libver ^ OPENSSL_VERSION_NUMBER) & 0xFFF00000) {
-            serf__log(LOGLVL_WARNING, LOGCOMP_SSL, __FILE__, NULL,
-                      "Warning: OpenSSL library version mismatch, compile-"
-                      "time was %lx, runtime is %lx.\n",
-                      OPENSSL_VERSION_NUMBER, libver);
-        }
+    if ((libver ^ OPENSSL_VERSION_NUMBER) & 0xFFF00000) {
+        serf__log(LOGLVL_WARNING, LOGCOMP_SSL, __FILE__, NULL,
+                  "Warning: OpenSSL library version mismatch, compile-"
+                  "time was %lx, runtime is %lx.\n",
+                  OPENSSL_VERSION_NUMBER, libver);
+    }
 #endif
 
 #ifdef SERF_HAVE_OPENSSL_SSL_LIBRARY_INIT
-        ERR_load_crypto_strings();
-        SSL_load_error_strings();
-        SSL_library_init();
-        OpenSSL_add_all_algorithms();
+    ERR_load_crypto_strings();
+    SSL_load_error_strings();
+    SSL_library_init();
+    OpenSSL_add_all_algorithms();
 #endif
 
 #if APR_HAS_THREADS && defined(SERF_HAVE_SSL_LOCKING_CALLBACKS)
-        numlocks = CRYPTO_num_locks();
-        apr_pool_create(&ssl_pool, NULL);
-        ssl_locks = apr_palloc(ssl_pool, sizeof(apr_thread_mutex_t*)*numlocks);
-        for (i = 0; i < numlocks; i++) {
-            apr_status_t rv;
+    numlocks = CRYPTO_num_locks();
+    apr_pool_create(&ssl_pool, NULL);
+    ssl_locks = apr_palloc(ssl_pool, sizeof(apr_thread_mutex_t*)*numlocks);
+    for (i = 0; i < numlocks; i++) {
+        apr_status_t status;
 
-            /* Intraprocess locks don't /need/ a filename... */
-            rv = apr_thread_mutex_create(&ssl_locks[i],
+        /* Intraprocess locks don't /need/ a filename... */
+        status = apr_thread_mutex_create(&ssl_locks[i],
                                          APR_THREAD_MUTEX_DEFAULT, ssl_pool);
-            if (rv != APR_SUCCESS) {
-                /* FIXME: error out here */
-            }
-        }
-        CRYPTO_set_locking_callback(ssl_lock);
-        CRYPTO_set_id_callback(ssl_id);
-        CRYPTO_set_dynlock_create_callback(ssl_dyn_create);
-        CRYPTO_set_dynlock_lock_callback(ssl_dyn_lock);
-        CRYPTO_set_dynlock_destroy_callback(ssl_dyn_destroy);
+        if (status != APR_SUCCESS)
+            return status;
+    }
+    CRYPTO_set_locking_callback(ssl_lock);
+    CRYPTO_set_id_callback(ssl_id);
+    CRYPTO_set_dynlock_create_callback(ssl_dyn_create);
+    CRYPTO_set_dynlock_lock_callback(ssl_dyn_lock);
+    CRYPTO_set_dynlock_destroy_callback(ssl_dyn_destroy);
 
-        apr_pool_cleanup_register(ssl_pool, NULL, cleanup_ssl, cleanup_ssl);
+    apr_pool_cleanup_register(ssl_pool, NULL, cleanup_ssl, cleanup_ssl);
 #endif
-        apr_atomic_cas32(&have_init_ssl, INIT_DONE, INIT_BUSY);
-    }
-  else
-    {
-        /* Make sure we don't continue before the initialization in another
-           thread has completed */
-        while (val != INIT_DONE) {
-            apr_sleep(APR_USEC_PER_SEC / 1000);
-
-            val = apr_atomic_cas32(&have_init_ssl,
-                                   INIT_UNINITIALIZED,
-                                   INIT_UNINITIALIZED);
-        }
-    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t init_ssl_libraries(void)
+{
+    SERF__DECLARE_STATIC_INIT_ONCE_CONTEXT(init_ctx);
+    return serf__init_once(&init_ctx, do_init_libraries, NULL);
 }
 
 static int ssl_need_client_cert(SSL *ssl, X509 **cert, EVP_PKEY **pkey)
@@ -1774,7 +1750,8 @@ static serf_ssl_context_t *ssl_init_cont
 {
     serf_ssl_context_t *ssl_ctx;
 
-    init_ssl_libraries();
+    if (init_ssl_libraries())
+        return NULL;
 
     ssl_ctx = serf_bucket_mem_alloc(allocator, sizeof(*ssl_ctx));
 
@@ -2023,11 +2000,13 @@ apr_status_t serf_ssl_load_cert_file(
     status = apr_file_open(&cert_file, file_path, APR_READ, APR_OS_DEFAULT,
                            pool);
 
+    if (!status) {
+        status = init_ssl_libraries();
+    }
     if (status) {
         return status;
     }
 
-    init_ssl_libraries();
 
     biom = bio_meth_file_new();
     bio = BIO_new(biom);

Modified: serf/trunk/serf_private.h
URL: 
http://svn.apache.org/viewvc/serf/trunk/serf_private.h?rev=1926660&r1=1926659&r2=1926660&view=diff
==============================================================================
--- serf/trunk/serf_private.h (original)
+++ serf/trunk/serf_private.h Mon Jun 23 10:06:34 2025
@@ -167,6 +167,32 @@ typedef int serf__bool_t; /* Not _Bool *
 #endif
 #endif
 
+/*** One-time initialization. ***/
+
+/* Init-once context. */
+struct serf__init_once_context
+{
+    volatile apr_uint32_t state;
+    apr_status_t status;
+};
+
+#define SERF__INIT_ONCE_NONE 0
+#define SERF__DECLARE_STATIC_INIT_ONCE_CONTEXT(name)  \
+    static struct serf__init_once_context name = {    \
+        SERF__INIT_ONCE_NONE, APR_SUCCESS             \
+    }
+
+/* The init-once callback function. */
+typedef apr_status_t (*serf__init_once_func_t)(void *baton);
+
+/* The function that performs on-time initialization.
+   If APR_HAS_THREADS, will use a spinlock to serialize the call to
+   the initialization function. */
+apr_status_t serf__init_once(struct serf__init_once_context *init_ctx,
+                             serf__init_once_func_t init_func,
+                             void *init_baton);
+
+
 typedef struct serf__authn_scheme_t serf__authn_scheme_t;
 
 typedef struct serf_io_baton_t {

Added: serf/trunk/src/init_once.c
URL: 
http://svn.apache.org/viewvc/serf/trunk/src/init_once.c?rev=1926660&view=auto
==============================================================================
--- serf/trunk/src/init_once.c (added)
+++ serf/trunk/src/init_once.c Mon Jun 23 10:06:34 2025
@@ -0,0 +1,88 @@
+/* ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+#include <apr.h>
+#include <apr_version.h>
+#if APR_HAS_THREADS
+#  include <apr_atomic.h>
+#  include <apr_time.h>
+/* FIXME: Do we really want to support APR-0.9.x? */
+#  if APR_MAJOR_VERSION < 1
+#    define apr_atomic_cas32(m, v, c) apr_atomic_cas((m), (v), (c))
+#  endif
+#endif
+
+#include "serf.h"
+#include "serf_private.h"
+
+
+enum init_once_state
+{
+    INIT_ONCE_NONE = SERF__INIT_ONCE_NONE,
+    INIT_ONCE_BUSY,
+    INIT_ONCE_DONE
+};
+
+static apr_uint32_t cas(volatile apr_uint32_t *mem,
+                        apr_uint32_t val,
+                        apr_uint32_t cmp)
+{
+#if APR_HAS_THREADS
+    return apr_atomic_cas32(mem, val, cmp);
+#else
+    const apr_uint32_t prev = *mem;
+    if (prev == cmp) {
+        *mem = val;
+    }
+    return prev;
+#endif
+}
+
+apr_status_t serf__init_once(struct serf__init_once_context *init_ctx,
+                             serf__init_once_func_t init_func,
+                             void *init_baton)
+{
+    apr_uint32_t state = cas(&init_ctx->state, INIT_ONCE_BUSY, INIT_ONCE_NONE);
+    for (;;)
+    {
+        switch (state)
+        {
+        case INIT_ONCE_DONE:
+            /* Already initialized. */
+            return init_ctx->status;
+
+        case INIT_ONCE_BUSY:
+            /* Spin while the initializer is working. */
+            apr_sleep(APR_USEC_PER_SEC / 500);
+            state = cas(&init_ctx->state, INIT_ONCE_NONE, INIT_ONCE_NONE);
+            continue;
+
+        case INIT_ONCE_NONE:
+            /* We're the single initializer, invoke the init code. */
+            init_ctx->status = init_func(init_baton);
+            cas(&init_ctx->state, INIT_ONCE_DONE, INIT_ONCE_BUSY);
+            return init_ctx->status;
+
+        default:
+            /* Not reached, can't happen. */
+            return APR_EGENERAL; /* FIXME: Just abort()? */
+        }
+    }
+}

Propchange: serf/trunk/src/init_once.c
------------------------------------------------------------------------------
    svn:eol-style = native


Reply via email to