1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
|
#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.get()) {
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.get()) {
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.get()) {
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;
};
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;
}
|