From 85c46a0fcce3495fe94397d53791628a8ccc74c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Fri, 17 Jan 2014 21:02:19 +0100
Subject: Sluift: Allow blocking calls to be interrupted.

Change-Id: I3755e796fbddc038022bbf543c7b1c0529a9b0f9

diff --git a/Sluift/Examples/Login.lua b/Sluift/Examples/Login.lua
index c43b72a..fadb651 100644
--- a/Sluift/Examples/Login.lua
+++ b/Sluift/Examples/Login.lua
@@ -1,5 +1,5 @@
 --[[
-	Copyright (c) 2010-2013 Remko Tronçon
+	Copyright (c) 2010-2014 Remko Tronçon
 	Licensed under the GNU General Public License v3.
 	See Documentation/Licenses/GPLv3.txt for more information.
 --]]
@@ -21,7 +21,7 @@ sluift.debug = os.getenv("SLUIFT_DEBUG") or false
 
 print("Connecting " .. os.getenv("SLUIFT_JID") .. " ...")
 c = sluift.new_client(os.getenv("SLUIFT_JID"), os.getenv("SLUIFT_PASS"))
-c:set_options({compress = false, tls = false})
+c:set_options{compress = false, tls = false}
 c:connect()
 c:send_presence("")
 
diff --git a/Sluift/SConscript b/Sluift/SConscript
index d88e948..38baef1 100644
--- a/Sluift/SConscript
+++ b/Sluift/SConscript
@@ -32,6 +32,7 @@ elif env["SCONS_STAGE"] == "build" :
 		"ElementConvertors/CommandConvertor.cpp",
 		"ClientHelpers.cpp",
 		"SluiftClient.cpp",
+		"Watchdog.cpp",
 		"core.c",
 		"client.cpp",
 		"sluift.cpp"
diff --git a/Sluift/SluiftGlobals.h b/Sluift/SluiftGlobals.h
index 18a90c2..0d8e637 100644
--- a/Sluift/SluiftGlobals.h
+++ b/Sluift/SluiftGlobals.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013 Remko Tronçon
+ * Copyright (c) 2013-2014 Remko Tronçon
  * Licensed under the GNU General Public License.
  * See the COPYING file for more information.
  */
@@ -9,10 +9,11 @@
 #include <Sluift/LuaElementConvertors.h>
 #include <Swiften/EventLoop/SimpleEventLoop.h>
 #include <Swiften/Network/BoostNetworkFactories.h>
+#include <signal.h>
 
 namespace Swift {
 	struct SluiftGlobals {
-		SluiftGlobals() : networkFactories(&eventLoop) {}
+		SluiftGlobals() : networkFactories(&eventLoop), interruptRequested(0) {}
 
 		int timeout;
 		bool debug;
@@ -20,5 +21,6 @@ namespace Swift {
 		SimpleEventLoop eventLoop;
 		BoostNetworkFactories networkFactories;
 		int coreLibIndex;
+		sig_atomic_t interruptRequested;
 	};
 }
diff --git a/Sluift/Watchdog.cpp b/Sluift/Watchdog.cpp
new file mode 100644
index 0000000..8b1c9ab
--- /dev/null
+++ b/Sluift/Watchdog.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014 Remko Tronçon
+ * Licensed under the GNU General Public License.
+ * See the COPYING file for more information.
+ */
+
+#include <Sluift/Watchdog.h>
+
+#include <Sluift/globals.h>
+
+static const int INTERVAL_MS = 500;
+
+
+using namespace Swift;
+
+Watchdog::Watchdog(int timeout, TimerFactory* timerFactory) : 
+		remainingTime(timeout), 
+		timerFactory(timerFactory), 
+		timedOut(false) {
+	Sluift::globals.interruptRequested = 0;
+
+	int nextTimeout = remainingTime >= 0 ? std::min(remainingTime, INTERVAL_MS) : INTERVAL_MS;
+
+	timer = timerFactory->createTimer(nextTimeout);
+	timer->onTick.connect(boost::bind(&Watchdog::handleTimerTick, this));
+	remainingTime -= nextTimeout;
+	timer->start();
+}
+
+Watchdog::~Watchdog() {
+	if (timer) {
+		timer->stop();
+	}
+}
+
+void Watchdog::handleTimerTick() {
+	if (Sluift::globals.interruptRequested || remainingTime == 0) {
+		timedOut = true;
+	}
+	else {
+		int nextTimeout = remainingTime >= 0 ? std::min(remainingTime, INTERVAL_MS) : INTERVAL_MS;
+		if (nextTimeout != INTERVAL_MS) {
+			timer->onTick.disconnect(boost::bind(&Watchdog::handleTimerTick, this));
+			timer = timerFactory->createTimer(nextTimeout);
+			timer->onTick.connect(boost::bind(&Watchdog::handleTimerTick, this));
+		}
+		remainingTime -= nextTimeout;
+		timer->start();
+	}
+}
diff --git a/Sluift/Watchdog.h b/Sluift/Watchdog.h
index 95b6971..868621e 100644
--- a/Sluift/Watchdog.h
+++ b/Sluift/Watchdog.h
@@ -1,44 +1,32 @@
 /*
- * Copyright (c) 2011 Remko Tronçon
+ * Copyright (c) 2011-2014 Remko Tronçon
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
 #pragma once
 
+#include <algorithm>
+
 #include <Swiften/Network/TimerFactory.h>
 
 namespace Swift {
 	class Watchdog {
 		public:
-			Watchdog(int timeout, TimerFactory* timerFactory) : timedOut(false) {
-				if (timeout > 0) {
-					timer = timerFactory->createTimer(timeout);
-					timer->start();
-					timer->onTick.connect(boost::bind(&Watchdog::handleTimerTick, this));
-				}
-				else if (timeout == 0) {
-					timedOut = true;
-				}
-			}
-
-			~Watchdog() {
-				if (timer) {
-					timer->stop();
-				}
-			}
+			Watchdog(int timeout, TimerFactory* timerFactory);
+			~Watchdog();
 
 			bool getTimedOut() const {
 				return timedOut;
 			}
 
 		private:
-			void handleTimerTick() {
-				timedOut = true;
-			}
+			void handleTimerTick();
 
 		private:
 			Timer::ref timer;
+			int remainingTime;
+			TimerFactory* timerFactory;
 			bool timedOut;
 	};
 }
diff --git a/Sluift/client.cpp b/Sluift/client.cpp
index 16f1281..9cb5090 100644
--- a/Sluift/client.cpp
+++ b/Sluift/client.cpp
@@ -25,7 +25,6 @@
 #include <Swiften/Roster/SetRosterRequest.h>
 #include <Swiften/Presence/SubscriptionManager.h>
 #include <Swiften/Roster/XMPPRosterItem.h>
-#include <Sluift/Watchdog.h>
 #include <Swiften/Queries/Requests/GetSoftwareVersionRequest.h>
 #include <Sluift/Lua/FunctionRegistration.h>
 #include <Swiften/Base/foreach.h>
diff --git a/Sluift/main.cpp b/Sluift/main.cpp
index 76ba572..505cc5c 100644
--- a/Sluift/main.cpp
+++ b/Sluift/main.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013 Remko Tronçon
+ * Copyright (c) 2013-2014 Remko Tronçon
  * Licensed under the GNU General Public License.
  * See the COPYING file for more information.
  */
@@ -15,6 +15,7 @@
 #include <boost/version.hpp>
 #include <boost/numeric/conversion/cast.hpp>
 #include <boost/assign/list_of.hpp>
+#include <Sluift/globals.h>
 #include <Sluift/Console.h>
 #include <Sluift/StandardTerminal.h>
 #include <Sluift/sluift.h>
@@ -48,6 +49,10 @@ static const luaL_Reg defaultLibraries[] = {
 	{NULL, NULL}
 };
 
+static void handleInterruptSignal(int) {
+	Sluift::globals.interruptRequested = 1;
+}
+
 static void checkResult(lua_State* L, int result) {
 	if (result && !lua_isnil(L, -1)) {
 		const char* errorMessage = lua_tostring(L, -1);
@@ -150,6 +155,9 @@ int main(int argc, char* argv[]) {
 
 		// Run console
 		if (arguments.count("interactive") || arguments.count("script") == 0) {
+			// Set up signal handler
+			signal(SIGINT, handleInterruptSignal);
+
 			// Import some useful functions into the global namespace
 			lua_getglobal(L, "sluift");
 			std::vector<std::string> globalImports = boost::assign::list_of
-- 
cgit v0.10.2-6-g49f6