[ccan] [PATCH 1/5] altstack: Consolidate thread-local variables

David Gibson david at gibson.dropbear.id.au
Fri Jun 3 18:41:59 AEST 2016


altstack uses a number of __thread variables to track internal state.  This
allows altstack to be thread-safe, although it's still not re-entrant.
This patch gathers all these variables into a single per-thread state
structure.  This makes it easy to see at a glance what the whole of the
required state is, and thereby easier to reason about correctness of
changes to the implementation.

Signed-off-by: David Gibson <david at gibson.dropbear.id.au>
---
 ccan/altstack/altstack.c | 67 +++++++++++++++++++++++++-----------------------
 ccan/altstack/test/run.c |  2 +-
 2 files changed, 36 insertions(+), 33 deletions(-)

diff --git a/ccan/altstack/altstack.c b/ccan/altstack/altstack.c
index 6351293..080cd50 100644
--- a/ccan/altstack/altstack.c
+++ b/ccan/altstack/altstack.c
@@ -11,43 +11,48 @@
 #include <unistd.h>
 #include <sys/mman.h>
 
-static __thread char ebuf[ALTSTACK_ERR_MAXLEN];
-static __thread unsigned elen;
-
-#define bang(x)							\
-	(elen += snprintf(ebuf + elen, sizeof(ebuf) - elen,	\
-		 "%s(altstack@%d) %s%s%s",			\
-		 elen  ? "; " : "", __LINE__, (x),		\
-		 errno ? ": " : "", errno ? strerror(errno) : ""))
+static __thread struct altstack_state {
+	char ebuf[ALTSTACK_ERR_MAXLEN];
+	unsigned elen;
+	jmp_buf jmp;
+	void *rsp_save_[2];
+	rlim_t max;
+	void *(*fn)(void *);
+	void *arg, *out;
+} state;
+
+#define bang(x)								\
+	(state.elen += snprintf(state.ebuf + state.elen,		\
+				sizeof(state.ebuf) - state.elen,	\
+				"%s(altstack@%d) %s%s%s",		\
+				state.elen  ? "; " : "", __LINE__, (x),	\
+				errno ? ": " : "",			\
+				errno ? strerror(errno) : ""))
 
 void altstack_perror(void)
 {
-	fprintf(stderr, "%s\n", ebuf);
+	fprintf(stderr, "%s\n", state.ebuf);
 }
 
 char *altstack_geterr(void)
 {
-	return ebuf;
+	return state.ebuf;
 }
 
-static __thread jmp_buf jmp;
-
 static void segvjmp(int signum)
 {
-	longjmp(jmp, 1);
+	longjmp(state.jmp, 1);
 }
 
-static __thread void *rsp_save_[2];
-static __thread rlim_t max_;
 
 rlim_t altstack_max(void) {
-	return max_;
+	return state.max;
 }
 
 static ptrdiff_t rsp_save(unsigned i) {
 	assert(i < 2);
-	asm volatile ("movq %%rsp, %0" : "=g" (rsp_save_[i]));
-	return (char *) rsp_save_[0] - (char *) rsp_save_[i];
+	asm volatile ("movq %%rsp, %0" : "=g" (state.rsp_save_[i]));
+	return (char *) state.rsp_save_[0] - (char *) state.rsp_save_[i];
 }
 
 void altstack_rsp_save(void) {
@@ -58,9 +63,6 @@ ptrdiff_t altstack_used(void) {
 	return rsp_save(1);
 }
 
-static __thread void *(*fn_)(void *);
-static __thread void *arg_, *out_;
-
 int altstack(rlim_t max, void *(*fn)(void *), void *arg, void **out)
 {
 	long pgsz = sysconf(_SC_PAGESIZE);
@@ -73,11 +75,11 @@ int altstack(rlim_t max, void *(*fn)(void *), void *arg, void **out)
 	assert(max > 0 && fn);
 	#define ok(x, y) ({ long __r = (long) (x); if (__r == -1) { bang(#x); if (y) goto out; } __r; })
 
-	fn_  = fn;
-	arg_ = arg;
-	out_ = 0;
-	max_ = max;
-	ebuf[elen = 0] = '\0';
+	state.fn  = fn;
+	state.arg = arg;
+	state.out = 0;
+	state.max = max;
+	state.ebuf[state.elen = 0] = '\0';
 	if (out) *out = 0;
 
 	// if the first page below the mapping is in use, we get max-pgsz usable bytes
@@ -85,13 +87,13 @@ int altstack(rlim_t max, void *(*fn)(void *), void *arg, void **out)
 	max += pgsz;
 
 	ok(getrlimit(RLIMIT_STACK, &rl_save), 1);
-	ok(setrlimit(RLIMIT_STACK, &(struct rlimit) { max_, rl_save.rlim_max }), 1);
+	ok(setrlimit(RLIMIT_STACK, &(struct rlimit) { state.max, rl_save.rlim_max }), 1);
 	undo++;
 
 	ok(m = mmap(0, max, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_GROWSDOWN|MAP_NORESERVE, -1, 0), 1);
 	undo++;
 
-	if (setjmp(jmp) == 0) {
+	if (setjmp(state.jmp) == 0) {
 		unsigned char sigstk[SIGSTKSZ];
 		stack_t ss = { .ss_sp = sigstk, .ss_size = sizeof(sigstk) };
 		struct sigaction sa = { .sa_handler = segvjmp, .sa_flags = SA_NODEFER|SA_RESETHAND|SA_ONSTACK };
@@ -108,12 +110,13 @@ int altstack(rlim_t max, void *(*fn)(void *), void *arg, void **out)
 			"mov %1, %%rsp\n\t"
 			"sub $8, %%rsp\n\t"
 			"push %%r10"
-			: "=r" (rsp_save_[0]) : "0" (m + max) : "r10", "memory");
-		out_ = fn_(arg_);
+			: "=r" (state.rsp_save_[0])
+			: "0" (m + max) : "r10", "memory");
+		state.out = state.fn(state.arg);
 		asm volatile ("pop %%rsp"
 			      : : : "memory");
 		ret = 0;
-		if (out) *out = out_;
+		if (out) *out = state.out;
 	}
 	else {
 		errno = 0;
@@ -137,5 +140,5 @@ out:
 
 	if (errno_save)
 		errno = errno_save;
-	return !ret && elen ? 1 : ret;
+	return !ret && state.elen ? 1 : ret;
 }
diff --git a/ccan/altstack/test/run.c b/ccan/altstack/test/run.c
index 12cc460..23dd2e9 100644
--- a/ccan/altstack/test/run.c
+++ b/ccan/altstack/test/run.c
@@ -24,7 +24,7 @@ char *m_;
 rlim_t msz_;
 #define e(x) (900+(x))
 #define seterr(x) (errno = e(x))
-#define setcall(x) ((call1 |= !errno ? (x) : 0), (call2 |= errno || out_ ? (x) : 0))
+#define setcall(x) ((call1 |= !errno ? (x) : 0), (call2 |= errno || state.out ? (x) : 0))
 #define getrlimit(...)		(fail&getrlimit_	? (seterr(getrlimit_),		-1) : (setcall(getrlimit_),	getrlimit(__VA_ARGS__)))
 #define mmap(...)		(fail&mmap_		? (seterr(mmap_),	(void *)-1) : (setcall(mmap_),		mmap(__VA_ARGS__)))
 #define munmap(a, b)		(fail&munmap_		? (seterr(munmap_),		-1) : (setcall(munmap_),	munmap(m_=(a), msz_=(b))))
-- 
2.5.5



More information about the ccan mailing list