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