# Copyright 2011 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


# The prelaunchd's job is to keep a quickopen instance warmed up in the
# background, and service "give me a prelauncher" requests from quickopend
# clients.
import os
import subprocess
import logging

def _is_port_bindable(host, port):
  import socket
  s = socket.socket()
  try:
    s.bind((host, port))
  except socket.error:
    return False
  s.close()
  return True

class PrelaunchDaemon(object):
  def __init__(self, server):
    server.add_json_route('/existing_quickopen', self.get_existing_quickopen, ['GET'])
    server.exit.add_listener(self._on_exit)
    server.lo_idle.add_listener(self._join_in_use_processes)
    self._quickopen = None
    self._cur_control_port = 27412
    self._in_use_processes = []

  def _get_another_control_port(self):
    self._cur_control_port += 1
    for i in range(100):
      p = self._cur_control_port + i
      if not _is_port_bindable("", p):
        continue
      return p
    raise Exception("Could not find open control port")

  def _launch_new_quickopen(self):
    assert not self._quickopen
    quickopen_script = os.path.join(os.path.dirname(__file__), "../quickopen")
    assert os.path.exists(quickopen_script)
    
    self._cur_control_port = self._get_another_control_port()
    self._quickopen = subprocess.Popen([quickopen_script,
                                        "prelaunch",
                                        "--wait",
                                        "--control-port",
                                        str(self._cur_control_port)])

  def get_existing_quickopen(self, m, verb, data):
    if self._quickopen == None:
      self._launch_new_quickopen()
    try:
      self._in_use_processes.append(self._quickopen)
      self._quickopen = None
      return self._cur_control_port
    finally:
      # todo, move this to another place... ideally, when the previous prelaunch quits
      self._launch_new_quickopen()
      pass

  def _on_exit(self):
    self.stop()

  def _join_in_use_processes(self):
    procs = list(self._in_use_processes)
    del self._in_use_processes[:]
    for p in procs:
      if not p.poll():
        self._in_use_processes.append(p)
      else:
        logging.debug("prelaunched pid=%i is gone" % p.pid)

  def stop(self):
    logging.debug("closing prelaunched quickopen")
    if self._quickopen:
      self._quickopen.kill()
    self._join_in_use_processes()
    for p in self._in_use_processes:
      if not p.poll():
        logging.debug("killing %i" % p.pid)
        try:
          p.kill()
        except:
          pass

@@ -945,16 +945,350 @@
 n True%0A%0A
+class PrelaunchedProcess(object):%0A  def __init__(self, proc, port):%0A    if not isinstance(proc, subprocess.Popen):%0A      raise %22Expected subprocess%22%0A    self.proc = proc%0A    self.port = port%0A%0A  @property%0A  def pid(self):%0A    return self.proc.pid%0A%0A  def poll(self):%0A    return self.proc.poll()%0A%0A  def kill(self):%0A    self.proc.kill()%0A%0A
 class Pr
@@ -1573,64 +1573,65 @@
 lf._
-cur_control_port = 27412%0A    self._in_use_processes = %5B%5D
+in_use_processes = %5B%5D%0A    self._next_control_port = 27412
 %0A%0A  
@@ -1669,35 +1669,36 @@
 elf):%0A    self._
-cur
+next
 _control_port +=
@@ -1734,22 +1734,19 @@
     
- p =
  self._
-cur
+next
 _con
@@ -1756,18 +1756,19 @@
 l_port +
- i
+= 1
 %0A      i
@@ -1795,17 +1795,39 @@
 ble(%22%22, 
-p
+self._next_control_port
 ):%0A     
@@ -1851,17 +1851,39 @@
  return 
-p
+self._next_control_port
 %0A    rai
@@ -2125,27 +2125,13 @@
 pt)%0A
-    
 %0A    
-self._cur_
 cont
@@ -2178,31 +2178,20 @@
 t()%0A    
-self._quickopen
+proc
  = subpr
@@ -2250,27 +2250,16 @@
         
-           
 %22prelaun
@@ -2292,27 +2292,16 @@
         
-  
-         
 %22--wait%22
@@ -2331,27 +2331,16 @@
         
-     
-      
 %22--contr
@@ -2382,33 +2382,75 @@
     
-           str(self._cur_
+str(control_port)%5D)%0A    self._quickopen = PrelaunchedProcess(proc, 
 cont
@@ -2458,18 +2458,16 @@
 ol_port)
-%5D)
 %0A%0A  def 
@@ -2640,16 +2640,50 @@
 ckopen)%0A
+      port = self._quickopen.port%0A
       se
@@ -2720,26 +2720,8 @@
 urn 
-self._cur_control_
 port

Make a PrelaunchedProcess type on daemon side and stash both Popen and control port inside it.