Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Work around popen not being thread safe on MacOS #695

Merged
merged 79 commits into from
Nov 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
53ca24e
Fix rare crash
Thomas1664 Sep 4, 2022
173e43d
Fix return type of `get_concurrency`
Thomas1664 Sep 4, 2022
b6e9f86
Driveby: Proper exception handling for `std::stoi()`
Thomas1664 Sep 4, 2022
df2e722
format
Thomas1664 Sep 4, 2022
fe1c13f
Remove lock
Thomas1664 Sep 5, 2022
2d555ef
Add unit test
Thomas1664 Sep 5, 2022
b8619e6
Fix unit test
Thomas1664 Sep 6, 2022
7d7c673
Fix unit test
Thomas1664 Sep 6, 2022
ce4650d
FIx apple CI
Thomas1664 Sep 6, 2022
8949eda
Try again
Thomas1664 Sep 6, 2022
5e3e74e
Fix windows tests
Thomas1664 Sep 6, 2022
f7d82c4
Fix tests
Thomas1664 Sep 6, 2022
eac0671
debug
Thomas1664 Sep 6, 2022
b5783e3
Fix apple
Thomas1664 Sep 6, 2022
8dfa0a7
.
Thomas1664 Sep 6, 2022
032ca5e
.
Thomas1664 Sep 6, 2022
6960279
Additional debug info to confurm my guess
Thomas1664 Sep 6, 2022
a5ed40a
Where does it fail?
Thomas1664 Sep 6, 2022
8f65824
Gone?
Thomas1664 Sep 6, 2022
d8dbe0a
What are the contents of the string? Do I get segfault here?
Thomas1664 Sep 6, 2022
db44da0
Fix logging
Thomas1664 Sep 6, 2022
b4cb8ae
printf seems to prevent the bug
Thomas1664 Sep 6, 2022
4ea1e80
This bug is trying to hide
Thomas1664 Sep 6, 2022
a2c0a59
Fix use of stringview from temporary
Thomas1664 Sep 7, 2022
d2d95cc
Fix test
Thomas1664 Sep 7, 2022
41466d0
Disable debugging
Thomas1664 Sep 7, 2022
ff44929
Remove use of stringview
Thomas1664 Sep 7, 2022
46de042
Revert
Thomas1664 Sep 7, 2022
804dc14
Fix
Thomas1664 Sep 7, 2022
3326fd5
Are you sure?
Thomas1664 Sep 7, 2022
373d822
Fix stringview in debug
Thomas1664 Sep 7, 2022
cbaa801
Maybe we don't flush?
Thomas1664 Sep 7, 2022
a421ebd
Flush stdin as well
Thomas1664 Sep 7, 2022
4f94ec9
Remove debug message
Thomas1664 Sep 7, 2022
b79c39b
0 buffer
Thomas1664 Sep 7, 2022
0cb3879
fgets overflows
Thomas1664 Sep 7, 2022
92ee820
revert debugging changes
Thomas1664 Sep 7, 2022
0e388ac
add zeroing buffer again
Thomas1664 Sep 7, 2022
03c5ffc
more tests
Thomas1664 Sep 7, 2022
728f258
semicolon
Thomas1664 Sep 7, 2022
167971f
mode debug info
Thomas1664 Sep 7, 2022
fd30fe3
really?
Thomas1664 Sep 7, 2022
37eb5f8
Are you kidding me?
Thomas1664 Sep 7, 2022
ffd02c7
Fix build
Thomas1664 Sep 7, 2022
45a50e2
Where are the wrong inputs coming from?
Thomas1664 Sep 7, 2022
8abe11c
fix build
Thomas1664 Sep 7, 2022
6f00d21
remove debug
Thomas1664 Sep 7, 2022
8004b61
.
Thomas1664 Sep 7, 2022
5801b42
seems like popen doesn't like a const char* temporary
Thomas1664 Sep 7, 2022
f782422
Default initialize string to avoid reading from uninitialized memory
Thomas1664 Sep 7, 2022
e104faf
Why is the string not properly initialized?
Thomas1664 Sep 7, 2022
aeaef49
Default initialize string in `ExitCodeAndOutput`
Thomas1664 Sep 7, 2022
67ece80
Revert stringview
Thomas1664 Sep 7, 2022
eec8067
Ready
Thomas1664 Sep 7, 2022
b7bcd4e
0
Thomas1664 Sep 7, 2022
e4f9585
string view
Thomas1664 Sep 7, 2022
d316efc
Update src/vcpkg/base/system.process.cpp
Thomas1664 Sep 8, 2022
64590c3
Merge branch 'microsoft:main' into fix-crash
Thomas1664 Sep 10, 2022
411867b
Fix test on osx
Thomas1664 Sep 10, 2022
eba4910
Move workaround from archives to process
Thomas1664 Sep 10, 2022
1067ffb
format
Thomas1664 Sep 10, 2022
f4de072
Doesn't trigger bug at all
Thomas1664 Sep 10, 2022
1b34cda
comment out debug message
Thomas1664 Sep 10, 2022
6dbd453
move workaround to tests
Thomas1664 Sep 10, 2022
0c5dfb9
include stdio
Thomas1664 Sep 10, 2022
43be615
Ready for review
Thomas1664 Sep 10, 2022
12dbd71
Remove debugging
Thomas1664 Sep 10, 2022
81befe1
Merge branch 'microsoft:main' into fix-crash
Thomas1664 Sep 12, 2022
007f4d3
Actually fix race condition
Thomas1664 Sep 23, 2022
2c3e0bc
Merge branch 'microsoft:main' into fix-crash
Thomas1664 Oct 9, 2022
feac129
CR response
Thomas1664 Oct 13, 2022
1e0c58c
fix comment
Thomas1664 Oct 13, 2022
1178840
regenerate messages
Thomas1664 Oct 13, 2022
b9b5372
Merge remote-tracking branch 'origin/main' into fix-crash
BillyONeal Nov 16, 2022
e667203
Apply CR
Thomas1664 Nov 19, 2022
9c77f81
Partial revert
Thomas1664 Nov 19, 2022
a4f0e44
Revert revert
Thomas1664 Nov 19, 2022
c9c0885
Debug message
Thomas1664 Nov 19, 2022
c0e76df
Also wrap pclose with mutex
Thomas1664 Nov 19, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/vcpkg/base/messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,10 @@ namespace vcpkg
"Embedding `vcpkg-configuration` in a manifest file is an EXPERIMENTAL feature.");
DECLARE_MESSAGE(EmptyArg, (msg::option), "", "The option --{option} must be passed a non-empty argument.");
DECLARE_MESSAGE(EmptyLicenseExpression, (), "", "SPDX license expression was empty.");
DECLARE_MESSAGE(EnvInvalidMaxConcurrency,
(msg::env_var, msg::value),
"{value} is the invalid value of an environment variable",
"{env_var} is {value}, must be > 0");
DECLARE_MESSAGE(EndOfStringInCodeUnit, (), "", "found end of string in middle of code point");
DECLARE_MESSAGE(EnvStrFailedToExtract, (), "", "could not expand the environment string:");
DECLARE_MESSAGE(ErrorDetectingCompilerInfo,
Expand Down
2 changes: 1 addition & 1 deletion include/vcpkg/base/system.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ namespace vcpkg

const Optional<Path>& get_program_files_platform_bitness();

int get_concurrency();
unsigned int get_concurrency();

Optional<CPUArchitecture> guess_visual_studio_prompt_target_architecture();
}
2 changes: 2 additions & 0 deletions locales/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@
"_EmptyArg.comment": "An example of {option} is editable.",
"EmptyLicenseExpression": "SPDX license expression was empty.",
"EndOfStringInCodeUnit": "found end of string in middle of code point",
"EnvInvalidMaxConcurrency": "{env_var} is {value}, must be > 0",
"_EnvInvalidMaxConcurrency.comment": "{value} is the invalid value of an environment variable An example of {env_var} is VCPKG_DEFAULT_TRIPLET.",
"EnvStrFailedToExtract": "could not expand the environment string:",
"ErrorDetectingCompilerInfo": "while detecting compiler information:\nThe log file content at \"{path}\" is:",
"_ErrorDetectingCompilerInfo.comment": "An example of {path} is /foo/bar.",
Expand Down
34 changes: 34 additions & 0 deletions src/vcpkg-test/system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,37 @@ TEST_CASE ("cmdlinebuilder", "[system]")
REQUIRE(cmd.command_line() == "\"trailing\\\\slash\\\\\" \"inner\\\"quotes\"");
#endif
}

TEST_CASE ("cmd_execute_and_capture_output_parallel", "[system]")
{
std::vector<vcpkg::Command> vec;
for (size_t i = 0; i < 50; i++)
{
#if defined(_WIN32)
vcpkg::Command cmd("cmd.exe");
cmd.string_arg("/c");
const auto cmd_str = "echo " + std::to_string(i);
cmd.string_arg(cmd_str);
#else
vcpkg::Command cmd("echo");
const auto cmd_str = std::string(i, 'a');
cmd.string_arg(cmd_str);
#endif
vec.emplace_back(std::move(cmd));
}

auto res = vcpkg::cmd_execute_and_capture_output_parallel(vcpkg::View<vcpkg::Command>(vec));

for (size_t i = 0; i < res.size(); ++i)
{
auto out = res[i].get();
REQUIRE(out != nullptr);
REQUIRE(out->exit_code == 0);

#if defined(_WIN32)
REQUIRE(out->output == (std::to_string(i) + "\r\n"));
#else
REQUIRE(out->output == (std::string(i, 'a') + "\n"));
#endif
}
}
16 changes: 0 additions & 16 deletions src/vcpkg/archives.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,22 +346,6 @@ namespace vcpkg
{
auto results =
cmd_execute_and_capture_output_parallel(jobs, default_working_directory, get_clean_environment());
#ifdef __APPLE__
size_t i = 0;
for (auto& maybe_result : results)
{
if (const auto result = maybe_result.get())
{
if (result->exit_code == 127 && result->output.empty())
{
Debug::print(jobs[i].command_line(), ": pclose returned 127, try again \n");
maybe_result =
cmd_execute_and_capture_output(jobs[i], default_working_directory, get_clean_environment());
}
}
++i;
}
#endif

std::vector<ExpectedL<Unit>> filtered_results;
filtered_results.reserve(jobs.size());
Expand Down
1 change: 1 addition & 0 deletions src/vcpkg/base/messages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@ namespace vcpkg
REGISTER_MESSAGE(EmptyArg);
REGISTER_MESSAGE(EmptyLicenseExpression);
REGISTER_MESSAGE(EndOfStringInCodeUnit);
REGISTER_MESSAGE(EnvInvalidMaxConcurrency);
REGISTER_MESSAGE(EnvStrFailedToExtract);
REGISTER_MESSAGE(ErrorDetectingCompilerInfo);
REGISTER_MESSAGE(ErrorIndividualPackagesUnsupported);
Expand Down
26 changes: 22 additions & 4 deletions src/vcpkg/base/system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -468,17 +468,35 @@ namespace vcpkg
return ProgramW6432;
}

int get_concurrency()
unsigned int get_concurrency()
{
static int concurrency = [] {
static unsigned int concurrency = [] {
auto user_defined_concurrency = get_environment_variable("VCPKG_MAX_CONCURRENCY");
if (user_defined_concurrency)
{
return std::stoi(user_defined_concurrency.value_or_exit(VCPKG_LINE_INFO));
int res = -1;
try
{
res = std::stoi(user_defined_concurrency.value_or_exit(VCPKG_LINE_INFO));
}
catch (std::exception&)
{
Checks::msg_exit_with_message(
VCPKG_LINE_INFO, msgOptionMustBeInteger, msg::option = "VCPKG_MAX_CONCURRENCY");
}

if (!(res > 0))
{
Checks::msg_exit_with_message(VCPKG_LINE_INFO,
msgEnvInvalidMaxConcurrency,
msg::env_var = "VCPKG_MAX_CONCURRENCY",
msg::value = res);
}
return static_cast<unsigned int>(res);
}
else
{
return static_cast<int>(std::thread::hardware_concurrency()) + 1;
return std::thread::hardware_concurrency() + 1;
}
}();

Expand Down
37 changes: 31 additions & 6 deletions src/vcpkg/base/system.process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -466,8 +466,8 @@ namespace vcpkg
const WorkingDirectory& wd,
const Environment& env)
{
std::vector<ExpectedL<ExitCodeAndOutput>> res(cmd_lines.size(), LocalizedString());
if (cmd_lines.size() == 0)
std::vector<ExpectedL<ExitCodeAndOutput>> res(cmd_lines.size(), LocalizedString{});
if (cmd_lines.empty())
{
return res;
}
Expand All @@ -480,7 +480,7 @@ namespace vcpkg

std::atomic<size_t> work_item{0};
const auto num_threads =
static_cast<size_t>(std::max(1, std::min(get_concurrency(), static_cast<int>(cmd_lines.size()))));
std::max(static_cast<size_t>(1), std::min(static_cast<size_t>(get_concurrency()), cmd_lines.size()));

auto work = [&]() {
std::size_t item;
Expand All @@ -491,6 +491,7 @@ namespace vcpkg
};

std::vector<std::future<void>> workers;
workers.reserve(num_threads - 1);
for (size_t x = 0; x < num_threads - 1; ++x)
{
workers.emplace_back(std::async(std::launch::async | std::launch::deferred, work));
Expand Down Expand Up @@ -901,7 +902,7 @@ namespace vcpkg
return output.wait_and_stream_output(data_cb, encoding);
});
g_ctrl_c_state.transition_from_spawn_process();
#else // ^^^ _WIN32 // !_WIN32 vvv
#else // ^^^ _WIN32 // !_WIN32 vvv
Checks::check_exit(VCPKG_LINE_INFO, encoding == Encoding::Utf8);

std::string actual_cmd_line;
Expand All @@ -924,7 +925,23 @@ namespace vcpkg
// Flush stdout before launching external process
fflush(stdout);

const auto pipe = popen(actual_cmd_line.c_str(), "r");
FILE* pipe = nullptr;
#if defined(__APPLE__)
static std::mutex mtx;
#endif

// Scope for lock guard
{
#if defined(__APPLE__)
// `popen` sometimes returns 127 on OSX when executed in parallel.
// Related: https://github.com/microsoft/vcpkg-tool/pull/695#discussion_r973364608

std::lock_guard guard(mtx);
#endif

pipe = popen(actual_cmd_line.c_str(), "r");
}

if (pipe == nullptr)
{
return format_system_error_message("popen", errno);
Expand All @@ -942,7 +959,15 @@ namespace vcpkg
return format_system_error_message("feof", errno);
}

int ec = pclose(pipe);
int ec;
// Scope for lock guard
{
#if defined(__APPLE__)
// See the comment above at the call to `popen`.
std::lock_guard guard(mtx);
#endif
ec = pclose(pipe);
}
if (WIFEXITED(ec))
{
ec = WEXITSTATUS(ec);
Expand Down
2 changes: 1 addition & 1 deletion src/vcpkg/commands.dependinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ namespace vcpkg::Commands::DependInfo
std::string value = iter->second;
try
{
return std::stoi(value);
return std::max(std::stoi(value), NO_RECURSE_LIMIT_VALUE);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who says a user puts only numbers >= -1 in here?

}
catch (std::exception&)
{
Expand Down