instalog: change rpc stop function to be asynchronous by default

- Fix a bug in daemon_utils which doesn't save PID to disk in
  foreground mode.
- Change RPC Stop function in Core to be asynchronous by default.

BUG=None
TEST=Manually on local machine

Change-Id: I03e9f71d4735a613838004ea3f88e81bb6e9eec1
Reviewed-on: https://chromium-review.googlesource.com/429670
Commit-Ready: Joel Kitching <kitching@chromium.org>
Tested-by: Joel Kitching <kitching@chromium.org>
Reviewed-by: Chun-Tsen Kuo <chuntsen@chromium.org>
diff --git a/py/instalog/core.py b/py/instalog/core.py
index 4cb01da..5a30ff3 100644
--- a/py/instalog/core.py
+++ b/py/instalog/core.py
@@ -233,8 +233,8 @@
       logging.exception(e)
 
     # In case there was some error in the Run function (exception or otherwise),
-    # call Stop at the end just in case.
-    self.Stop()
+    # call Stop synchronously at the end just in case.
+    self.Stop(True)
     logging.warning('Stopped')
 
   def _Start(self):
@@ -253,9 +253,21 @@
     with self._rpc_lock:
       return self._state is UP
 
-  def Stop(self):
+  def Stop(self, sync=False):
+    """Stops Instalog.
+
+    Args:
+      sync: If true, only returns when Instalog has stopped running.
+    """
+    # If a Stop is already in process, return immediately.
     if self._state in (STOPPING, DOWN):
       return
+
+    # If called in asynchronous mode, kick off a thread to perform the stop.
+    if not sync:
+      threading.Thread(target=self.Stop, args=(True,)).start()
+      return
+
     self._ShutdownRPCServer()
     with self._rpc_lock:
       self._state = STOPPING
diff --git a/py/instalog/daemon_utils.py b/py/instalog/daemon_utils.py
index f4cc1dd..9ca0c7e 100644
--- a/py/instalog/daemon_utils.py
+++ b/py/instalog/daemon_utils.py
@@ -78,6 +78,11 @@
     os.dup2(so.fileno(), sys.stdout.fileno())
     os.dup2(se.fileno(), sys.stderr.fileno())
 
+    # Register pidfile.
+    self._RegisterPID()
+
+  def _RegisterPID(self):
+    """Saves the PID and registers a handler to remove the file on exit."""
     # Remove pidfile when exiting.
     atexit.register(self._RemovePID)
 
@@ -129,10 +134,15 @@
       sys.stderr.write(message % self.pidfile)
       sys.exit(1)
 
-    # Start the daemon.
-    if not foreground:
-      if self._Daemonize() == PARENT:
-        return
+    # Foreground mode: Registering the PID is sufficient.Daemonize if necessary.
+    if foreground:
+      self._RegisterPID()
+    # Daemon mode: Daemonize.  The function will fork and return with two
+    # possible values: PARENT or CHILD.
+    #   PARENT is the original process and should return to caller.
+    #   CHILD is the daemon and should invoke self.Run.
+    elif self._Daemonize() == PARENT:
+      return
     self.Run(foreground)
 
   def Stop(self):