git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jeff King <peff@peff.net>
To: git@vger.kernel.org
Subject: [RFC/PATCH] transport-helper: initialize debug flag before starting threads
Date: Mon, 8 Dec 2014 03:22:06 -0500	[thread overview]
Message-ID: <20141208082206.GA28302@peff.net> (raw)

The transport_debug() function uses a static variable to
lazily cache the boolean value of $TRANSPORT_DEBUG. If a
program uses transport-helper's bidirectional_transfer_loop
(as remote-ext and remote-fd do), then two threads may both
try to write the variable at the same time.

We can fix this by initializing the variable right before
starting the threads.

Noticed by "-fsanitize=thread". This probably isn't a
problem in practice, as both threads will write the same
value, and we are unlikely to see a torn write on an "int"
(i.e., on sane platforms a write to an int is atomic).

Signed-off-by: Jeff King <peff@peff.net>
---
I'm playing around with -fsanitize=thread and found this. The fix isn't
_too_ bad, but it's not the only case.

For example, "grep" may spawn many threads, each of which calls
want_color(), which does the same static cache-the-env trick. Should
grep call want_color() preemptively before staring threads? That's also
not too bad, but we're starting to cross a lot of module boundaries here
(i.e., it's a bit gross that grep needs to know how want_color() is
implemented).

As noted above, I think these cases are pretty benign. But it looks like
-fsanitize=thread would be a good way to find cases that are not (e.g.,
places in index-pack where we need to take the global lock but don't),
and it would be nice to keep things clean. The options for doing so (and
hence the RFC) are:

  1. Fixes like this (or grep calling want_color preemptively0. I'm not
     sure yet how many would be necessary, but probably a handful.

  2. Annotate sites like this (a single int read/write where all threads
     should get the same value) with "it's OK to race here" markers.

  3. Introduce locking into each site. This has performance
     implications, which makes me hesitate if this isn't a problem in
     practice. There's probably a simple lockless solution, though.

I think I'd favor (2) if we don't mind polluting our code with such
annotations. I haven't investigated exactly what they would look like,
though (there is a "dynamic annotation" that is pretty gross, as it
requires linking with extra code; but I think there may also be a
valgrind-suppression-like scheme that can live outside the code
completely).

 transport-helper.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/transport-helper.c b/transport-helper.c
index 0224687..fc3756c 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -1058,17 +1058,23 @@ int transport_helper_init(struct transport *transport, const char *name)
 /* This should be enough to hold debugging message. */
 #define PBUFFERSIZE 8192
 
+static int transport_debug_enabled(void)
+{
+	static int debug_enabled = -1;
+
+	if (debug_enabled < 0)
+		debug_enabled = getenv("GIT_TRANSLOOP_DEBUG") ? 1 : 0;
+	return debug_enabled;
+}
+
 /* Print bidirectional transfer loop debug message. */
 __attribute__((format (printf, 1, 2)))
 static void transfer_debug(const char *fmt, ...)
 {
 	va_list args;
 	char msgbuf[PBUFFERSIZE];
-	static int debug_enabled = -1;
 
-	if (debug_enabled < 0)
-		debug_enabled = getenv("GIT_TRANSLOOP_DEBUG") ? 1 : 0;
-	if (!debug_enabled)
+	if (!transport_debug_enabled)
 		return;
 
 	va_start(args, fmt);
@@ -1237,6 +1243,10 @@ static int tloop_spawnwait_tasks(struct bidirectional_transfer_state *s)
 	pthread_t ptg_thread;
 	int err;
 	int ret = 0;
+
+	/* initialize static global debug flag as a side effect */
+	transport_debug_enabled();
+
 	err = pthread_create(&gtp_thread, NULL, udt_copy_task_routine,
 		&s->gtp);
 	if (err)
-- 
2.2.0.390.gf60752d

             reply	other threads:[~2014-12-08  8:22 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-12-08  8:22 Jeff King [this message]
2014-12-08 10:36 ` [RFC/PATCH] transport-helper: initialize debug flag before starting threads Jeff King

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=20141208082206.GA28302@peff.net \
    --to=peff@peff.net \
    --cc=git@vger.kernel.org \
    /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;
as well as URLs for NNTP newsgroup(s).