Commit 9abc01fb5047b8e2f8b45c913b94008deef662e9
1 parent
589eb3fc
Use cmd.block() in command_blocking
Also make cmd.reply() copy the value and use a mutex to make it thread safe. No noticable speed hits, probably thanks to RVO.
Showing
4 changed files
with
29 additions
and
40 deletions
examples/speed_test_async.cpp
| @@ -57,7 +57,6 @@ int main(int argc, char* argv[]) { | @@ -57,7 +57,6 @@ int main(int argc, char* argv[]) { | ||
| 57 | 57 | ||
| 58 | long final_count = stol(rdx.get("simple_loop:count")); | 58 | long final_count = stol(rdx.get("simple_loop:count")); |
| 59 | 59 | ||
| 60 | - | ||
| 61 | double t_elapsed = time_s() - t0; | 60 | double t_elapsed = time_s() - t0; |
| 62 | double actual_freq = (double)count / t_elapsed; | 61 | double actual_freq = (double)count / t_elapsed; |
| 63 | 62 |
src/command.cpp
| @@ -26,14 +26,10 @@ Command<ReplyT>::Command( | @@ -26,14 +26,10 @@ Command<ReplyT>::Command( | ||
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | template<class ReplyT> | 28 | template<class ReplyT> |
| 29 | -Command<ReplyT>& Command<ReplyT>::block() { | ||
| 30 | - std::unique_lock<std::mutex> lk(blocker_lock_); | ||
| 31 | - blocker_.wait(lk, [this]() { | ||
| 32 | - logger_.info() << "checking blocker: " << blocking_done_; | ||
| 33 | - return blocking_done_.load(); }); | ||
| 34 | - logger_.info() << "returning from block"; | ||
| 35 | - blocking_done_ = {false}; | ||
| 36 | - return *this; | 29 | +void Command<ReplyT>::wait() { |
| 30 | + std::unique_lock<std::mutex> lk(waiter_lock_); | ||
| 31 | + waiter_.wait(lk, [this]() { return waiting_done_.load(); }); | ||
| 32 | + waiting_done_ = {false}; | ||
| 37 | } | 33 | } |
| 38 | 34 | ||
| 39 | template<class ReplyT> | 35 | template<class ReplyT> |
| @@ -44,16 +40,14 @@ void Command<ReplyT>::processReply(redisReply* r) { | @@ -44,16 +40,14 @@ void Command<ReplyT>::processReply(redisReply* r) { | ||
| 44 | reply_obj_ = r; | 40 | reply_obj_ = r; |
| 45 | parseReplyObject(); | 41 | parseReplyObject(); |
| 46 | invoke(); | 42 | invoke(); |
| 47 | -// logger_.info() << "reply status " << reply_status_; | 43 | + |
| 48 | pending_--; | 44 | pending_--; |
| 49 | 45 | ||
| 50 | - blocking_done_ = true; | ||
| 51 | -// logger_.info() << "notifying blocker"; | ||
| 52 | - blocker_.notify_all(); | 46 | + waiting_done_ = true; |
| 47 | + waiter_.notify_all(); | ||
| 53 | 48 | ||
| 54 | // Allow free() method to free memory | 49 | // Allow free() method to free memory |
| 55 | if (!free_memory_) { | 50 | if (!free_memory_) { |
| 56 | -// logger.trace() << "Command memory not being freed, free_memory = " << free_memory; | ||
| 57 | free_guard_.unlock(); | 51 | free_guard_.unlock(); |
| 58 | return; | 52 | return; |
| 59 | } | 53 | } |
| @@ -105,15 +99,19 @@ void Command<ReplyT>::freeReply() { | @@ -105,15 +99,19 @@ void Command<ReplyT>::freeReply() { | ||
| 105 | template<class ReplyT> | 99 | template<class ReplyT> |
| 106 | void Command<ReplyT>::freeCommand(Command<ReplyT>* c) { | 100 | void Command<ReplyT>::freeCommand(Command<ReplyT>* c) { |
| 107 | c->rdx_->template remove_active_command<ReplyT>(c->id_); | 101 | c->rdx_->template remove_active_command<ReplyT>(c->id_); |
| 108 | -// logger.debug() << "Deleted Command " << c->id << " at " << c; | ||
| 109 | delete c; | 102 | delete c; |
| 110 | } | 103 | } |
| 111 | 104 | ||
| 112 | - | 105 | +/** |
| 106 | +* Create a copy of the reply and return it. Use a guard | ||
| 107 | +* to make sure we don't return a reply while it is being | ||
| 108 | +* modified. | ||
| 109 | +*/ | ||
| 113 | template<class ReplyT> | 110 | template<class ReplyT> |
| 114 | -const ReplyT& Command<ReplyT>::reply() const { | 111 | +ReplyT Command<ReplyT>::reply() { |
| 112 | + std::lock_guard<std::mutex> lg(free_guard_); | ||
| 115 | if (!ok()) { | 113 | if (!ok()) { |
| 116 | - logger_.warning() << cmd_ << ": Accessing value of reply with status != OK."; | 114 | + logger_.warning() << cmd_ << ": Accessing reply value while status != OK."; |
| 117 | } | 115 | } |
| 118 | return reply_val_; | 116 | return reply_val_; |
| 119 | } | 117 | } |
src/command.hpp
| @@ -57,7 +57,7 @@ public: | @@ -57,7 +57,7 @@ public: | ||
| 57 | * to block(). If it is the first call, then returns once the callback | 57 | * to block(). If it is the first call, then returns once the callback |
| 58 | * is invoked for the first time. | 58 | * is invoked for the first time. |
| 59 | */ | 59 | */ |
| 60 | - Command<ReplyT>& block(); | 60 | + void wait(); |
| 61 | 61 | ||
| 62 | /** | 62 | /** |
| 63 | * Returns true if the command has been canceled. | 63 | * Returns true if the command has been canceled. |
| @@ -77,7 +77,7 @@ public: | @@ -77,7 +77,7 @@ public: | ||
| 77 | /** | 77 | /** |
| 78 | * Returns the reply value, if the reply was successful (ok() == true). | 78 | * Returns the reply value, if the reply was successful (ok() == true). |
| 79 | */ | 79 | */ |
| 80 | - const ReplyT& reply() const; | 80 | + ReplyT reply(); |
| 81 | 81 | ||
| 82 | const std::string& cmd() const { return cmd_; }; | 82 | const std::string& cmd() const { return cmd_; }; |
| 83 | 83 | ||
| @@ -130,9 +130,8 @@ private: | @@ -130,9 +130,8 @@ private: | ||
| 130 | const std::function<void(Command<ReplyT>&)> callback_; | 130 | const std::function<void(Command<ReplyT>&)> callback_; |
| 131 | 131 | ||
| 132 | // Place to store the reply value and status. | 132 | // Place to store the reply value and status. |
| 133 | - // ONLY for blocking commands | ||
| 134 | ReplyT reply_val_; | 133 | ReplyT reply_val_; |
| 135 | - int reply_status_; | 134 | + std::atomic_int reply_status_; |
| 136 | 135 | ||
| 137 | // How many messages sent to server but not received reply | 136 | // How many messages sent to server but not received reply |
| 138 | std::atomic_int pending_ = {0}; | 137 | std::atomic_int pending_ = {0}; |
| @@ -148,13 +147,19 @@ private: | @@ -148,13 +147,19 @@ private: | ||
| 148 | std::mutex free_guard_; | 147 | std::mutex free_guard_; |
| 149 | 148 | ||
| 150 | // For synchronous use | 149 | // For synchronous use |
| 151 | - std::condition_variable blocker_; | ||
| 152 | - std::mutex blocker_lock_; | ||
| 153 | - std::atomic_bool blocking_done_ = {false}; | 150 | + std::condition_variable waiter_; |
| 151 | + std::mutex waiter_lock_; | ||
| 152 | + std::atomic_bool waiting_done_ = {false}; | ||
| 154 | 153 | ||
| 155 | // Passed on from Redox class | 154 | // Passed on from Redox class |
| 156 | log::Logger& logger_; | 155 | log::Logger& logger_; |
| 157 | 156 | ||
| 157 | + // Explicitly delete copy constructor and assignment operator, | ||
| 158 | + // Command objects should never be copied because they hold | ||
| 159 | + // state with a network resource. | ||
| 160 | + Command(const Command&) = delete; | ||
| 161 | + Command& operator=(const Command&) = delete; | ||
| 162 | + | ||
| 158 | friend class Redox; | 163 | friend class Redox; |
| 159 | }; | 164 | }; |
| 160 | 165 |
src/redox.hpp
| @@ -449,21 +449,8 @@ Command<ReplyT>& Redox::command_looping( | @@ -449,21 +449,8 @@ Command<ReplyT>& Redox::command_looping( | ||
| 449 | 449 | ||
| 450 | template<class ReplyT> | 450 | template<class ReplyT> |
| 451 | Command<ReplyT>& Redox::command_blocking(const std::string& cmd) { | 451 | Command<ReplyT>& Redox::command_blocking(const std::string& cmd) { |
| 452 | - | ||
| 453 | - std::condition_variable cv; | ||
| 454 | - std::mutex m; | ||
| 455 | - std::unique_lock<std::mutex> lk(m); | ||
| 456 | - std::atomic_bool done = {false}; | ||
| 457 | - | ||
| 458 | - Command<ReplyT>& c = createCommand<ReplyT>(cmd, | ||
| 459 | - [&cv, &done](Command<ReplyT>& cmd_obj) { | ||
| 460 | - done = true; | ||
| 461 | - cv.notify_one(); | ||
| 462 | - }, | ||
| 463 | - 0, 0, false // No repeats, don't free memory | ||
| 464 | - ); | ||
| 465 | - | ||
| 466 | - cv.wait(lk, [&done]() { return done.load(); }); | 452 | + auto& c = createCommand<ReplyT>(cmd, nullptr, 0, 0, false); |
| 453 | + c.wait(); | ||
| 467 | return c; | 454 | return c; |
| 468 | } | 455 | } |
| 469 | 456 |