mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-08 06:40:48 -06:00
Since commit 5420639a8d (cmExecuteProcessCommand: Replace cmsysProcess
with cmUVProcessChain, 2023-06-01, v3.28.0-rc1~138^2~8) we've
occasionally observed immediate timeouts on processes that take longer
than the timeout to start, even though we only start the timer after the
child processes start. The problem is that:
* `uv_loop_init` initializes `uv_loop_t`'s cached "now" time.
* Starting processes takes time but does not update the "now" time.
* `uv_timer_start` computes expiry relative the cached "now" time,
so short timers may be expired as soon as they are started.
* `uv_run` invokes expired timer callbacks before polling for I/O
or process completion, so we "timeout" immediately.
Fix this by updating the cached "now" time via `uv_update_time` just
before starting timers. This is needed only for timers that start
before the `uv_run` event loop. Update our `uv_timer_ptr` wrapper
to make all callers consider the choice when calling `start()`.
154 lines
3.2 KiB
C++
154 lines
3.2 KiB
C++
#include <functional>
|
|
#include <iostream>
|
|
#include <memory>
|
|
|
|
#include <cm3p/uv.h>
|
|
|
|
#include "cmGetPipes.h"
|
|
#include "cmUVHandlePtr.h"
|
|
|
|
static bool testBool()
|
|
{
|
|
cm::uv_async_ptr async;
|
|
cm::uv_handle_ptr handle;
|
|
cm::uv_idle_ptr idle;
|
|
cm::uv_pipe_ptr pipe;
|
|
cm::uv_process_ptr process;
|
|
cm::uv_signal_ptr signal;
|
|
cm::uv_stream_ptr stream;
|
|
cm::uv_timer_ptr timer;
|
|
cm::uv_tty_ptr tty;
|
|
return !async && !handle && !idle && !pipe && !process && !signal &&
|
|
!stream && !timer && !tty;
|
|
}
|
|
|
|
static bool testIdle()
|
|
{
|
|
bool idled = false;
|
|
|
|
cm::uv_loop_ptr loop;
|
|
loop.init();
|
|
|
|
auto cb = [](uv_idle_t* handle) {
|
|
auto idledPtr = static_cast<bool*>(handle->data);
|
|
*idledPtr = true;
|
|
uv_idle_stop(handle);
|
|
};
|
|
|
|
cm::uv_idle_ptr idle;
|
|
idle.init(*loop, &idled);
|
|
idle.start(cb);
|
|
uv_run(loop, UV_RUN_DEFAULT);
|
|
|
|
if (!idled) {
|
|
std::cerr << "uv_idle_ptr did not trigger callback" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
idled = false;
|
|
|
|
idle.start(cb);
|
|
idle.stop();
|
|
uv_run(loop, UV_RUN_DEFAULT);
|
|
|
|
if (idled) {
|
|
std::cerr << "uv_idle_ptr::stop did not stop callback" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool testTimer()
|
|
{
|
|
bool timed = false;
|
|
|
|
cm::uv_loop_ptr loop;
|
|
loop.init();
|
|
|
|
auto cb = [](uv_timer_t* handle) {
|
|
auto timedPtr = static_cast<bool*>(handle->data);
|
|
*timedPtr = true;
|
|
uv_timer_stop(handle);
|
|
};
|
|
|
|
cm::uv_timer_ptr timer;
|
|
timer.init(*loop, &timed);
|
|
timer.start(cb, 10, 0, cm::uv_update_time::no);
|
|
uv_run(loop, UV_RUN_DEFAULT);
|
|
|
|
if (!timed) {
|
|
std::cerr << "uv_timer_ptr did not trigger callback" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
timed = false;
|
|
timer.start(cb, 10, 0, cm::uv_update_time::no);
|
|
timer.stop();
|
|
uv_run(loop, UV_RUN_DEFAULT);
|
|
|
|
if (timed) {
|
|
std::cerr << "uv_timer_ptr::stop did not stop callback" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool testWriteCallback()
|
|
{
|
|
int pipe[] = { -1, -1 };
|
|
if (cmGetPipes(pipe) < 0) {
|
|
std::cout << "cmGetPipes() returned an error" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
cm::uv_loop_ptr loop;
|
|
loop.init();
|
|
|
|
cm::uv_pipe_ptr pipeRead;
|
|
pipeRead.init(*loop, 0);
|
|
uv_pipe_open(pipeRead, pipe[0]);
|
|
|
|
cm::uv_pipe_ptr pipeWrite;
|
|
pipeWrite.init(*loop, 0);
|
|
uv_pipe_open(pipeWrite, pipe[1]);
|
|
|
|
char c = '.';
|
|
uv_buf_t buf = uv_buf_init(&c, sizeof(c));
|
|
int status = -1;
|
|
auto cb = std::make_shared<std::function<void(int)>>(
|
|
[&status](int s) { status = s; });
|
|
|
|
// Test getting a callback after the write is done.
|
|
cm::uv_write(pipeWrite, &buf, 1, cb);
|
|
uv_run(loop, UV_RUN_DEFAULT);
|
|
if (status != 0) {
|
|
std::cout << "cm::uv_write non-zero status: " << status << std::endl;
|
|
return false;
|
|
}
|
|
|
|
// Test deleting the callback before it is made.
|
|
status = -1;
|
|
cm::uv_write(pipeWrite, &buf, 1, cb);
|
|
cb.reset();
|
|
uv_run(loop, UV_RUN_DEFAULT);
|
|
if (status != -1) {
|
|
std::cout << "cm::uv_write callback incorrectly called with status: "
|
|
<< status << std::endl;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int testUVHandlePtr(int, char** const)
|
|
{
|
|
bool passed = true;
|
|
passed = testBool() && passed;
|
|
passed = testIdle() && passed;
|
|
passed = testTimer() && passed;
|
|
passed = testWriteCallback() && passed;
|
|
return passed ? 0 : -1;
|
|
}
|