1 /*
2   Copyright (c) DataStax, Inc.
3 
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7 
8   http://www.apache.org/licenses/LICENSE-2.0
9 
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15 */
16 
17 #ifndef DATASTAX_INTERNAL_LOOP_WATCHER_HPP
18 #define DATASTAX_INTERNAL_LOOP_WATCHER_HPP
19 
20 #include "allocated.hpp"
21 #include "callback.hpp"
22 #include "macros.hpp"
23 
24 #include <uv.h>
25 
26 namespace datastax { namespace internal { namespace core {
27 
28 template <class Type, class HType>
29 class LoopWatcher {
30 public:
31   typedef internal::Callback<void, Type*> Callback;
32   typedef HType HandleType;
33 
LoopWatcher()34   LoopWatcher()
35       : handle_(NULL)
36       , state_(CLOSED) {}
37 
~LoopWatcher()38   ~LoopWatcher() { close_handle(); }
39 
40   /**
41    * Start the handle.
42    *
43    * @param loop The event loop that will process the handle.
44    * @param callback A callback that handles events.
45    */
start(uv_loop_t * loop,const Callback & callback)46   int start(uv_loop_t* loop, const Callback& callback) {
47     int rc = 0;
48     if (handle_ == NULL) {
49       handle_ = new AllocatedT<HandleType>();
50       handle_->loop = NULL;
51       handle_->data = this;
52     }
53     if (state_ == CLOSED) {
54       rc = Type::init_handle(loop, handle_);
55       if (rc != 0) return rc;
56       state_ = STOPPED;
57     }
58     if (state_ == STOPPED) {
59       rc = Type::start_handle(handle_, on_run);
60       if (rc != 0) return rc;
61       state_ = STARTED;
62     }
63     callback_ = callback;
64     return 0;
65   }
66 
67   /**
68    * Stop the handle.
69    */
stop()70   void stop() {
71     if (state_ == STARTED) {
72       state_ = STOPPED;
73       Type::stop_handle(handle_);
74     }
75   }
76 
77   /**
78    * Close the handle.
79    */
close_handle()80   void close_handle() {
81     if (handle_ != NULL) {
82       if (state_ == CLOSED) { // The handle was allocated, but initialization failed.
83         delete handle_;
84       } else { // If initialized or started then close the handle properly.
85         uv_close(reinterpret_cast<uv_handle_t*>(handle_), on_close);
86       }
87       state_ = CLOSED;
88       handle_ = NULL;
89     }
90   }
91 
92 public:
is_running() const93   bool is_running() const { return state_ == STARTED; }
loop()94   uv_loop_t* loop() { return handle_ ? handle_->loop : NULL; }
95 
96 private:
on_run(HandleType * handle)97   static void on_run(HandleType* handle) {
98     Type* watcher = static_cast<Type*>(handle->data);
99     watcher->callback_(watcher);
100   }
101 
on_close(uv_handle_t * handle)102   static void on_close(uv_handle_t* handle) {
103     delete reinterpret_cast<AllocatedT<HandleType>*>(handle);
104   }
105 
106 private:
107   enum State { CLOSED, STOPPED, STARTED };
108 
109 private:
110   AllocatedT<HandleType>* handle_;
111   State state_;
112   Callback callback_;
113 
114 private:
115   DISALLOW_COPY_AND_ASSIGN(LoopWatcher);
116 };
117 
118 /**
119  * A wrapper for uv_prepare. This is useful for running a callback right before
120  * the event loop begins polling.
121  */
122 class Prepare : public LoopWatcher<Prepare, uv_prepare_t> {
123 private:
124   typedef uv_prepare_cb HandleCallback;
125   friend class LoopWatcher<Prepare, HandleType>;
126 
init_handle(uv_loop_t * loop,HandleType * handle)127   static int init_handle(uv_loop_t* loop, HandleType* handle) {
128     return uv_prepare_init(loop, handle);
129   }
130 
start_handle(HandleType * handle,HandleCallback callback)131   static int start_handle(HandleType* handle, HandleCallback callback) {
132     return uv_prepare_start(handle, callback);
133   }
134 
stop_handle(HandleType * handle)135   static void stop_handle(HandleType* handle) { uv_prepare_stop(handle); }
136 };
137 
138 /**
139  * A wrapper for uv_check. This is useful for running a callback right after
140  * the event loop returns from polling.
141  */
142 class Check : public LoopWatcher<Check, uv_check_t> {
143 private:
144   typedef uv_check_cb HandleCallback;
145   friend class LoopWatcher<Check, HandleType>;
146 
init_handle(uv_loop_t * loop,HandleType * handle)147   static int init_handle(uv_loop_t* loop, HandleType* handle) {
148     return uv_check_init(loop, handle);
149   }
150 
start_handle(HandleType * handle,HandleCallback callback)151   static int start_handle(HandleType* handle, HandleCallback callback) {
152     return uv_check_start(handle, callback);
153   }
154 
stop_handle(HandleType * handle)155   static void stop_handle(HandleType* handle) { uv_check_stop(handle); }
156 };
157 
158 }}} // namespace datastax::internal::core
159 
160 #endif
161