You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

236 lines
5.0 KiB

#include <chrono>
#include <iostream>
#include <thread>
#include <utility>
#include <cm3p/uv.h>
#include "cmUVHandlePtr.h"
static void signal_reset_fn(uv_async_t* handle)
{
auto ptr = static_cast<cm::uv_async_ptr*>(handle->data);
ptr->reset();
}
// A common pattern is to use an async signal to shutdown the server.
static bool testAsyncShutdown()
{
uv_loop_t Loop;
auto err = uv_loop_init(&Loop);
if (err != 0) {
std::cerr << "Could not init loop" << std::endl;
return false;
}
{
cm::uv_async_ptr signal;
signal.init(Loop, &signal_reset_fn, &signal);
std::thread([&] {
std::this_thread::sleep_for(std::chrono::seconds(2));
signal.send();
}).detach();
if (uv_run(&Loop, UV_RUN_DEFAULT) != 0) {
std::cerr << "Unclean exit state in testAsyncDtor" << std::endl;
return false;
}
if (signal) {
std::cerr << "Loop exited with signal not being cleaned up" << std::endl;
return false;
}
}
uv_loop_close(&Loop);
return true;
}
static void signal_fn(uv_async_t*)
{
}
// Async dtor is sort of a pain; since it locks a mutex we must be sure its
// dtor always calls reset otherwise the mutex is deleted then locked.
static bool testAsyncDtor()
{
uv_loop_t Loop;
auto err = uv_loop_init(&Loop);
if (err != 0) {
std::cerr << "Could not init loop" << std::endl;
return false;
}
{
cm::uv_async_ptr signal;
signal.init(Loop, signal_fn);
}
if (uv_run(&Loop, UV_RUN_DEFAULT) != 0) {
std::cerr << "Unclean exit state in testAsyncDtor" << std::endl;
return false;
}
uv_loop_close(&Loop);
return true;
}
// Async needs a relatively stateful deleter; make sure that is properly
// accounted for and doesn't try to hold on to invalid state when it is
// moved
static bool testAsyncMove()
{
uv_loop_t Loop;
auto err = uv_loop_init(&Loop);
if (err != 0) {
std::cerr << "Could not init loop" << std::endl;
return false;
}
{
cm::uv_async_ptr signal;
{
cm::uv_async_ptr signalTmp;
signalTmp.init(Loop, signal_fn);
signal = std::move(signalTmp);
}
}
if (uv_run(&Loop, UV_RUN_DEFAULT) != 0) {
std::cerr << "Unclean exit state in testAsyncDtor" << std::endl;
return false;
}
uv_loop_close(&Loop);
return true;
}
// When a type is castable to another uv type (pipe -> stream) here,
// and the deleter is convertible as well, we should allow moves from
// one type to the other.
static bool testCrossAssignment()
{
uv_loop_t Loop;
auto err = uv_loop_init(&Loop);
if (err != 0) {
std::cerr << "Could not init loop" << std::endl;
return false;
}
{
cm::uv_pipe_ptr pipe;
pipe.init(Loop, 0);
cm::uv_stream_ptr stream = std::move(pipe);
if (pipe) {
std::cerr << "Move should be sure to invalidate the previous ptr"
<< std::endl;
return false;
}
cm::uv_handle_ptr handle = std::move(stream);
if (stream) {
std::cerr << "Move should be sure to invalidate the previous ptr"
<< std::endl;
return false;
}
}
if (uv_run(&Loop, UV_RUN_DEFAULT) != 0) {
std::cerr << "Unclean exit state in testCrossAssignment" << std::endl;
return false;
}
uv_loop_close(&Loop);
return true;
}
// This test can't fail at run time; but this makes sure we have all our move
// ctors created correctly.
static bool testAllMoves()
{
using namespace cm;
struct allTypes
{
uv_stream_ptr _7;
uv_timer_ptr _8;
uv_tty_ptr _9;
uv_process_ptr _11;
uv_pipe_ptr _12;
uv_async_ptr _13;
uv_signal_ptr _14;
uv_handle_ptr _15;
uv_idle_ptr _16;
};
allTypes a;
allTypes b(std::move(a));
allTypes c = std::move(b);
return true;
}
static bool testLoopReset()
{
bool closed = false;
cm::uv_loop_ptr loop;
loop.init();
uv_timer_t timer;
uv_timer_init(loop, &timer);
timer.data = &closed;
uv_close(reinterpret_cast<uv_handle_t*>(&timer), [](uv_handle_t* handle) {
auto closedPtr = static_cast<bool*>(handle->data);
*closedPtr = true;
});
loop.reset();
if (!closed) {
std::cerr << "uv_loop_ptr did not finish" << std::endl;
return false;
}
return true;
}
static bool testLoopDestructor()
{
bool closed = false;
uv_timer_t timer;
{
cm::uv_loop_ptr loop;
loop.init();
uv_timer_init(loop, &timer);
timer.data = &closed;
uv_close(reinterpret_cast<uv_handle_t*>(&timer), [](uv_handle_t* handle) {
auto closedPtr = static_cast<bool*>(handle->data);
*closedPtr = true;
});
}
if (!closed) {
std::cerr << "uv_loop_ptr did not finish" << std::endl;
return false;
}
return true;
}
int testUVRAII(int, char** const)
{
if (!testAsyncShutdown()) {
return -1;
}
bool passed = true;
passed = testAsyncDtor() && passed;
passed = testAsyncMove() && passed;
passed = testCrossAssignment() && passed;
passed = testAllMoves() && passed;
passed = testLoopReset() && passed;
passed = testLoopDestructor() && passed;
return passed ? 0 : -1;
}