blob: 3c81befbe469ea2726354621e2e6d5fc4b3c8e9a [file] [log] [blame]
# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
'''
A library to prespawn autotest processes to minimize startup overhead.
'''
import cPickle as pickle
import logging
import os
import subprocess
import sys
import threading
from Queue import Queue
from cros.factory.utils.process_utils import Spawn
NUM_PRESPAWNED_PROCESSES = 1
PRESPAWNER_PATH = '/usr/local/autotest/bin/prespawner.py'
class Prespawner():
def __init__(self):
self.prespawned = Queue(NUM_PRESPAWNED_PROCESSES)
self.thread = None
self.terminated = False
def spawn(self, args, env_additions=None):
'''
Spawns a new autotest (reusing an prespawned process if available).
@param args: A list of arguments (sys.argv)
@param env_additions: Items to add to the current environment
'''
new_env = dict(os.environ)
if env_additions:
new_env.update(env_additions)
process = self.prespawned.get()
# Write the environment and argv to the process's stdin; it will launch
# autotest once these are received.
pickle.dump((new_env, args), process.stdin, protocol=2)
process.stdin.close()
return process
def start(self):
'''
Starts a thread to pre-spawn autotests.
'''
def run():
while not self.terminated:
process = Spawn(
['python', '-u', PRESPAWNER_PATH,
'--prespawn_autotest'],
cwd=os.path.dirname(PRESPAWNER_PATH),
stdin=subprocess.PIPE)
logging.debug('Pre-spawned an autotest process %d', process.pid)
self.prespawned.put(process)
# Let stop() know that we are done
self.prespawned.put(None)
if not self.thread and os.path.exists(PRESPAWNER_PATH):
self.thread = threading.Thread(target=run, name='Prespawner')
self.thread.start()
def stop(self):
'''
Stops the pre-spawn thread gracefully.
'''
if not self.thread:
# Never started
return
self.terminated = True
# Wait for any existing prespawned processes.
while True:
process = self.prespawned.get()
if not process:
break
# Send a 'None' environment and arg list to tell the prespawner
# processes to exit.
pickle.dump((None, None), process.stdin, protocol=2)
process.stdin.close()
process.wait()
self.thread = None