mirror of
https://github.com/Kitware/CMake.git
synced 2026-07-08 07:37:33 +00:00
cmUVHandlePtr: Add uv_write wrapper to manage request lifetime
Provide a way to synchronously cancel a write request callback.
This commit is contained in:
@@ -6,6 +6,9 @@
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
#include <cm/memory>
|
||||
|
||||
#include <cm3p/uv.h>
|
||||
|
||||
@@ -306,4 +309,38 @@ UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(async)
|
||||
|
||||
UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(tty)
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
struct write_req : public uv_write_t
|
||||
{
|
||||
std::weak_ptr<std::function<void(int)>> cb_;
|
||||
write_req(std::weak_ptr<std::function<void(int)>> wcb)
|
||||
: cb_(std::move(wcb))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
void write_req_cb(uv_write_t* req, int status)
|
||||
{
|
||||
// Ownership has been transferred from the event loop.
|
||||
std::unique_ptr<write_req> self(static_cast<write_req*>(req));
|
||||
|
||||
// Notify the original uv_write caller if it is still interested.
|
||||
if (auto cb = self->cb_.lock()) {
|
||||
(*cb)(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int uv_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs,
|
||||
std::weak_ptr<std::function<void(int)>> cb)
|
||||
{
|
||||
auto req = cm::make_unique<write_req>(std::move(cb));
|
||||
int status = uv_write(req.get(), handle, bufs, nbufs, write_req_cb);
|
||||
if (status == 0) {
|
||||
// Ownership has been transferred to the event loop.
|
||||
static_cast<void>(req.release());
|
||||
}
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
@@ -286,4 +287,27 @@ UV_HANDLE_PTR_INSTANTIATE_EXTERN(tty)
|
||||
# undef UV_HANDLE_PTR_INSTANTIATE_EXTERN
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Wraps uv_write to add synchronous cancellation.
|
||||
*
|
||||
* libuv provides no way to synchronously cancel a write request.
|
||||
* Closing a write handle will cancel its pending write request, but its
|
||||
* callback will still be called asynchronously later with UV_ECANCELED.
|
||||
*
|
||||
* This wrapper provides a solution by handing ownership of the uv_write_t
|
||||
* request object to the event loop and taking it back in the callback.
|
||||
* Use this in combination with uv_loop_ptr to ensure the event loop
|
||||
* runs to completion and cleans up all resources.
|
||||
*
|
||||
* The caller may optionally provide a callback it owns with std::shared_ptr.
|
||||
* If the caller's lifetime ends before the write request completes, the
|
||||
* callback can be safely deleted and will not be called.
|
||||
*
|
||||
* The bufs array does not need to live beyond this call, but the memory
|
||||
* referenced by the uv_buf_t values must remain alive until the callback
|
||||
* is made or the stream is closed.
|
||||
*/
|
||||
int uv_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs,
|
||||
std::weak_ptr<std::function<void(int)>> cb);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include <cm3p/uv.h>
|
||||
|
||||
#include "cmGetPipes.h"
|
||||
#include "cmUVHandlePtr.h"
|
||||
|
||||
static bool testIdle()
|
||||
@@ -77,10 +80,58 @@ static bool testTimer()
|
||||
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 = testIdle() && passed;
|
||||
passed = testTimer() && passed;
|
||||
passed = testWriteCallback() && passed;
|
||||
return passed ? 0 : -1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user