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

Side by Side Diff: client/utils/file_path.py

Issue 2934983003: Improve error handling for file_path.rmtree() (Closed)
Patch Set: Redo Created 3 years, 6 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 | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2013 The LUCI Authors. All rights reserved. 1 # Copyright 2013 The LUCI Authors. All rights reserved.
2 # Use of this source code is governed under the Apache License, Version 2.0 2 # Use of this source code is governed under the Apache License, Version 2.0
3 # that can be found in the LICENSE file. 3 # that can be found in the LICENSE file.
4 4
5 """Provides functions: get_native_path_case(), isabs() and safe_join(). 5 """Provides functions: get_native_path_case(), isabs() and safe_join().
6 6
7 This module assumes that filesystem is not changing while current process 7 This module assumes that filesystem is not changing while current process
8 is running and thus it caches results of functions that depend on FS state. 8 is running and thus it caches results of functions that depend on FS state.
9 """ 9 """
10 10
(...skipping 1075 matching lines...) Expand 10 before | Expand all | Expand 10 after
1086 1086
1087 # First try the soft way: tries 3 times to delete and sleep a bit in between. 1087 # First try the soft way: tries 3 times to delete and sleep a bit in between.
1088 # Retries help if test subprocesses outlive main process and try to actively 1088 # Retries help if test subprocesses outlive main process and try to actively
1089 # use or write to the directory while it is being deleted. 1089 # use or write to the directory while it is being deleted.
1090 max_tries = 3 1090 max_tries = 3
1091 for i in xrange(max_tries): 1091 for i in xrange(max_tries):
1092 # errors is a list of tuple(function, path, excinfo). 1092 # errors is a list of tuple(function, path, excinfo).
1093 errors = [] 1093 errors = []
1094 fs.rmtree(root, onerror=lambda *args: errors.append(args)) 1094 fs.rmtree(root, onerror=lambda *args: errors.append(args))
1095 if not errors: 1095 if not errors:
1096 if i:
1097 sys.stderr.write('Succeeded.\n')
1096 return True 1098 return True
1097 if not i and sys.platform == 'win32': 1099 if not i and sys.platform == 'win32':
1098 for _, path, _ in errors: 1100 for path in sorted(set(path for _, path, _ in errors)):
1099 try: 1101 try:
1100 change_acl_for_delete(path) 1102 change_acl_for_delete(path)
1101 except Exception as e: 1103 except Exception as e:
1102 sys.stderr.write('- %s (failed to update ACL: %s)\n' % (path, e)) 1104 sys.stderr.write('- %s (failed to update ACL: %s)\n' % (path, e))
1103 1105
1104 if i == max_tries - 1: 1106 if i != max_tries - 1:
1105 sys.stderr.write(
1106 'Failed to delete %s. The following files remain:\n' % root)
1107 for _, path, _ in errors:
1108 sys.stderr.write('- %s\n' % path)
1109 else:
1110 delay = (i+1)*2 1107 delay = (i+1)*2
1111 sys.stderr.write( 1108 sys.stderr.write(
1112 'Failed to delete %s (%d files remaining).\n' 1109 'Failed to delete %s (%d files remaining).\n'
1113 ' Maybe the test has a subprocess outliving it.\n' 1110 ' Maybe the test has a subprocess outliving it.\n'
1114 ' Sleeping %d seconds.\n' % 1111 ' Sleeping %d seconds.\n' %
1115 (root, len(errors), delay)) 1112 (root, len(errors), delay))
1116 time.sleep(delay) 1113 time.sleep(delay)
1117 1114
1115 sys.stderr.write(
Vadim Sh. 2017/06/16 22:18:10 please add fs.exists(root) here too and ignore any
M-A Ruel 2017/06/17 00:42:57 added at line 1095 instead.
1116 'Failed to delete %s. The following files remain:\n' % root)
1117 # The same path may be listed multiple times.
1118 for path in sorted(set(path for _, path, _ in errors)):
1119 sys.stderr.write('- %s\n' % path)
1120
1118 # If soft retries fail on Linux, there's nothing better we can do. 1121 # If soft retries fail on Linux, there's nothing better we can do.
1119 if sys.platform != 'win32': 1122 if sys.platform != 'win32':
1120 raise errors[0][2][0], errors[0][2][1], errors[0][2][2] 1123 raise errors[0][2][0], errors[0][2][1], errors[0][2][2]
1121 1124
1122 # The soft way was not good enough. Try the hard way. Enumerates both: 1125 # The soft way was not good enough. Try the hard way.
1123 # - all child processes from this process. 1126 for i in xrange(max_tries):
1124 # - processes where the main executable in inside 'root'. The reason is that 1127 if not _kill_children_processes_win(root):
1125 # the ancestry may be broken so stray grand-children processes could be
1126 # undetected by the first technique.
1127 # This technique is not fool-proof but gets mostly there.
1128 def get_processes():
1129 processes = enum_processes_win()
1130 tree_processes = filter_processes_tree_win(processes)
1131 dir_processes = filter_processes_dir_win(processes, root)
1132 # Convert to dict to remove duplicates.
1133 processes = dict((p.ProcessId, p) for p in tree_processes)
1134 processes.update((p.ProcessId, p) for p in dir_processes)
1135 processes.pop(os.getpid())
1136 return processes
1137
1138 for i in xrange(3):
1139 sys.stderr.write('Enumerating processes:\n')
1140 processes = get_processes()
1141 if not processes:
1142 break 1128 break
1143 for _, proc in sorted(processes.iteritems()): 1129 if i != max_tries - 1:
1144 sys.stderr.write(
1145 '- pid %d; Handles: %d; Exe: %s; Cmd: %s\n' % (
1146 proc.ProcessId,
1147 proc.HandleCount,
1148 proc.ExecutablePath,
1149 proc.CommandLine))
1150 sys.stderr.write('Terminating %d processes.\n' % len(processes))
1151 for pid in sorted(processes):
1152 try:
1153 # Killing is asynchronous.
1154 os.kill(pid, 9)
1155 sys.stderr.write('- %d killed\n' % pid)
1156 except OSError:
1157 sys.stderr.write('- failed to kill %s\n' % pid)
1158 if i < 2:
1159 time.sleep((i+1)*2) 1130 time.sleep((i+1)*2)
1160 else: 1131 else:
1161 processes = get_processes() 1132 processes = _get_children_processes_win(root)
1162 if processes: 1133 if processes:
1163 sys.stderr.write('Failed to terminate processes.\n') 1134 sys.stderr.write('Failed to terminate processes.\n')
1164 raise errors[0][2][0], errors[0][2][1], errors[0][2][2] 1135 raise errors[0][2][0], errors[0][2][1], errors[0][2][2]
1165 1136
1166 # Now that annoying processes in root are evicted, try again. 1137 # Now that annoying processes in root are evicted, try again.
1167 errors = [] 1138 errors = []
1168 fs.rmtree(root, onerror=lambda *args: errors.append(args)) 1139 fs.rmtree(root, onerror=lambda *args: errors.append(args))
1169 if errors: 1140 if errors and fs.exists(root):
1170 # There's no hope. 1141 # There's no hope: the directory was tried to be removed 4 times. Give up
1142 # and raise an exception.
1171 sys.stderr.write( 1143 sys.stderr.write(
1172 'Failed to delete %s. The following files remain:\n' % root) 1144 'Failed to delete %s. The following files remain:\n' % root)
1173 for _, path, _ in errors: 1145 # The same path may be listed multiple times.
1146 for path in sorted(set(path for _, path, _ in errors)):
1174 sys.stderr.write('- %s\n' % path) 1147 sys.stderr.write('- %s\n' % path)
1175 raise errors[0][2][0], errors[0][2][1], errors[0][2][2] 1148 raise errors[0][2][0], errors[0][2][1], errors[0][2][2]
1176 return False 1149 return False
1150
1151
1152 ## Private code.
1153
1154
1155 def _kill_children_processes_win(root):
1156 """Try to kill all children processes indistriminately and prints updates to
1157 stderr.
1158
1159 Returns:
1160 True if at least one child process was found.
1161 """
1162 processes = _get_children_processes_win(root)
1163 if not processes:
1164 return False
1165 sys.stderr.write('Enumerating processes:\n')
1166 for _, proc in sorted(processes.iteritems()):
1167 sys.stderr.write(
1168 '- pid %d; Handles: %d; Exe: %s; Cmd: %s\n' % (
1169 proc.ProcessId,
1170 proc.HandleCount,
1171 proc.ExecutablePath,
1172 proc.CommandLine))
1173 sys.stderr.write('Terminating %d processes:\n' % len(processes))
1174 for pid in sorted(processes):
1175 try:
1176 # Killing is asynchronous.
1177 os.kill(pid, 9)
1178 sys.stderr.write('- %d killed\n' % pid)
1179 except OSError:
1180 sys.stderr.write('- failed to kill %s\n' % pid)
1181 return True
1182
1183
1184 def _get_children_processes_win(root):
1185 """Returns a list of processes.
1186
1187 Enumerates both:
1188 - all child processes from this process.
1189 - processes where the main executable in inside 'root'. The reason is that
1190 the ancestry may be broken so stray grand-children processes could be
1191 undetected by the first technique.
1192
1193 This technique is not fool-proof but gets mostly there.
1194 """
1195 processes = enum_processes_win()
1196 tree_processes = filter_processes_tree_win(processes)
1197 dir_processes = filter_processes_dir_win(processes, root)
1198 # Convert to dict to remove duplicates.
1199 processes = dict((p.ProcessId, p) for p in tree_processes)
1200 processes.update((p.ProcessId, p) for p in dir_processes)
1201 processes.pop(os.getpid())
1202 return processes
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698