Browse Source

If pthread_cancel is missing (Bionic/Android), emulate it using pthread_kill and pthread_exit

Luke Dashjr 13 years ago
parent
commit
6eb20f8340
3 changed files with 135 additions and 0 deletions
  1. 20 0
      compat.h
  2. 20 0
      miner.c
  3. 95 0
      util.c

+ 20 - 0
compat.h

@@ -141,4 +141,24 @@ typedef long suseconds_t;
 #define PTH(thr) ((thr)->pth)
 #endif /* WIN32 */
 
+#ifndef HAVE_PTHREAD_CANCEL
+
+// Bionic (Android) is intentionally missing pthread_cancel, so it is implemented using pthread_kill (handled in util.c)
+#include <pthread.h>
+#include <signal.h>
+#define pthread_cancel(pth)  pthread_kill(pth, SIGTERM)
+#ifndef PTHREAD_CANCEL_ENABLE
+#define PTHREAD_CANCEL_ENABLE  0
+#define PTHREAD_CANCEL_DISABLE 1
+#endif
+#ifndef PTHREAD_CANCEL_DEFERRED
+#define PTHREAD_CANCEL_DEFERRED     0
+#define PTHREAD_CANCEL_ASYNCHRONOUS 1
+#endif
+#ifndef PTHREAD_CANCELED
+#define PTHREAD_CANCELED ((void*)-1)
+#endif
+
+#endif
+
 #endif /* __COMPAT_H__ */

+ 20 - 0
miner.c

@@ -7603,6 +7603,11 @@ static bool my_blkmaker_sha256_callback(void *digest, const void *buffer, size_t
 	return true;
 }
 
+#ifndef HAVE_PTHREAD_CANCEL
+extern void setup_pthread_cancel_workaround();
+extern struct sigaction pcwm_orig_term_handler;
+#endif
+
 int main(int argc, char *argv[])
 {
 	bool pools_active = false;
@@ -7615,6 +7620,10 @@ int main(int argc, char *argv[])
 
 	blkmk_sha256_impl = my_blkmaker_sha256_callback;
 
+#ifndef HAVE_PTHREAD_CANCEL
+	setup_pthread_cancel_workaround();
+#endif
+
 	/* This dangerous functions tramples random dynamically allocated
 	 * variables so do it before anything at all */
 	if (unlikely(curl_global_init(CURL_GLOBAL_ALL)))
@@ -7663,7 +7672,13 @@ int main(int argc, char *argv[])
 	handler.sa_handler = &sighandler;
 	handler.sa_flags = 0;
 	sigemptyset(&handler.sa_mask);
+#ifdef HAVE_PTHREAD_CANCEL
 	sigaction(SIGTERM, &handler, &termhandler);
+#else
+	// Need to let pthread_cancel emulation handle SIGTERM first
+	termhandler = pcwm_orig_term_handler;
+	pcwm_orig_term_handler = handler;
+#endif
 	sigaction(SIGINT, &handler, &inthandler);
 #ifndef WIN32
 	signal(SIGPIPE, SIG_IGN);
@@ -7726,6 +7741,11 @@ int main(int argc, char *argv[])
 	if (!config_loaded)
 		load_default_config();
 
+#ifndef HAVE_PTHREAD_CANCEL
+	// Can't do this any earlier, or config isn't loaded
+	applog(LOG_DEBUG, "pthread_cancel workaround in use");
+#endif
+
 	if (opt_benchmark) {
 		struct pool *pool;
 

+ 95 - 0
util.c

@@ -934,6 +934,101 @@ void thr_info_cancel(struct thr_info *thr)
 	}
 }
 
+#ifndef HAVE_PTHREAD_CANCEL
+
+// Bionic (Android) is intentionally missing pthread_cancel, so it is implemented using pthread_kill
+
+enum pthread_cancel_workaround_mode {
+	PCWM_DEFAULT   = 0,
+	PCWM_TERMINATE = 1,
+	PCWM_ASYNC     = 2,
+	PCWM_DISABLED  = 4,
+	PCWM_CANCELLED = 8,
+};
+
+static pthread_key_t key_pcwm;
+struct sigaction pcwm_orig_term_handler;
+
+static
+void do_pthread_cancel_exit(int flags)
+{
+	if (!(flags & PCWM_ASYNC))
+		// NOTE: Logging disables cancel while mutex held, so this is safe
+		applog(LOG_WARNING, "pthread_cancel workaround: Cannot defer cancellation, terminating thread NOW");
+	pthread_exit(PTHREAD_CANCELED);
+}
+
+static
+void sighandler_pthread_cancel(int sig)
+{
+	int flags = (int)pthread_getspecific(key_pcwm);
+	if (flags & PCWM_TERMINATE)  // Main thread
+	{
+		// Restore original handler and call it
+		if (sigaction(sig, &pcwm_orig_term_handler, NULL))
+			quit(1, "pthread_cancel workaround: Failed to restore original handler");
+		raise(SIGTERM);
+		quit(1, "pthread_cancel workaround: Original handler returned");
+	}
+	if (flags & PCWM_CANCELLED)  // Already pending cancel
+		return;
+	if (flags & PCWM_DISABLED)
+	{
+		flags |= PCWM_CANCELLED;
+		if (pthread_setspecific(key_pcwm, (void*)flags))
+			quit(1, "pthread_cancel workaround: pthread_setspecific failed (setting PCWM_CANCELLED)");
+		return;
+	}
+	do_pthread_cancel_exit(flags);
+}
+
+int pthread_setcancelstate(int state, int *oldstate)
+{
+	int flags = (int)pthread_getspecific(key_pcwm);
+	if (oldstate)
+		*oldstate = (flags & PCWM_DISABLED) ? PTHREAD_CANCEL_DISABLE : PTHREAD_CANCEL_ENABLE;
+	if (state == PTHREAD_CANCEL_DISABLE)
+		flags |= PCWM_DISABLED;
+	else
+	{
+		if (flags & PCWM_CANCELLED)
+			do_pthread_cancel_exit(flags);
+		flags &= ~PCWM_DISABLED;
+	}
+	if (pthread_setspecific(key_pcwm, (void*)flags))
+		return -1;
+	return 0;
+}
+
+int pthread_setcanceltype(int type, int *oldtype)
+{
+	int flags = (int)pthread_getspecific(key_pcwm);
+	if (oldtype)
+		*oldtype = (flags & PCWM_ASYNC) ? PTHREAD_CANCEL_ASYNCHRONOUS : PTHREAD_CANCEL_DEFERRED;
+	if (type == PTHREAD_CANCEL_ASYNCHRONOUS)
+		flags |= PCWM_ASYNC;
+	else
+		flags &= ~PCWM_ASYNC;
+	if (pthread_setspecific(key_pcwm, (void*)flags))
+		return -1;
+	return 0;
+}
+
+void setup_pthread_cancel_workaround()
+{
+	if (pthread_key_create(&key_pcwm, NULL))
+		quit(1, "pthread_cancel workaround: pthread_key_create failed");
+	if (pthread_setspecific(key_pcwm, (void*)PCWM_TERMINATE))
+		quit(1, "pthread_cancel workaround: pthread_setspecific failed");
+	struct sigaction new_sigact = {
+		.sa_handler = sighandler_pthread_cancel,
+	};
+	if (sigaction(SIGTERM, &new_sigact, &pcwm_orig_term_handler))
+		quit(1, "pthread_cancel workaround: Failed to install SIGTERM handler");
+}
+
+#endif
+
 /* Provide a ms based sleep that uses nanosleep to avoid poor usleep accuracy
  * on SMP machines */
 void nmsleep(unsigned int msecs)