Commit 524e2fb8f26ec0d7e8820c5530dafa57b56415bc

Kano 2012-05-02T16:11:11

api.c V1.9 add 'restart' + redesign 'quit' so thread exits cleanly

diff --git a/README b/README
index d5260f8..c5155ea 100644
--- a/README
+++ b/README
@@ -647,7 +647,7 @@ The STATUS section is:
    This defaults to the cgminer version but is the value of --api-description
    if it was specified at runtime.
 
-For API version 1.8:
+For API version 1.9:
 
 The list of requests - a (*) means it requires privileged access - and replies are:
 
@@ -768,7 +768,7 @@ The list of requests - a (*) means it requires privileged access - and replies a
                               stating success or failure saving the cgminer config
                               to filename
 
- quit (*)      none           There is no status section but just a single "BYE|"
+ quit (*)      none           There is no status section but just a single "BYE"
                               reply before cgminer quits
 
  notify        NOTIFY         The last status and history count of each devices problem
@@ -798,6 +798,9 @@ The list of requests - a (*) means it requires privileged access - and replies a
                               by the 'devs' command
                               e.g. DEVDETAILS=0,Name=GPU,ID=0,Driver=opencl,...|
 
+ restart (*)   none           There is no status section but just a single "RESTART"
+                              reply before cgminer restarts
+
 When you enable, disable or restart a GPU or PGA, you will also get Thread messages
 in the cgminer status window
 
diff --git a/api.c b/api.c
index 72a1a21..6c02a97 100644
--- a/api.c
+++ b/api.c
@@ -158,7 +158,7 @@ static const char SEPARATOR = '|';
 #define SEPSTR "|"
 static const char GPUSEP = ',';
 
-static const char *APIVERSION = "1.8";
+static const char *APIVERSION = "1.9";
 static const char *DEAD = "Dead";
 static const char *SICK = "Sick";
 static const char *NOSTART = "NoStart";
@@ -229,6 +229,7 @@ static const char *OSINFO =
 #define _NOTIFY		"NOTIFY"
 #define _DEVDETAILS	"DEVDETAILS"
 #define _BYE		"BYE"
+#define _RESTART	"RESTART"
 
 static const char ISJSON = '{';
 #define JSON0		"{"
@@ -260,6 +261,7 @@ static const char ISJSON = '{';
 #define JSON_NOTIFY	JSON1 _NOTIFY JSON2
 #define JSON_DEVDETAILS	JSON1 _DEVDETAILS JSON2
 #define JSON_BYE	JSON1 _BYE JSON1
+#define JSON_RESTART	JSON1 _RESTART JSON1
 #define JSON_CLOSE	JSON3
 #define JSON_END	JSON4
 
@@ -479,6 +481,12 @@ static int my_thr_id = 0;
 static int bye = 0;
 static bool ping = true;
 
+// Used to control quit restart access to shutdown variables
+static pthread_mutex_t quit_restart_lock;
+
+static int do_a_quit = 0;
+static int do_a_restart = 0;
+
 static time_t when = 0;	// when the request occurred
 
 struct IP4ACCESS {
@@ -612,7 +620,7 @@ static int pgadevice(int pgaid)
 }
 #endif
 
-// All replies (except BYE) start with a message
+// All replies (except BYE and RESTART) start with a message
 //  thus for JSON, message() inserts JSON_START at the front
 //  and send_result() adds JSON_END at the end
 static char *message(int messageid, int paramid, char *param2, bool isjson)
@@ -1811,22 +1819,26 @@ static void gpuvddc(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, boo
 #endif
 }
 
-static void send_result(SOCKETTYPE c, bool isjson);
-
-void doquit(SOCKETTYPE c, __maybe_unused char *param, bool isjson)
+void doquit(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
 {
 	if (isjson)
 		strcpy(io_buffer, JSON_START JSON_BYE);
 	else
 		strcpy(io_buffer, _BYE);
 
-	send_result(c, isjson);
-	*io_buffer = '\0';
 	bye = 1;
+	do_a_quit = 1;
+}
 
-        PTH(&thr_info[my_thr_id]) = 0L;
+void dorestart(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
+{
+	if (isjson)
+		strcpy(io_buffer, JSON_START JSON_RESTART);
+	else
+		strcpy(io_buffer, _RESTART);
 
-	kill_work();
+	bye = 1;
+	do_a_restart = 1;
 }
 
 void privileged(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
@@ -2017,6 +2029,7 @@ struct CMDS {
 	{ "privileged",		privileged,	true },
 	{ "notify",		notify,		false },
 	{ "devdetails",		devdetails,	false },
+	{ "restart",		dorestart,	true },
 	{ NULL,			NULL,		false }
 };
 
@@ -2041,11 +2054,12 @@ static void send_result(SOCKETTYPE c, bool isjson)
 		else
 			applog(LOG_DEBUG, "API: sent %d", n);
 	}
-
 }
 
 static void tidyup(__maybe_unused void *arg)
 {
+	mutex_lock(&quit_restart_lock);
+
 	bye = 1;
 
 	if (sock != INVSOCK) {
@@ -2068,6 +2082,8 @@ static void tidyup(__maybe_unused void *arg)
 		free(io_buffer);
 		io_buffer = NULL;
 	}
+
+	mutex_unlock(&quit_restart_lock);
 }
 
 /*
@@ -2172,8 +2188,37 @@ popipo:
 	free(buf);
 }
 
+static void *quit_thread(__maybe_unused void *userdata)
+{
+	// allow thread creator to finish whatever it's doing
+	mutex_lock(&quit_restart_lock);
+	mutex_unlock(&quit_restart_lock);
+
+	if (opt_debug)
+		applog(LOG_DEBUG, "API: killing cgminer");
+
+	kill_work();
+
+	return NULL;
+}
+
+static void *restart_thread(__maybe_unused void *userdata)
+{
+	// allow thread creator to finish whatever it's doing
+	mutex_lock(&quit_restart_lock);
+	mutex_unlock(&quit_restart_lock);
+
+	if (opt_debug)
+		applog(LOG_DEBUG, "API: restarting cgminer");
+
+	app_restart();
+
+	return NULL;
+}
+
 void api(int api_thr_id)
 {
+	struct thr_info bye_thr;
 	char buf[BUFSIZ];
 	char param_buf[BUFSIZ];
 	const char *localaddr = "127.0.0.1";
@@ -2197,6 +2242,8 @@ void api(int api_thr_id)
 	bool did;
 	int i;
 
+	mutex_init(&quit_restart_lock);
+
 	pthread_cleanup_push(tidyup, NULL);
 	my_thr_id = api_thr_id;
 
@@ -2408,4 +2455,33 @@ void api(int api_thr_id)
 	}
 die:
 	pthread_cleanup_pop(true);
+
+	if (opt_debug)
+		applog(LOG_DEBUG, "API: terminating due to: %s",
+				do_a_quit ? "QUIT" : (do_a_restart ? "RESTART" : (bye ? "BYE" : "UNKNOWN!")));
+
+	mutex_lock(&quit_restart_lock);
+
+	if (do_a_restart != 0) {
+
+		if (thr_info_create(&bye_thr, NULL, restart_thread, &bye_thr)) {
+			mutex_unlock(&quit_restart_lock);
+			quit(1, "API failed to initiate a restart - aborting");
+		}
+
+		pthread_detach(bye_thr.pth);
+
+	} else
+		if (do_a_quit != 0) {
+
+			if (thr_info_create(&bye_thr, NULL, quit_thread, &bye_thr)) {
+				mutex_unlock(&quit_restart_lock);
+				quit(1, "API failed to initiate a clean quit - aborting");
+			}
+
+			pthread_detach(bye_thr.pth);
+
+		}
+
+	mutex_unlock(&quit_restart_lock);
 }