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