13742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// Copyright (c) 2012 The Chromium Authors. All rights reserved. 23742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// Use of this source code is governed by a BSD-style license that can be 33742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// found in the LICENSE file. 43742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 53742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman#include <stdio.h> 63742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman#include <stdlib.h> 73742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 83742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman#define NOMINMAX 93742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman#include <windows.h> 103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman#include <algorithm> 123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman#include <iterator> 133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman#include <sstream> 143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman#include <string> 153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman#include <vector> 163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romantypedef std::basic_string<TCHAR> tstring; 183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romannamespace { 203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman const bool g_is_debug = (_wgetenv(L"LIMITER_DEBUG") != NULL); 213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman} 223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// Don't use stderr for errors because VS has large buffers on them, leading 243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// to confusing error output. 253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanstatic void Error(const wchar_t* msg, ...) { 263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman tstring new_msg = tstring(L"limiter fatal error: ") + msg + L"\n"; 273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman va_list args; 283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman va_start(args, msg); 293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman vwprintf(new_msg.c_str(), args); 303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman va_end(args); 313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman} 323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanstatic void Warn(const wchar_t* msg, ...) { 343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (!g_is_debug) 353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman return; 363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman tstring new_msg = tstring(L"limiter warning: ") + msg + L"\n"; 373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman va_list args; 383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman va_start(args, msg); 393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman vwprintf(new_msg.c_str(), args); 403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman va_end(args); 413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman} 423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanstatic tstring ErrorMessageToString(DWORD err) { 443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman TCHAR* msg_buf = NULL; 453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman DWORD rc = FormatMessage( 463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman NULL, // lpSource 483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman err, 493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman reinterpret_cast<LPTSTR>(&msg_buf), 513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 0, // nSize 523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman NULL); // Arguments 533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (!rc) 543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman return L"unknown error"; 553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman tstring ret(msg_buf); 563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman LocalFree(msg_buf); 573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman return ret; 583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman} 593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanstatic DWORD RunExe(const tstring& exe_name) { 613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman STARTUPINFO startup_info = { sizeof(STARTUPINFO) }; 623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman PROCESS_INFORMATION process_info; 633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman DWORD exit_code; 643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman GetStartupInfo(&startup_info); 663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman tstring cmdline = tstring(GetCommandLine()); 673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman size_t first_space = cmdline.find(' '); 693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (first_space == -1) { 703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // I'm not sure why this would ever happen, but just in case... 713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman cmdline = exe_name; 723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } else { 733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman cmdline = exe_name + cmdline.substr(first_space); 743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (!CreateProcess(NULL, // lpApplicationName 773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman &cmdline[0], 783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman NULL, // lpProcessAttributes 793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman NULL, // lpThreadAttributes 803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman TRUE, // bInheritHandles 813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 0, // dwCreationFlags, 823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman NULL, // lpEnvironment, 833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman NULL, // lpCurrentDirectory, 843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman &startup_info, 853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman &process_info)) { 863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman Error(L"Error in CreateProcess[%s]: %s", 873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman cmdline.c_str(), ErrorMessageToString(GetLastError()).c_str()); 883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman return MAXDWORD; 893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman CloseHandle(process_info.hThread); 913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman WaitForSingleObject(process_info.hProcess, INFINITE); 923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman GetExitCodeProcess(process_info.hProcess, &exit_code); 933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman CloseHandle(process_info.hProcess); 943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman return exit_code; 953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman} 963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// Returns 0 if there was an error 983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanstatic int CpuConcurrencyMetric(const tstring& envvar_name) { 993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman int max_concurrent = 0; 1003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman std::vector<char> buffer(1); 1013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman BOOL ok = false; 1023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman DWORD last_error = 0; 1033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman do { 1043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman DWORD bufsize = buffer.size(); 1053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman ok = GetLogicalProcessorInformation( 1063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman reinterpret_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION>(&buffer[0]), 1073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman &bufsize); 1083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman last_error = GetLastError(); 1093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (!ok && last_error == ERROR_INSUFFICIENT_BUFFER && 1103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman bufsize > buffer.size()) { 1113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman buffer.resize(bufsize); 1123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } while (!ok && last_error == ERROR_INSUFFICIENT_BUFFER); 1143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (!ok) { 1163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman Warn(L"Error while getting number of cores. Try setting the " 1173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" environment variable '%s' to (num_cores - 1): %s", 1183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman envvar_name.c_str(), ErrorMessageToString(last_error).c_str()); 1193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman return 0; 1203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman PSYSTEM_LOGICAL_PROCESSOR_INFORMATION pproc_info = 1233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman reinterpret_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION>(&buffer[0]); 1243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman int num_entries = buffer.size() / 1253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); 1263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman for (int i = 0; i < num_entries; ++i) { 1283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman SYSTEM_LOGICAL_PROCESSOR_INFORMATION& info = pproc_info[i]; 1293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (info.Relationship == RelationProcessorCore) { 1303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman ++max_concurrent; 1313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // Leave one core for other tasks 1353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman return max_concurrent - 1; 1363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman} 1373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// TODO(defaults): Create a better heuristic than # of CPUs. It seems likely 1393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// that the right value will, in fact, be based on the memory capacity of the 1403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// machine, not on the number of CPUs. 1413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanenum ConcurrencyMetricEnum { 1423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman CONCURRENCY_METRIC_ONE, 1433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman CONCURRENCY_METRIC_CPU, 1443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman CONCURRENCY_METRIC_DEFAULT = CONCURRENCY_METRIC_CPU 1453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman}; 1463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanstatic int GetMaxConcurrency(const tstring& base_pipename, 1483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman ConcurrencyMetricEnum metric) { 1493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman static int max_concurrent = -1; 1503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (max_concurrent == -1) { 1523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman tstring envvar_name = base_pipename + L"_MAXCONCURRENCY"; 1533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman const LPTSTR max_concurrent_str = _wgetenv(envvar_name.c_str()); 1553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman max_concurrent = max_concurrent_str ? _wtoi(max_concurrent_str) : 0; 1563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (max_concurrent == 0) { 1583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman switch (metric) { 1593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman case CONCURRENCY_METRIC_CPU: 1603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman max_concurrent = CpuConcurrencyMetric(envvar_name); 1613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (max_concurrent) 1623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman break; 1633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // else fall through 1643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman case CONCURRENCY_METRIC_ONE: 1653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman max_concurrent = 1; 1663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman break; 1673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman max_concurrent = std::min(std::max(max_concurrent, 1), 1713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman PIPE_UNLIMITED_INSTANCES); 1723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman return max_concurrent; 1753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman} 1763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanstatic HANDLE WaitForPipe(const tstring& pipename, 1783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman HANDLE event, 1793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman int max_concurrency) { 1803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // We're using a named pipe instead of a semaphore so the Kernel can clean up 1813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // after us if we crash while holding onto the pipe (A real semaphore will 1823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // not release on process termination). 1833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman HANDLE pipe = INVALID_HANDLE_VALUE; 1843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman for (;;) { 1853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman pipe = CreateNamedPipe( 1863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman pipename.c_str(), 1873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman PIPE_ACCESS_DUPLEX, 1883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman PIPE_TYPE_BYTE, 1893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman max_concurrency, 1903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1, // nOutBufferSize 1913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1, // nInBufferSize 1923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 0, // nDefaultTimeOut 1933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman NULL); // Default security attributes (noinherit) 1943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (pipe != INVALID_HANDLE_VALUE) 1953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman break; 1963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman DWORD error = GetLastError(); 1983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (error == ERROR_PIPE_BUSY) { 1993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (event) { 2003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman WaitForSingleObject(event, 60 * 1000 /* ms */); 2013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } else { 2023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // TODO(iannucci): Maybe we should error out here instead of falling 2033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // back to a sleep-poll 2043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman Sleep(5 * 1000 /* ms */); 2053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 2063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } else { 2073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman Warn(L"Got error %d while waiting for pipe: %s", error, 2083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman ErrorMessageToString(error).c_str()); 2093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman return INVALID_HANDLE_VALUE; 2103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 2113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 2123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 2133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman return pipe; 2143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman} 2153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 2163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanstatic int WaitAndRun(const tstring& shimmed_exe, 2173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman const tstring& base_pipename) { 2183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman ULONGLONG start_time = 0, end_time = 0; 2193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman tstring pipename = L"\\\\.\\pipe\\" + base_pipename; 2203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman tstring event_name = L"Local\\EVENT_" + base_pipename; 2213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 2223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // This event lets us do better than strict polling, but we don't rely on it 2233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // (in case a process crashes before signalling the event). 2243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman HANDLE event = CreateEvent( 2253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman NULL, // Default security attributes 2263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman FALSE, // Manual reset 2273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman FALSE, // Initial state 2283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman event_name.c_str()); 2293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 2303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (g_is_debug) 2313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman start_time = GetTickCount64(); 2323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 2333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman HANDLE pipe = 2343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman WaitForPipe(pipename, event, 2353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman GetMaxConcurrency(base_pipename, CONCURRENCY_METRIC_DEFAULT)); 2363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 2373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (g_is_debug) { 2383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman end_time = GetTickCount64(); 2393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman wprintf(L" took %.2fs to acquire semaphore.\n", 2403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman (end_time - start_time) / 1000.0); 2413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 2423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 2433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman DWORD ret = RunExe(shimmed_exe); 2443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 2453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (pipe != INVALID_HANDLE_VALUE) 2463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman CloseHandle(pipe); 2473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (event != NULL) 2483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman SetEvent(event); 2493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 2503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman return ret; 2513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman} 2523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 2533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanvoid Usage(const tstring& msg) { 2543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman tstring usage(msg); 2553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman usage += L"\n" 2563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L"Usage: SHIMED_NAME__SEMAPHORE_NAME\n" 2573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L"\n" 2583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" SHIMMED_NAME - ex. 'link.exe' or 'lib.exe'\n" 2593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" - can be exe, bat, or com\n" 2603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" - must exist in PATH\n" 2613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L"\n" 2623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" SEMAPHORE_NAME - ex. 'SOME_NAME' or 'GROOVY_SEMAPHORE'\n" 2633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L"\n" 2643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" Example:\n" 2653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" link.exe__LINK_LIMITER.exe\n" 2663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" lib.exe__LINK_LIMITER.exe\n" 2673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" * Both will limit on the same semaphore\n" 2683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L"\n" 2693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" link.exe__LINK_LIMITER.exe\n" 2703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" lib.exe__LIB_LIMITER.exe\n" 2713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" * Both will limit on independent semaphores\n" 2723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L"\n" 2733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" This program is meant to be run after renaming it into the\n" 2743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" above format. Once you have done so, executing it will block\n" 2753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" on the availability of the semaphore SEMAPHORE_NAME. Once\n" 2763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" the semaphore is obtained, it will execute SHIMMED_NAME, \n" 2773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" passing through all arguments as-is.\n" 2783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L"\n" 2793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" The maximum concurrency can be manually set by setting the\n" 2803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" environment variable <SEMAPHORE_NAME>_MAXCONCURRENCY to an\n" 2813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" integer value (1, 254).\n" 2823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" * This value must be set the same for ALL invocations.\n" 2833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" * If the value is not set, it defaults to (num_cores-1).\n" 2843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L"\n" 2853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" The semaphore is automatically released when the program\n" 2863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" completes normally, OR if the program crashes (or even if\n" 2873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L" limiter itself crashes).\n"; 2883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman Error(usage.c_str()); 2893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman exit(-1); 2903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman} 2913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 2923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// Input command line is assumed to be of the form: 2933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// 2943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// thing.exe__PIPE_NAME.exe ... 2953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// 2963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// Specifically, wait for a semaphore (whose concurrency is specified by 2973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// LIMITER_MAXCONCURRENT), and then pass through everything once we have 2983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// acquired the semaphore. 2993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// 3003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// argv[0] is parsed for: 3013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// * exe_to_shim_including_extension.exe 3023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// * This could also be a bat or com. Anything that CreateProcess will 3033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// accept. 3043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// * "__" 3053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// * We search for this separator from the end of argv[0], so the exe name 3063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// could contain a double underscore if necessary. 3073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// * PIPE_NAME 3083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// * Can only contain single underscores, not a double underscore. 3093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// * i.e. HELLO_WORLD_PIPE will work, but HELLO__WORLD_PIPE will not. 3103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// * This would allow the shimmed exe to contain arbitrary numbers of 3113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// underscores. We control the pipe name, but not necessarily the thing 3123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// we're shimming. 3133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// 3143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanint wmain(int, wchar_t** argv) { 3153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman tstring shimmed_plus_pipename = argv[0]; 3163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman size_t last_slash = shimmed_plus_pipename.find_last_of(L"/\\"); 3173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (last_slash != tstring::npos) { 3183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman shimmed_plus_pipename = shimmed_plus_pipename.substr(last_slash + 1); 3193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 3203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 3213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman size_t separator = shimmed_plus_pipename.rfind(L"__"); 3223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (separator == tstring::npos) { 3233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman Usage(L"Cannot parse argv[0]. No '__' found. " 3243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman L"Should be like '[...(\\|/)]link.exe__PIPE_NAME.exe'"); 3253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 3263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman tstring shimmed_exe = shimmed_plus_pipename.substr(0, separator); 3273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman tstring base_pipename = shimmed_plus_pipename.substr(separator + 2); 3283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 3293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman size_t dot = base_pipename.find(L'.'); 3303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (dot == tstring::npos) { 3313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman Usage(L"Expected an executable extension in argv[0]. No '.' found."); 3323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 3333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman base_pipename = base_pipename.substr(0, dot); 3343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 3353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman return WaitAndRun(shimmed_exe, base_pipename); 3363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman} 3373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 3383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman