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

Side by Side Diff: tools/win/link_limiter/limiter.cpp

Issue 10826067: Implement a tool called link_limiter to impose a global restriction on how many (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: refactor concurrency metrics Created 8 years, 4 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 | Annotate | Revision Log
« .gitignore ('K') | « tools/win/link_limiter/build_link_limiter.py ('k') | 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
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #define NOMINMAX
6 #include <windows.h>
7
8 #include <stdio.h>
9 #include <stdlib.h>
10
11 #include <algorithm>
12 #include <iterator>
13 #include <string>
14 #include <sstream>
15 #include <vector>
16
17 #ifndef SHIM_EXE
18 # error You must define SHIM_EXE when building (like "link.exe" or "lib.exe")
19 #endif
20
21 typedef std::basic_string<TCHAR> tstring;
22
23 const bool g_is_debug = (_wgetenv(L"LIMITER_DEBUG") != NULL);
24 const int g_wait_time = 30 * 1000; // 30 seconds
25 const LPTSTR g_pipe_name = L"\\\\.\\pipe\\LIMITER_PIPE";
26 const LPTSTR g_event_name = L"Local\\LIMITER_EVENT";
27 const LPTSTR g_envvar_name = L"LIMITER_MAXCONCURRENT";
28
29 // Don't use stderr for errors because VS has large buffers on them, leading
30 // to confusing error output.
31 static void Fatal(const wchar_t* msg) {
32 wprintf(L"limiter(" SHIM_EXE L") fatal error: %s\n", msg);
33 exit(1);
34 }
35
36 static tstring ErrorMessageToString(DWORD err) {
37 wchar_t* msg_buf = NULL;
38 DWORD rc = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
39 FORMAT_MESSAGE_FROM_SYSTEM,
40 NULL,
41 err,
42 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
43 reinterpret_cast<LPTSTR>(&msg_buf),
44 0,
45 NULL);
46 if (!rc)
47 return L"unknown error";
48 tstring ret(msg_buf);
49 LocalFree(msg_buf);
50 return ret;
51 }
52
53 static DWORD RunExe() {
54 STARTUPINFO startup_info = { sizeof(STARTUPINFO) };
55 PROCESS_INFORMATION process_info;
56 DWORD exit_code;
57
58 GetStartupInfo(&startup_info);
59 tstring cmdline = tstring(GetCommandLine());
60
61 size_t first_space = cmdline.find(' ')+1;
62 if (first_space == -1) {
63 // I'm not sure why this would ever happen, but just in case...
64 cmdline = SHIM_EXE;
65 } else {
66 cmdline = SHIM_EXE L" " + cmdline.substr(first_space);
67 }
68
69 if (!CreateProcess(NULL,
70 const_cast<TCHAR*>(cmdline.c_str()),
71 NULL,
72 NULL,
73 TRUE,
74 0,
75 NULL,
76 NULL,
77 &startup_info, &process_info)) {
78 tstring error = ErrorMessageToString(GetLastError());
79 Fatal(error.c_str());
80 }
81 CloseHandle(process_info.hThread);
82 WaitForSingleObject(process_info.hProcess, INFINITE);
83 GetExitCodeProcess(process_info.hProcess, &exit_code);
84 CloseHandle(process_info.hProcess);
85 return exit_code;
86 }
87
88 int CpuConcurrencyMetric() {
89 int max_concurrent = 0;
90 std::vector<char> buffer(1);
91 BOOL ok = false;
92 DWORD last_error = 0;
93 do {
94 DWORD bufsize = buffer.size();
95 ok = GetLogicalProcessorInformation(
96 reinterpret_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION>(&buffer[0]),
97 &bufsize);
98 last_error = GetLastError();
99 if (!ok && last_error == ERROR_INSUFFICIENT_BUFFER &&
100 bufsize > buffer.size())
101 {
102 buffer.resize(bufsize);
103 }
104 } while(!ok && last_error == ERROR_INSUFFICIENT_BUFFER);
105
106 if (!ok) {
107 if (g_is_debug)
108 wprintf(L"Error while getting number of cores. Try setting the "
109 L" environment variable '%s' to (num_cores-1): %d\n",
110 g_envvar_name, last_error);
111 exit(RunExe());
112 }
113
114 PSYSTEM_LOGICAL_PROCESSOR_INFORMATION pproc_info =
115 reinterpret_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION>(&buffer[0]);
116 int num_entries = buffer.size() /
117 sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
118
119 for(int i = 0; i < num_entries; ++i) {
120 SYSTEM_LOGICAL_PROCESSOR_INFORMATION &info = pproc_info[i];
121 if(info.Relationship == RelationProcessorCore) {
122 max_concurrent += 1;
123 }
124 }
125
126 // Leave one core for other tasks
127 return max_concurrent-1;
128 }
129
130
131 // TODO(defaults): Create a better heuristic than # of CPUs. It seems likely
132 // that the right value will, in fact, be based on the memory capacity of the
133 // machine, not on the number of CPUs.
134 enum ConcurrencyMetricEnum {
135 CONCURRENCY_METRIC_ONE,
136 CONCURRENCY_METRIC_CPU,
137 CONCURRENCY_METRIC_DEFAULT = CONCURRENCY_METRIC_CPU
138 };
139
140 int GetMaxConcurrency(
141 ConcurrencyMetricEnum metric = CONCURRENCY_METRIC_DEFAULT)
142 {
143 static int max_concurrent = -1;
144
145 if (max_concurrent == -1) {
146 const LPTSTR max_concurrent_str = _wgetenv(g_envvar_name);
147 max_concurrent = _wtoi(max_concurrent_str ? max_concurrent_str : L"0");
148
149 if (max_concurrent == 0) {
150 switch(metric) {
151 case CONCURRENCY_METRIC_ONE:
152 max_concurrent = 1;
153 break;
154 case CONCURRENCY_METRIC_CPU:
155 max_concurrent = CpuConcurrencyMetric();
156 break;
157 }
158 }
159
160 max_concurrent = std::min(std::max(max_concurrent, 1),
161 PIPE_UNLIMITED_INSTANCES);
162 }
163
164 return max_concurrent;
165 }
166
167
168 // Input command line is assumed to be of the form:
169 //
170 // SHIM_EXE ...
171 //
172 // Specifically, wait for a semaphore (whose concurrency is specified by
173 // LIMITER_MAXCONCURRENT), and then pass through everything once we have
174 // acquired the semaphore.
175 int wmain(int, wchar_t**) {
176 ULONGLONG start_time = 0, end_time = 0;
177
178 if (g_is_debug)
179 start_time = GetTickCount64();
180
181 // This event lets us do better than strict polling, but we don't rely on it
182 // (in case a process crashes before signalling the event).
183 HANDLE hEvent = CreateEvent(
184 NULL, // Default security attributes
185 FALSE, // Manual reset
186 FALSE, // Initial state
187 g_event_name);
188
189 // We're using a named pipe instead of a semaphore so the Kernel can clean up
190 // after us if we crash while holding onto the pipe (A real semaphore will
191 // not release on process termination).
192 HANDLE hPipe = INVALID_HANDLE_VALUE;
193 for(;;) {
194 hPipe = CreateNamedPipe(
195 g_pipe_name,
196 PIPE_ACCESS_DUPLEX,
197 PIPE_TYPE_BYTE,
198 GetMaxConcurrency(),
199 1, // nOutBufferSize
200 1, // nInBufferSize
201 0, // nDefaultTimeOut
202 NULL // Default security attributes (noinherit)
203 );
204 if (hPipe != INVALID_HANDLE_VALUE)
205 break;
206
207 DWORD error = GetLastError();
208 if (error == ERROR_PIPE_BUSY) {
209 if (hEvent) {
210 WaitForSingleObject(hEvent, g_wait_time);
211 } else {
212 Sleep(g_wait_time);
213 }
214 } else {
215 if (g_is_debug) {
216 std::wostringstream strm;
217 strm << L"Got error " << error << L": "
218 << ErrorMessageToString(error) << L"\n";
219 wprintf(strm.str().c_str());
220 }
221 return RunExe();
222 }
223 }
224
225 if (g_is_debug) {
226 end_time = GetTickCount64();
227 wprintf(L" took %.2fs to acquire semaphore.\n",
228 (end_time - start_time) / 1000.0);
229 }
230
231 DWORD ret = RunExe();
232
233 CloseHandle(hPipe);
234 if (hEvent != NULL) {
235 SetEvent(hEvent);
236 }
237
238 return ret;
239 }
OLDNEW
« .gitignore ('K') | « tools/win/link_limiter/build_link_limiter.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698