Git development
 help / color / mirror / Atom feed
From: "Johannes Schindelin via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Johannes Schindelin <johannes.schindelin@gmx.de>,
	Johannes Schindelin <johannes.schindelin@gmx.de>
Subject: [PATCH 4/4] sha1dc: make `sha1dc_init()` thread-safe
Date: Mon, 28 Sep 2026 15:51:31 +0000	[thread overview]
Message-ID: <aac6a83a8ebd91f33cbe2074245b6d45cb264105.1790610691.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2240.git.1790610691.gitgitgadget@gmail.com>

From: Johannes Schindelin <johannes.schindelin@gmx.de>

The `sha1dc_init` function pointer initially points to a function that
determines which sha1dc backend to use. Naturally, this initialization
should only run once.

To allow for that function to be called concurrently in multiple
threads, we need to use a pthread primitive to ensure that the
`sha1dc_*()` function pointers are initialized exactly once.

Unfortunately, this requires quite a bit of non-DRY code to prevent data
races when different threads run `initial_init()` concurrently (see
https://en.cppreference.com/c/language/memory_model#Threads_and_data_races).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 sha1dc_git.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 56 insertions(+), 7 deletions(-)

diff --git a/sha1dc_git.c b/sha1dc_git.c
index dcc5c1ca8e..eba62b12ab 100644
--- a/sha1dc_git.c
+++ b/sha1dc_git.c
@@ -7,6 +7,7 @@
 #ifdef DC_SHA1_RS
 #include "config.h"
 #include "repository.h"
+#include "thread-utils.h"
 #endif
 
 #ifdef DC_SHA1_EXTERNAL
@@ -58,16 +59,21 @@ static void sha1dc_c_discard(SHA1_CTX *ctx UNUSED)
 }
 
 /* The first SHA-1 initialization must precede concurrent hashing. */
-static void sha1dc_choose(SHA1_CTX *ctx);
+static void initial_init(SHA1_CTX *);
+static void initial_clone(SHA1_CTX *, const SHA1_CTX *);
+static void initial_update(SHA1_CTX *, const void *, size_t);
+static void initial_final(unsigned char [20], SHA1_CTX *,
+			  void (*die_fn)(const char *, ...));
+static void initial_discard(SHA1_CTX *ctx);
 
-void (*sha1dc_init)(SHA1_CTX *) = sha1dc_choose;
-void (*sha1dc_clone)(SHA1_CTX *, const SHA1_CTX *);
-void (*sha1dc_update)(SHA1_CTX *, const void *, size_t);
+void (*sha1dc_init)(SHA1_CTX *) = initial_init;
+void (*sha1dc_clone)(SHA1_CTX *, const SHA1_CTX *) = initial_clone;
+void (*sha1dc_update)(SHA1_CTX *, const void *, size_t) = initial_update;
 void (*sha1dc_final)(unsigned char [20], SHA1_CTX *,
-		     void (*die_fn)(const char *, ...));
-void (*sha1dc_discard)(SHA1_CTX *);
+		     void (*die_fn)(const char *, ...)) = initial_final;
+void (*sha1dc_discard)(SHA1_CTX *) = initial_discard;
 
-static void sha1dc_choose(SHA1_CTX *ctx)
+static void sha1dc_choose(void)
 {
 	const char *backend;
 	int use_c = 0;
@@ -86,6 +92,49 @@ static void sha1dc_choose(SHA1_CTX *ctx)
 	sha1dc_final = use_c ? git_SHA1DCFinal : sha1dc_rs_final;
 	sha1dc_discard = use_c ? sha1dc_c_discard : sha1dc_rs_discard;
 	sha1dc_init = use_c ? git_SHA1DCInit : sha1dc_rs_init;
+}
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+static void initial_init(SHA1_CTX *ctx)
+{
+	int ret = pthread_once(&once, sha1dc_choose);
+	if (ret)
+		die("cannot initialize SHA-1 backend: %s", strerror(ret));
 	sha1dc_init(ctx);
 }
+
+static void initial_clone(SHA1_CTX *dst, const SHA1_CTX *src)
+{
+	int ret = pthread_once(&once, sha1dc_choose);
+	if (ret)
+		die("cannot initialize SHA-1 backend: %s", strerror(ret));
+	sha1dc_clone(dst, src);
+}
+
+static void initial_update(SHA1_CTX *ctx, const void *buf, size_t len)
+{
+	int ret = pthread_once(&once, sha1dc_choose);
+	if (ret)
+		die("cannot initialize SHA-1 backend: %s", strerror(ret));
+	sha1dc_update(ctx, buf, len);
+}
+
+static void initial_final(unsigned char hash[20], SHA1_CTX *ctx,
+			  void (*die_fn)(const char *, ...))
+{
+	int ret = pthread_once(&once, sha1dc_choose);
+	if (ret)
+		die("cannot initialize SHA-1 backend: %s", strerror(ret));
+	sha1dc_final(hash, ctx, die_fn);
+}
+
+static void initial_discard(SHA1_CTX *ctx)
+{
+	int ret = pthread_once(&once, sha1dc_choose);
+	if (ret)
+		die("cannot initialize SHA-1 backend: %s", strerror(ret));
+	sha1dc_discard(ctx);
+}
+
 #endif
-- 
gitgitgadget

  parent reply	other threads:[~2026-09-28 15:51 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-28 15:51 [PATCH 0/4] Add a compile-time option to use the new, very fast sha1dc Rust crate Johannes Schindelin via GitGitGadget
2026-09-28 15:51 ` [PATCH 1/4] libgitcore: add `sha1dc` as an optional feature Johannes Schindelin via GitGitGadget
2026-10-03 21:01   ` brian m. carlson
2026-09-28 15:51 ` [PATCH 2/4] sha1dc: allow selecting the C backend without rebuilding Johannes Schindelin via GitGitGadget
2026-09-28 15:51 ` [PATCH 3/4] pthread: provide `pthread_once()` shims for Windows and for NO_PTHREADS Johannes Schindelin via GitGitGadget
2026-09-28 15:51 ` Johannes Schindelin via GitGitGadget [this message]
2026-09-28 16:20 ` [PATCH 0/4] Add a compile-time option to use the new, very fast sha1dc Rust crate Johannes Schindelin
2026-09-29  7:30 ` Junio C Hamano
2026-10-03 10:23   ` Johannes Schindelin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aac6a83a8ebd91f33cbe2074245b6d45cb264105.1790610691.git.gitgitgadget@gmail.com \
    --to=gitgitgadget@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=johannes.schindelin@gmx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox