1 #ifndef UV_HELPER__HPP
2 #define UV_HELPER__HPP
3
4 /* $Id: UvHelper.hpp 598932 2019-12-17 14:21:36Z satskyse $
5 * ===========================================================================
6 *
7 * PUBLIC DOMAIN NOTICE
8 * National Center for Biotechnology Information
9 *
10 * This software/database is a "United States Government Work" under the
11 * terms of the United States Copyright Act. It was written as part of
12 * the author's official duties as a United States Government employee and
13 * thus cannot be copyrighted. This software/database is freely available
14 * to the public for use. The National Library of Medicine and the U.S.
15 * Government have not placed any restriction on its use or reproduction.
16 *
17 * Although all reasonable efforts have been taken to ensure the accuracy
18 * and reliability of the software and data, the NLM and the U.S.
19 * Government do not and cannot warrant the performance or results that
20 * may be obtained by using this software or data. The NLM and the U.S.
21 * Government disclaim all warranties, express or implied, including
22 * warranties of performance, merchantability or fitness for any particular
23 * purpose.
24 *
25 * Please cite the author in any work or product based on this material.
26 *
27 * ===========================================================================
28 *
29 * Authors: Dmitri Dmitrienko
30 *
31 * File Description:
32 *
33 */
34
35 #include <stdexcept>
36 #include <sstream>
37
38 #include <corelib/ncbimtx.hpp>
39 USING_NCBI_SCOPE;
40
41 #include "uv.h"
42
43 #include "pubseq_gateway_logging.hpp"
44
45 class CUvBaseException : public CException
46 {
47 public:
48 enum EErrCode {
49 eSignalAlreadyStarted,
50 eSignalNotStarted,
51 eTcpHandleClosed
52 };
53
54 const char* GetErrCodeString(void) const override;
55
56 NCBI_EXCEPTION_DEFAULT(CUvBaseException, CException);
57 };
58
59 class CUvException : public CUvBaseException
60 {
61 public:
62 enum EErrCode {
63 eUvLoopInitFailure,
64 eUvSignalInitFailure,
65 eUvSignalStartFailure,
66 eUvIp4AddrFailure,
67 eUvTcpInitFailure,
68 eUvTcpBindFailure,
69 eUvSemInitFailure
70 };
71
72 CUvException(const CDiagCompileInfo& info,
73 const CException* prev_exception,
74 EErrCode err_code,
75 const string& message,
76 int uv_err_code,
77 EDiagSev severity = eDiag_Error) :
78 CUvBaseException(info, prev_exception, (CUvBaseException::EErrCode) err_code,
79 message + ": " + uv_strerror(uv_err_code), severity),
80 m_UvErrCode(uv_err_code)
81 NCBI_EXCEPTION_DEFAULT_IMPLEMENTATION(CUvException, CUvBaseException);
82
83 const char* GetErrCodeString() const override;
84
85 private:
86 int m_UvErrCode;
87 };
88
89 class CUvLoop {
90 private:
91 uv_loop_t m_loop;
92 bool m_initialized;
s_walk(uv_handle_t * handle,void * arg)93 static void s_walk(uv_handle_t* handle, void* arg) {
94 std::function<void(uv_handle_t* handle)> *cb = static_cast<std::function<void(uv_handle_t* handle)>*>(arg);
95 (*cb)(handle);
96 }
97 public:
CUvLoop()98 CUvLoop() :
99 m_loop({0}),
100 m_initialized(false)
101 {
102 PSG_TRACE("CUvLoop::CUvLoop " << &m_loop);
103 int rc = uv_loop_init(&m_loop);
104 if (rc)
105 NCBI_THROW2(CUvException, eUvLoopInitFailure, "uv_loop_init failed", rc);
106 m_initialized = true;
107 }
~CUvLoop()108 ~CUvLoop() {
109 PSG_TRACE("CUvLoop::~CUvLoop " << &m_loop);
110 Close();
111 }
Handle()112 uv_loop_t* Handle() {
113 return &m_loop;
114 }
Close()115 int Close() {
116 PSG_TRACE("CUvLoop::Close " << &m_loop);
117 int rc = 0;
118 if (m_initialized) {
119 rc = uv_run(&m_loop, UV_RUN_DEFAULT);
120 if (rc)
121 PSG_TRACE("uv_run returned " << rc);
122 rc = uv_loop_close(&m_loop);
123 if (rc)
124 PSG_TRACE("uv_loop_close returned " << rc);
125 m_initialized = false;
126 }
127 return rc;
128 }
Stop()129 void Stop() {
130 if (m_initialized) {
131 PSG_TRACE("CUvLoop::Stop " << &m_loop);
132 uv_stop(&m_loop);
133 }
134 }
Walk(std::function<void (uv_handle_t * handle)> cb)135 void Walk(std::function<void(uv_handle_t* handle)> cb) {
136 if (m_initialized) {
137 uv_walk(&m_loop, s_walk, &cb);
138 }
139 }
140 };
141
142 class CUvSignal {
143 private:
144 uv_signal_t m_sig;
145 bool m_initialized;
146 bool m_started;
147 public:
CUvSignal(uv_loop_t * loop)148 CUvSignal(uv_loop_t *loop) :
149 m_sig({0}),
150 m_initialized(false),
151 m_started(false)
152 {
153 int rc;
154 rc = uv_signal_init(loop, &m_sig);
155 if (rc)
156 NCBI_THROW2(CUvException, eUvSignalInitFailure, "uv_signal_init failed", rc);
157 m_initialized = true;
158 }
~CUvSignal()159 ~CUvSignal() {
160 Close();
161 }
Start(int signum,uv_signal_cb cb)162 void Start(int signum, uv_signal_cb cb) {
163 if (m_started)
164 NCBI_THROW(CUvBaseException, eSignalAlreadyStarted, "signal has already started");
165 int rc = uv_signal_start(&m_sig, cb, signum);
166 if (rc)
167 NCBI_THROW2(CUvException, eUvSignalStartFailure, "uv_signal_start failed", rc);
168 m_started = true;
169 }
Stop()170 void Stop() {
171 if (!m_started)
172 NCBI_THROW(CUvBaseException, eSignalNotStarted, "signal hasn't started");
173 uv_signal_stop(&m_sig);
174 m_started = false;
175 }
Close()176 void Close() {
177 if (m_initialized) {
178 if (m_started) {
179 uv_signal_stop(&m_sig);
180 m_started = false;
181 }
182 uv_close(reinterpret_cast<uv_handle_t*>(&m_sig), NULL);
183 m_initialized = false;
184 }
185 }
186 };
187
188 class CUvTcp {
189 private:
190 uv_tcp_t m_tcp;
191 bool m_initialized;
s_close_cb(uv_handle_t * handle)192 static void s_close_cb(uv_handle_t *handle) {
193 CUvTcp *self = static_cast<CUvTcp*>(handle->data);
194 self->m_initialized = false;
195 }
InternalClose(void (* close_cb)(uv_handle_t * handle),bool from_dtor)196 void InternalClose(void (*close_cb)(uv_handle_t* handle), bool from_dtor) {
197 if (m_initialized) {
198 PSG_TRACE("CUvTcp::Close " << &m_tcp);
199 uv_handle_t *handle = reinterpret_cast<uv_handle_t*>(&m_tcp);
200 if (from_dtor) {
201 handle->data = this;
202 uv_close(handle, s_close_cb);
203 while (m_initialized)
204 uv_run(handle->loop, UV_RUN_ONCE);
205 }
206 else {
207 m_initialized = false;
208 uv_close(handle, close_cb);
209 }
210 }
211 }
212 public:
CUvTcp(uv_loop_t * loop)213 CUvTcp(uv_loop_t *loop) :
214 m_tcp({0}),
215 m_initialized(false)
216 {
217 PSG_TRACE("CUvTcp::CUvTcp " << &m_tcp);
218 Init(loop);
219 }
~CUvTcp()220 ~CUvTcp() {
221 PSG_TRACE("CUvTcp::~CUvTcp " << &m_tcp);
222 InternalClose(nullptr, true);
223 }
Init(uv_loop_t * loop)224 void Init(uv_loop_t *loop) {
225 if (!m_initialized) {
226 int rc;
227 rc = uv_tcp_init(loop, &m_tcp);
228 if (rc)
229 NCBI_THROW2(CUvException, eUvTcpInitFailure, "uv_tcp_init failed", rc);
230 m_initialized = true;
231 }
232 }
Initialized() const233 bool Initialized() const {
234 return m_initialized;
235 }
NoDelay(bool set)236 void NoDelay(bool set) {
237 if (m_initialized) {
238 uv_tcp_nodelay(&m_tcp, set ? 1 : 0);
239 }
240 }
KeepAlive(bool set)241 void KeepAlive(bool set) {
242 if (m_initialized) {
243 uv_tcp_keepalive(&m_tcp, set ? 1 : 0, 120);
244 }
245 }
Handle()246 uv_tcp_t* Handle() {
247 if (!m_initialized)
248 NCBI_THROW(CUvBaseException, eTcpHandleClosed, "tcp handle is closed");
249 return &m_tcp;
250 }
Bind(const char * addr,unsigned int port)251 void Bind(const char* addr, unsigned int port) {
252 int e;
253 std::stringstream ss;
254 struct sockaddr_in addr_in;
255
256 e = uv_ip4_addr(addr, port, &addr_in);
257
258 if (e != 0) {
259 ss << (e == EINVAL ? "invalid" : "failed to parse") << " address/port: " << addr << ':' << port;
260 NCBI_THROW2(CUvException, eUvIp4AddrFailure, ss.str(), e);
261 }
262
263 e = uv_tcp_bind(&m_tcp, reinterpret_cast<struct sockaddr*>(&addr_in), 0);
264 if (e != 0 || errno == EADDRINUSE) {
265 ss << "failed to bind socket to address/port: " << addr << ':' << port;
266 NCBI_THROW2(CUvException, eUvTcpBindFailure, ss.str(), e);
267 }
268 }
Close(void (* close_cb)(uv_handle_t * handle))269 void Close(void (*close_cb)(uv_handle_t* handle)) {
270 InternalClose(close_cb, false);
271 }
StopRead()272 void StopRead() {
273 if (m_initialized) {
274 PSG_TRACE("CUvTcp::StopRead " << &m_tcp);
275 uv_read_stop(reinterpret_cast<uv_stream_t*>(&m_tcp));
276 }
277 }
278 };
279
GetErrCodeString(void) const280 inline const char* CUvBaseException::GetErrCodeString(void) const
281 {
282 switch (GetErrCode())
283 {
284 case eSignalAlreadyStarted: return "eSignalAlreadyStarted";
285 case eSignalNotStarted: return "eSignalNotStarted";
286 case eTcpHandleClosed: return "eTcpHandleClosed";
287 default: return CException::GetErrCodeString();
288 }
289 }
290
GetErrCodeString(void) const291 inline const char* CUvException::GetErrCodeString(void) const
292 {
293 switch (GetErrCode()) {
294 case eUvLoopInitFailure: return "eUvLoopInitFailure";
295 case eUvSignalInitFailure: return "eUvSignalInitFailure";
296 case eUvSignalStartFailure: return "eUvSignalStartFailure";
297 case eUvIp4AddrFailure: return "eUvIp4AddrFailure";
298 case eUvTcpInitFailure: return "eUvTcpInitFailure";
299 case eUvTcpBindFailure: return "eUvTcpBindFailure";
300 case eUvSemInitFailure: return "eUvSemInitFailure";
301 default: return CException::GetErrCodeString();
302 }
303 }
304
305 #endif
306