Browse Source

New thread-safe bfg_strerror function to portably stringify error codes

Luke Dashjr 12 years ago
parent
commit
ac0bae4e91
3 changed files with 155 additions and 0 deletions
  1. 3 0
      miner.c
  2. 145 0
      util.c
  3. 7 0
      util.h

+ 3 - 0
miner.c

@@ -8592,6 +8592,8 @@ static void raise_fd_limits(void)
 #endif
 }
 
+extern void bfg_init_threadlocal();
+
 int main(int argc, char *argv[])
 {
 	struct sigaction handler;
@@ -8607,6 +8609,7 @@ int main(int argc, char *argv[])
 
 	blkmk_sha256_impl = my_blkmaker_sha256_callback;
 
+	bfg_init_threadlocal();
 #ifndef HAVE_PTHREAD_CANCEL
 	setup_pthread_cancel_workaround();
 #endif

+ 145 - 0
util.c

@@ -2256,6 +2256,151 @@ static const char *WindowsErrorStr(DWORD dwMessageId)
 }
 #endif
 
+static pthread_key_t key_bfgtls;
+struct bfgtls_data {
+	char *bfg_strerror_result;
+	size_t bfg_strerror_resultsz;
+#ifdef WIN32
+	LPSTR bfg_strerror_socketresult;
+#endif
+};
+
+void bfg_init_threadlocal()
+{
+	struct bfgtls_data *bfgtls;
+	void *p;
+	
+	bfgtls = malloc(sizeof(*bfgtls));
+	if (!bfgtls)
+		quit(1, "malloc bfgtls failed");
+	p = malloc(64);
+	if (!p)
+		quit(1, "malloc bfg_strerror_result failed");
+	*bfgtls = (struct bfgtls_data){
+		.bfg_strerror_resultsz = 64,
+		.bfg_strerror_result = p,
+	};
+	if (pthread_key_create(&key_bfgtls, NULL))
+		quit(1, "pthread_key_create failed");
+	if (pthread_setspecific(key_bfgtls, bfgtls))
+		quit(1, "pthread_setspecific failed");
+}
+
+static
+struct bfgtls_data *get_bfgtls()
+{
+	return pthread_getspecific(key_bfgtls);
+}
+
+static
+bool bfg_grow_buffer(char ** const bufp, size_t * const bufszp, size_t minimum)
+{
+	if (minimum <= *bufszp)
+		return false;
+	
+	while (minimum > *bufszp)
+		*bufszp = 2;
+	*bufp = realloc(*bufp, *bufszp);
+	if (unlikely(!*bufp))
+		quit(1, "realloc failed in bfg_grow_buffer");
+	
+	return true;
+}
+
+static
+const char *bfg_strcpy_growing_buffer(char ** const bufp, size_t * const bufszp, const char *src)
+{
+	if (!src)
+		return NULL;
+	
+	const size_t srcsz = strlen(src) + 1;
+	
+	bfg_grow_buffer(bufp, bufszp, srcsz);
+	memcpy(*bufp, src, srcsz);
+	
+	return *bufp;
+}
+
+// Guaranteed to always return some string (or quit)
+const char *bfg_strerror(int e, enum bfg_strerror_type type)
+{
+	static __maybe_unused pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+	struct bfgtls_data *bfgtls = get_bfgtls();
+	size_t * const bufszp = &bfgtls->bfg_strerror_resultsz;
+	char ** const bufp = &bfgtls->bfg_strerror_result;
+	const char *have = NULL;
+	
+	switch (type) {
+		case BST_LIBUSB:
+#ifdef HAVE_LIBUSB
+#	if HAVE_DECL_LIBUSB_ERROR_NAME
+			// libusb makes no guarantees for thread-safety or persistence
+			mutex_lock(&mutex);
+			have = bfg_strcpy_growing_buffer(bufp, bufszp, libusb_error_name(e));
+			mutex_unlock(&mutex);
+#	endif
+#endif
+			break;
+		case BST_SOCKET:
+		{
+#ifdef WIN32
+			// Windows has a different namespace for socket errors
+			LPSTR *msg = &bfgtls->bfg_strerror_socketresult;
+			if (*msg)
+				LocalFree(*msg);
+			if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, e, 0, (LPSTR)msg, 0, 0))
+				return *msg;
+			*msg = NULL;
+			
+			break;
+#endif
+		}
+			// Fallthru on non-WIN32
+		case BST_ERRNO:
+		{
+#ifdef __STRERROR_S_WORKS
+			// FIXME: Not sure how to get this on MingW64
+retry:
+			if (likely(!strerror_s(*bufp, *bufszp, e)))
+			{
+				if (bfg_grow_buffer(bufp, bufszp, strlen(*bufp) + 2))
+					goto retry;
+				return *bufp;
+			}
+// TODO: XSI strerror_r
+// TODO: GNU strerror_r
+#else
+			mutex_lock(&mutex);
+			have = bfg_strcpy_growing_buffer(bufp, bufszp, strerror(e));
+			mutex_unlock(&mutex);
+#endif
+		}
+	}
+	
+	if (have)
+		return *bufp;
+	
+	// Failback: Stringify the number
+	static const char fmt[] = "%s error #%d", *typestr;
+	switch (type) {
+		case BST_ERRNO:
+			typestr = "System";
+			break;
+		case BST_SOCKET:
+			typestr = "Socket";
+			break;
+		case BST_LIBUSB:
+			typestr = "libusb";
+			break;
+		default:
+			typestr = "Unexpected";
+	}
+	int sz = snprintf((char*)bfgtls, 0, fmt, typestr, e) + 1;
+	bfg_grow_buffer(bufp, bufszp, sz);
+	sprintf(*bufp, fmt, typestr, e);
+	return *bufp;
+}
+
 void notifier_init(notifier_t pipefd)
 {
 #ifdef WIN32

+ 7 - 0
util.h

@@ -111,6 +111,13 @@ void *realloc_strcat(char *ptr, char *s);
 extern char *sanestr(char *o, char *s);
 void RenameThread(const char* name);
 
+enum bfg_strerror_type {
+	BST_ERRNO,
+	BST_SOCKET,
+	BST_LIBUSB,
+};
+extern const char *bfg_strerror(int, enum bfg_strerror_type);
+
 typedef SOCKETTYPE notifier_t[2];
 extern void notifier_init(notifier_t);
 extern void notifier_wake(notifier_t);