Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(132)

Side by Side Diff: testing/legion/process.py

Issue 1108173002: Roll //build, //native_client, and a few more targets of opportunity. Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Test fix Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « testing/legion/legion_test_case.py ('k') | testing/legion/rpc_methods.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2015 The Chromium Authors. All rights reserved. 1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Defines the task RPC methods.""" 5 """RPC compatible subprocess-type module.
6 6
7 import os 7 This module defined both a task-side process class as well as a controller-side
8 process wrapper for easier access and usage of the task-side process.
9 """
10
11 import logging
12 import subprocess
8 import sys 13 import sys
9 import logging
10 import threading 14 import threading
11 15
12 #pylint: disable=relative-import 16 #pylint: disable=relative-import
13 import common_lib 17 import common_lib
14 18
15 # Map swarming_client to use subprocess42 19 # Map swarming_client to use subprocess42
16 sys.path.append(common_lib.SWARMING_DIR) 20 sys.path.append(common_lib.SWARMING_DIR)
17 21
18 from utils import subprocess42 22 from utils import subprocess42
19 23
20 24
21 class RPCMethods(object): 25 class ControllerProcessWrapper(object):
22 """Class exposing RPC methods.""" 26 """Controller-side process wrapper class.
23 27
24 _dotted_whitelist = ['subprocess'] 28 This class provides a more intuitive interface to task-side processes
29 than calling the methods directly using the RPC object.
30 """
25 31
26 def __init__(self, server): 32 def __init__(self, rpc, cmd, verbose=False, detached=False, cwd=None):
27 self._server = server 33 self._rpc = rpc
28 self.subprocess = Subprocess 34 self._id = rpc.subprocess.Process(cmd)
35 if verbose:
36 self._rpc.subprocess.SetVerbose(self._id)
37 if detached:
38 self._rpc.subprocess.SetDetached(self._id)
39 if cwd:
40 self._rpc.subprocess.SetCwd(self._rpc, cwd)
41 self._rpc.subprocess.Start(self._id)
29 42
30 def _dispatch(self, method, params): 43 def Terminate(self):
31 obj = self 44 logging.debug('Terminating process %s', self._id)
32 if '.' in method: 45 return self._rpc.subprocess.Terminate(self._id)
33 # Allow only white listed dotted names
34 name, method = method.split('.')
35 assert name in self._dotted_whitelist
36 obj = getattr(self, name)
37 return getattr(obj, method)(*params)
38 46
39 def Echo(self, message): 47 def Kill(self):
40 """Simple RPC method to print and return a message.""" 48 logging.debug('Killing process %s', self._id)
41 logging.info('Echoing %s', message) 49 self._rpc.subprocess.Kill(self._id)
42 return 'echo %s' % str(message)
43 50
44 def AbsPath(self, path): 51 def Delete(self):
45 """Returns the absolute path.""" 52 return self._rpc.subprocess.Delete(self._id)
46 return os.path.abspath(path)
47 53
48 def Quit(self): 54 def GetReturncode(self):
49 """Call _server.shutdown in another thread. 55 return self._rpc.subprocess.GetReturncode(self._id)
50 56
51 This is needed because server.shutdown waits for the server to actually 57 def ReadStdout(self):
52 quit. However the server cannot shutdown until it completes handling this 58 """Returns all stdout since the last call to ReadStdout.
53 call. Calling this in the same thread results in a deadlock. 59
60 This call allows the user to read stdout while the process is running.
61 However each call will flush the local stdout buffer. In order to make
62 multiple calls to ReadStdout and to retain the entire output the results
63 of this call will need to be buffered in the calling code.
54 """ 64 """
55 t = threading.Thread(target=self._server.shutdown) 65 return self._rpc.subprocess.ReadStdout(self._id)
56 t.start() 66
67 def ReadStderr(self):
68 """Returns all stderr read since the last call to ReadStderr.
69
70 See ReadStdout for additional details.
71 """
72 return self._rpc.subprocess.ReadStderr(self._id)
73
74 def ReadOutput(self):
75 """Returns the (stdout, stderr) since the last Read* call.
76
77 See ReadStdout for additional details.
78 """
79 return self._rpc.subprocess.ReadOutput(self._id)
80
81 def Wait(self):
82 return self._rpc.subprocess.Wait(self._id)
83
84 def Poll(self):
85 return self._rpc.subprocess.Poll(self._id)
86
87 def GetPid(self):
88 return self._rpc.subprocess.GetPid(self._id)
57 89
58 90
59 class Subprocess(object): 91
60 """Implements a server-based non-blocking subprocess. 92 class Process(object):
93 """Implements a task-side non-blocking subprocess.
61 94
62 This non-blocking subprocess allows the caller to continue operating while 95 This non-blocking subprocess allows the caller to continue operating while
63 also able to interact with this subprocess based on a key returned to 96 also able to interact with this subprocess based on a key returned to
64 the caller at the time of creation. 97 the caller at the time of creation.
65 98
66 Creation args are set via Set* methods called after calling Process but 99 Creation args are set via Set* methods called after calling Process but
67 before calling Start. This is due to a limitation of the XML-RPC 100 before calling Start. This is due to a limitation of the XML-RPC
68 implementation not supporting keyword arguments. 101 implementation not supporting keyword arguments.
69 """ 102 """
70 103
(...skipping 30 matching lines...) Expand all
101 @classmethod 134 @classmethod
102 def KillAll(cls): 135 def KillAll(cls):
103 for key in cls._processes: 136 for key in cls._processes:
104 cls.Kill(key) 137 cls.Kill(key)
105 138
106 @classmethod 139 @classmethod
107 def Process(cls, cmd): 140 def Process(cls, cmd):
108 with cls._creation_lock: 141 with cls._creation_lock:
109 key = 'Process%d' % cls._process_next_id 142 key = 'Process%d' % cls._process_next_id
110 cls._process_next_id += 1 143 cls._process_next_id += 1
111 logging.debug('Creating process %s', key) 144 logging.debug('Creating process %s with cmd %r', key, cmd)
112 process = cls(cmd) 145 process = cls(cmd)
113 cls._processes[key] = process 146 cls._processes[key] = process
114 return key 147 return key
115 148
116 def _Start(self): 149 def _Start(self):
117 logging.info('Starting process %s', self) 150 logging.info('Starting process %s', self)
118 self.proc = subprocess42.Popen(self.cmd, stdout=subprocess42.PIPE, 151 self.proc = subprocess42.Popen(self.cmd, stdout=subprocess42.PIPE,
119 stderr=subprocess42.PIPE, 152 stderr=subprocess42.PIPE,
120 detached=self.detached, cwd=self.cwd) 153 detached=self.detached, cwd=self.cwd)
121 threading.Thread(target=self._reader).start() 154 threading.Thread(target=self._reader).start()
122 155
123 @classmethod 156 @classmethod
124 def Start(cls, key): 157 def Start(cls, key):
125 cls._processes[key]._Start() 158 cls._processes[key]._Start()
126 159
127 @classmethod 160 @classmethod
128 def SetCwd(cls, key, cwd): 161 def SetCwd(cls, key, cwd):
129 """Sets the process's cwd.""" 162 """Sets the process's cwd."""
130 logging.debug('Setting %s cwd to %s', key, cwd) 163 logging.debug('Setting %s cwd to %s', key, cwd)
131 cls._processes[key].cwd = cwd 164 cls._processes[key].cwd = cwd
132 165
133 @classmethod 166 @classmethod
134 def SetDetached(cls, key): 167 def SetDetached(cls, key):
135 """Creates a detached process.""" 168 """Creates a detached process."""
136 logging.debug('Setting %s to run detached', key) 169 logging.debug('Setting %s.detached = True', key)
137 cls._processes[key].detached = True 170 cls._processes[key].detached = True
138 171
139 @classmethod 172 @classmethod
140 def SetVerbose(cls, key): 173 def SetVerbose(cls, key):
141 """Sets the stdout and stderr to be emitted locally.""" 174 """Sets the stdout and stderr to be emitted locally."""
142 logging.debug('Setting %s to be verbose', key) 175 logging.debug('Setting %s.verbose = True', key)
143 cls._processes[key].verbose = True 176 cls._processes[key].verbose = True
144 177
145 @classmethod 178 @classmethod
146 def Terminate(cls, key): 179 def Terminate(cls, key):
147 logging.debug('Terminating process %s', key) 180 logging.debug('Terminating process %s', key)
148 cls._processes[key].proc.terminate() 181 cls._processes[key].proc.terminate()
149 182
150 @classmethod 183 @classmethod
151 def Kill(cls, key): 184 def Kill(cls, key):
152 logging.debug('Killing process %s', key) 185 logging.debug('Killing process %s', key)
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
205 def Wait(cls, key): 238 def Wait(cls, key):
206 return cls._processes[key].proc.wait() 239 return cls._processes[key].proc.wait()
207 240
208 @classmethod 241 @classmethod
209 def Poll(cls, key): 242 def Poll(cls, key):
210 return cls._processes[key].proc.poll() 243 return cls._processes[key].proc.poll()
211 244
212 @classmethod 245 @classmethod
213 def GetPid(cls, key): 246 def GetPid(cls, key):
214 return cls._processes[key].proc.pid 247 return cls._processes[key].proc.pid
OLDNEW
« no previous file with comments | « testing/legion/legion_test_case.py ('k') | testing/legion/rpc_methods.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698