Commit 589eb3fc18505a95a92a78a3cbbfd6e3474f0efe
1 parent
c43f2747
Simplified API for .command()
Split up command into command, command_blocking, and command_looping. The original command no longer returns anything, which clarifies how it should be used in most cases. command_looping and command_blocking both return a command object. Also added a waiting CV to the Command object, though it is not used in command_blocking yet.
Showing
7 changed files
with
142 additions
and
83 deletions
examples/speed_test_async.cpp
| @@ -21,7 +21,8 @@ int main(int argc, char* argv[]) { | @@ -21,7 +21,8 @@ int main(int argc, char* argv[]) { | ||
| 21 | Redox rdx = {"/var/run/redis/redis.sock", nullptr}; | 21 | Redox rdx = {"/var/run/redis/redis.sock", nullptr}; |
| 22 | if(!rdx.start()) return 1; | 22 | if(!rdx.start()) return 1; |
| 23 | 23 | ||
| 24 | - if(rdx.command_blocking("SET simple_loop:count 0")) { | 24 | + bool status = rdx.command_blocking("SET simple_loop:count 0"); |
| 25 | + if(status) { | ||
| 25 | cout << "Reset the counter to zero." << endl; | 26 | cout << "Reset the counter to zero." << endl; |
| 26 | } else { | 27 | } else { |
| 27 | cerr << "Failed to reset counter." << endl; | 28 | cerr << "Failed to reset counter." << endl; |
| @@ -39,7 +40,7 @@ int main(int argc, char* argv[]) { | @@ -39,7 +40,7 @@ int main(int argc, char* argv[]) { | ||
| 39 | double t0 = time_s(); | 40 | double t0 = time_s(); |
| 40 | atomic_int count(0); | 41 | atomic_int count(0); |
| 41 | 42 | ||
| 42 | - Command<int>& cmd = rdx.command<int>( | 43 | + Command<int>& cmd = rdx.command_looping<int>( |
| 43 | cmd_str, | 44 | cmd_str, |
| 44 | [&count, &rdx](Command<int>& c) { | 45 | [&count, &rdx](Command<int>& c) { |
| 45 | if(!c.ok()) { | 46 | if(!c.ok()) { |
| @@ -54,21 +55,17 @@ int main(int argc, char* argv[]) { | @@ -54,21 +55,17 @@ int main(int argc, char* argv[]) { | ||
| 54 | this_thread::sleep_for(chrono::microseconds((int)(t*1e6))); | 55 | this_thread::sleep_for(chrono::microseconds((int)(t*1e6))); |
| 55 | cmd.cancel(); | 56 | cmd.cancel(); |
| 56 | 57 | ||
| 57 | - rdx.command<string>("GET simple_loop:count", [&](Command<string>& c) { | ||
| 58 | - if(!c.ok()) return; | ||
| 59 | - long final_count = stol(c.reply()); | 58 | + long final_count = stol(rdx.get("simple_loop:count")); |
| 60 | 59 | ||
| 61 | - double t_elapsed = time_s() - t0; | ||
| 62 | - double actual_freq = (double)count / t_elapsed; | ||
| 63 | 60 | ||
| 64 | - cout << "Sent " << count << " commands in " << t_elapsed << "s, " | ||
| 65 | - << "that's " << actual_freq << " commands/s." << endl; | 61 | + double t_elapsed = time_s() - t0; |
| 62 | + double actual_freq = (double)count / t_elapsed; | ||
| 66 | 63 | ||
| 67 | - cout << "Final value of counter: " << final_count << endl; | 64 | + cout << "Sent " << count << " commands in " << t_elapsed << "s, " |
| 65 | + << "that's " << actual_freq << " commands/s." << endl; | ||
| 68 | 66 | ||
| 69 | - rdx.stop_signal(); | ||
| 70 | - }); | 67 | + cout << "Final value of counter: " << final_count << endl; |
| 71 | 68 | ||
| 72 | - rdx.block(); | 69 | + rdx.stop(); |
| 73 | return 0; | 70 | return 0; |
| 74 | } | 71 | } |
examples/speed_test_async_multi.cpp
| @@ -43,7 +43,7 @@ int main(int argc, char* argv[]) { | @@ -43,7 +43,7 @@ int main(int argc, char* argv[]) { | ||
| 43 | 43 | ||
| 44 | vector<Command<int>*> commands; | 44 | vector<Command<int>*> commands; |
| 45 | for(int i = 0; i < parallel; i++) { | 45 | for(int i = 0; i < parallel; i++) { |
| 46 | - commands.push_back(&rdx.command<int>( | 46 | + commands.push_back(&rdx.command_looping<int>( |
| 47 | cmd_str, | 47 | cmd_str, |
| 48 | [&count, &rdx](Command<int>& c) { | 48 | [&count, &rdx](Command<int>& c) { |
| 49 | if(!c.ok()) { | 49 | if(!c.ok()) { |
src/command.cpp
| @@ -26,6 +26,17 @@ Command<ReplyT>::Command( | @@ -26,6 +26,17 @@ 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; | ||
| 37 | +} | ||
| 38 | + | ||
| 39 | +template<class ReplyT> | ||
| 29 | void Command<ReplyT>::processReply(redisReply* r) { | 40 | void Command<ReplyT>::processReply(redisReply* r) { |
| 30 | 41 | ||
| 31 | free_guard_.lock(); | 42 | free_guard_.lock(); |
| @@ -33,9 +44,13 @@ void Command<ReplyT>::processReply(redisReply* r) { | @@ -33,9 +44,13 @@ void Command<ReplyT>::processReply(redisReply* r) { | ||
| 33 | reply_obj_ = r; | 44 | reply_obj_ = r; |
| 34 | parseReplyObject(); | 45 | parseReplyObject(); |
| 35 | invoke(); | 46 | invoke(); |
| 36 | - | 47 | +// logger_.info() << "reply status " << reply_status_; |
| 37 | pending_--; | 48 | pending_--; |
| 38 | 49 | ||
| 50 | + blocking_done_ = true; | ||
| 51 | +// logger_.info() << "notifying blocker"; | ||
| 52 | + blocker_.notify_all(); | ||
| 53 | + | ||
| 39 | // Allow free() method to free memory | 54 | // Allow free() method to free memory |
| 40 | if (!free_memory_) { | 55 | if (!free_memory_) { |
| 41 | // logger.trace() << "Command memory not being freed, free_memory = " << free_memory; | 56 | // logger.trace() << "Command memory not being freed, free_memory = " << free_memory; |
| @@ -163,19 +178,18 @@ bool Command<ReplyT>::checkNilReply() { | @@ -163,19 +178,18 @@ bool Command<ReplyT>::checkNilReply() { | ||
| 163 | 178 | ||
| 164 | template<> | 179 | template<> |
| 165 | void Command<redisReply*>::parseReplyObject() { | 180 | void Command<redisReply*>::parseReplyObject() { |
| 181 | + if(!checkErrorReply()) reply_status_ = OK_REPLY; | ||
| 166 | reply_val_ = reply_obj_; | 182 | reply_val_ = reply_obj_; |
| 167 | } | 183 | } |
| 168 | 184 | ||
| 169 | template<> | 185 | template<> |
| 170 | void Command<string>::parseReplyObject() { | 186 | void Command<string>::parseReplyObject() { |
| 171 | - | ||
| 172 | if(!isExpectedReply(REDIS_REPLY_STRING, REDIS_REPLY_STATUS)) return; | 187 | if(!isExpectedReply(REDIS_REPLY_STRING, REDIS_REPLY_STATUS)) return; |
| 173 | reply_val_ = {reply_obj_->str, static_cast<size_t>(reply_obj_->len)}; | 188 | reply_val_ = {reply_obj_->str, static_cast<size_t>(reply_obj_->len)}; |
| 174 | } | 189 | } |
| 175 | 190 | ||
| 176 | template<> | 191 | template<> |
| 177 | void Command<char*>::parseReplyObject() { | 192 | void Command<char*>::parseReplyObject() { |
| 178 | - | ||
| 179 | if(!isExpectedReply(REDIS_REPLY_STRING, REDIS_REPLY_STATUS)) return; | 193 | if(!isExpectedReply(REDIS_REPLY_STRING, REDIS_REPLY_STATUS)) return; |
| 180 | reply_val_ = reply_obj_->str; | 194 | reply_val_ = reply_obj_->str; |
| 181 | } | 195 | } |
src/command.hpp
| @@ -8,6 +8,7 @@ | @@ -8,6 +8,7 @@ | ||
| 8 | #include <functional> | 8 | #include <functional> |
| 9 | #include <atomic> | 9 | #include <atomic> |
| 10 | #include <mutex> | 10 | #include <mutex> |
| 11 | +#include <condition_variable> | ||
| 11 | 12 | ||
| 12 | #include <hiredis/adapters/libev.h> | 13 | #include <hiredis/adapters/libev.h> |
| 13 | #include <hiredis/async.h> | 14 | #include <hiredis/async.h> |
| @@ -17,10 +18,6 @@ | @@ -17,10 +18,6 @@ | ||
| 17 | namespace redox { | 18 | namespace redox { |
| 18 | 19 | ||
| 19 | class Redox; | 20 | class Redox; |
| 20 | -//class Command; | ||
| 21 | - | ||
| 22 | -//template <typename ReplyT> | ||
| 23 | -//using CallbackT = std::function<void(Command<ReplyT>&)>; | ||
| 24 | 21 | ||
| 25 | /** | 22 | /** |
| 26 | * The Command class represents a single command string to be sent to | 23 | * The Command class represents a single command string to be sent to |
| @@ -55,25 +52,30 @@ public: | @@ -55,25 +52,30 @@ public: | ||
| 55 | void cancel() { canceled_ = true; } | 52 | void cancel() { canceled_ = true; } |
| 56 | 53 | ||
| 57 | /** | 54 | /** |
| 55 | + * This method returns once this command's callback has been invoked | ||
| 56 | + * (or would have been invoked if there is none) since the last call | ||
| 57 | + * to block(). If it is the first call, then returns once the callback | ||
| 58 | + * is invoked for the first time. | ||
| 59 | + */ | ||
| 60 | + Command<ReplyT>& block(); | ||
| 61 | + | ||
| 62 | + /** | ||
| 58 | * Returns true if the command has been canceled. | 63 | * Returns true if the command has been canceled. |
| 59 | */ | 64 | */ |
| 60 | bool canceled() const { return canceled_; } | 65 | bool canceled() const { return canceled_; } |
| 61 | 66 | ||
| 62 | /** | 67 | /** |
| 63 | * Returns the reply status of this command. | 68 | * Returns the reply status of this command. |
| 64 | - * Use ONLY with command_blocking. | ||
| 65 | */ | 69 | */ |
| 66 | - int status() const { return reply_status_; }; | 70 | + int status() const { return reply_status_; } |
| 67 | 71 | ||
| 68 | /** | 72 | /** |
| 69 | * Returns true if this command got a successful reply. | 73 | * Returns true if this command got a successful reply. |
| 70 | - * Use ONLY with command_blocking. | ||
| 71 | */ | 74 | */ |
| 72 | bool ok() const { return reply_status_ == OK_REPLY; } | 75 | bool ok() const { return reply_status_ == OK_REPLY; } |
| 73 | 76 | ||
| 74 | /** | 77 | /** |
| 75 | * Returns the reply value, if the reply was successful (ok() == true). | 78 | * Returns the reply value, if the reply was successful (ok() == true). |
| 76 | - * Use ONLY with command_blocking. | ||
| 77 | */ | 79 | */ |
| 78 | const ReplyT& reply() const; | 80 | const ReplyT& reply() const; |
| 79 | 81 | ||
| @@ -145,6 +147,11 @@ private: | @@ -145,6 +147,11 @@ private: | ||
| 145 | // Make sure we don't free resources until details taken care of | 147 | // Make sure we don't free resources until details taken care of |
| 146 | std::mutex free_guard_; | 148 | std::mutex free_guard_; |
| 147 | 149 | ||
| 150 | + // For synchronous use | ||
| 151 | + std::condition_variable blocker_; | ||
| 152 | + std::mutex blocker_lock_; | ||
| 153 | + std::atomic_bool blocking_done_ = {false}; | ||
| 154 | + | ||
| 148 | // Passed on from Redox class | 155 | // Passed on from Redox class |
| 149 | log::Logger& logger_; | 156 | log::Logger& logger_; |
| 150 | 157 |
src/redox.cpp
| @@ -380,7 +380,7 @@ void Redox::subscribe_raw(const string cmd_name, const string topic, | @@ -380,7 +380,7 @@ void Redox::subscribe_raw(const string cmd_name, const string topic, | ||
| 380 | // Start pubsub mode. No non-sub/unsub commands can be emitted by this client. | 380 | // Start pubsub mode. No non-sub/unsub commands can be emitted by this client. |
| 381 | pubsub_mode = true; | 381 | pubsub_mode = true; |
| 382 | 382 | ||
| 383 | - command<redisReply*>(cmd_name + " " + topic, | 383 | + command_looping<redisReply*>(cmd_name + " " + topic, |
| 384 | [this, topic, msg_callback, err_callback, sub_callback, unsub_callback](Command<redisReply*>& c) { | 384 | [this, topic, msg_callback, err_callback, sub_callback, unsub_callback](Command<redisReply*>& c) { |
| 385 | 385 | ||
| 386 | if(!c.ok()) { | 386 | if(!c.ok()) { |
| @@ -402,6 +402,8 @@ void Redox::subscribe_raw(const string cmd_name, const string topic, | @@ -402,6 +402,8 @@ void Redox::subscribe_raw(const string cmd_name, const string topic, | ||
| 402 | // } | 402 | // } |
| 403 | // cout << "------" << endl; | 403 | // cout << "------" << endl; |
| 404 | 404 | ||
| 405 | + // TODO cancel this command on unsubscription? | ||
| 406 | + | ||
| 405 | // If the last entry is an integer, then it is a [p]sub/[p]unsub command | 407 | // If the last entry is an integer, then it is a [p]sub/[p]unsub command |
| 406 | if((reply->type == REDIS_REPLY_ARRAY) && | 408 | if((reply->type == REDIS_REPLY_ARRAY) && |
| 407 | (reply->element[reply->elements-1]->type == REDIS_REPLY_INTEGER)) { | 409 | (reply->element[reply->elements-1]->type == REDIS_REPLY_INTEGER)) { |
| @@ -567,12 +569,8 @@ Redox::get_command_map<unordered_set<string>>() { return commands_unordered_set_ | @@ -567,12 +569,8 @@ Redox::get_command_map<unordered_set<string>>() { return commands_unordered_set_ | ||
| 567 | // Helpers | 569 | // Helpers |
| 568 | // ---------------------------- | 570 | // ---------------------------- |
| 569 | 571 | ||
| 570 | -void Redox::command(const string& cmd) { | ||
| 571 | - command<redisReply*>(cmd); | ||
| 572 | -} | ||
| 573 | - | ||
| 574 | bool Redox::command_blocking(const string& cmd) { | 572 | bool Redox::command_blocking(const string& cmd) { |
| 575 | - Command<redisReply*>& c = command_blocking<redisReply*>(cmd); | 573 | + auto& c = command_blocking<redisReply*>(cmd); |
| 576 | bool succeeded = c.ok(); | 574 | bool succeeded = c.ok(); |
| 577 | c.free(); | 575 | c.free(); |
| 578 | return succeeded; | 576 | return succeeded; |
src/redox.hpp
| @@ -87,32 +87,44 @@ public: | @@ -87,32 +87,44 @@ public: | ||
| 87 | void stop(); | 87 | void stop(); |
| 88 | 88 | ||
| 89 | /** | 89 | /** |
| 90 | - * Create an asynchronous Redis command to be executed. Return a pointer to a | ||
| 91 | - * Command object that represents this command. If the command succeeded, the | ||
| 92 | - * callback is invoked with a reference to the reply. If something went wrong, | ||
| 93 | - * the error_callback is invoked with an error_code. One of the two is guaranteed | ||
| 94 | - * to be invoked. The method is templated by the expected data type of the reply, | ||
| 95 | - * and can be one of {redisReply*, string, char*, int, long long int, nullptr_t}. | ||
| 96 | - * | ||
| 97 | - * cmd: The command to be run. | ||
| 98 | - * callback: A function invoked on a successful reply from the server. | ||
| 99 | - * error_callback: A function invoked on some error state. | ||
| 100 | - * repeat: If non-zero, executes the command continuously at the given rate | ||
| 101 | - * in seconds, until cancel() is called on the Command object. | ||
| 102 | - * after: If non-zero, executes the command after the given delay in seconds. | ||
| 103 | - * free_memory: If true (default), Redox automatically frees the Command object and | ||
| 104 | - * reply from the server after a callback is invoked. If false, the | ||
| 105 | - * user is responsible for calling free() on the Command object. | 90 | + * Asynchronously runs a command and invokes the callback when a reply is |
| 91 | + * received or there is an error. The callback is guaranteed to be invoked | ||
| 92 | + * exactly once. The Command object is provided to the callback, and the | ||
| 93 | + * memory for it is automatically freed when the callback returns. | ||
| 106 | */ | 94 | */ |
| 107 | template<class ReplyT> | 95 | template<class ReplyT> |
| 108 | - Command<ReplyT>& command( | ||
| 109 | - const std::string& cmd, | ||
| 110 | - const std::function<void(Command<ReplyT>&)>& callback = nullptr, | ||
| 111 | - double repeat = 0.0, | ||
| 112 | - double after = 0.0, | ||
| 113 | - bool free_memory = true | 96 | + void command( |
| 97 | + const std::string& cmd, | ||
| 98 | + const std::function<void(Command<ReplyT>&)>& callback = nullptr | ||
| 114 | ); | 99 | ); |
| 115 | 100 | ||
| 101 | + /** | ||
| 102 | + * Asynchronously runs a command and ignores any errors or replies. | ||
| 103 | + */ | ||
| 104 | + void command(const std::string& cmd) { command<redisReply*>(cmd, nullptr); } | ||
| 105 | + | ||
| 106 | + /** | ||
| 107 | + * Synchronously runs a command, returning the Command object only once | ||
| 108 | + * a reply is received or there is an error. The user is responsible for | ||
| 109 | + * calling the Command object's .free() method when done with it. | ||
| 110 | + */ | ||
| 111 | + template<class ReplyT> | ||
| 112 | + Command<ReplyT>& command_blocking(const std::string& cmd); | ||
| 113 | + | ||
| 114 | + /** | ||
| 115 | + * Synchronously runs a command, returning only once a reply is received | ||
| 116 | + * or there's an error. The return value is true if the command got a | ||
| 117 | + * successful reply, and false if something went wrong. | ||
| 118 | + */ | ||
| 119 | + bool command_blocking(const std::string& cmd); | ||
| 120 | + | ||
| 121 | + template<class ReplyT> | ||
| 122 | + Command<ReplyT>& command_looping( | ||
| 123 | + const std::string& cmd, | ||
| 124 | + const std::function<void(Command<ReplyT>&)>& callback, | ||
| 125 | + double repeat, | ||
| 126 | + double after = 0.0 | ||
| 127 | + ); | ||
| 116 | 128 | ||
| 117 | /** | 129 | /** |
| 118 | * A wrapper around command() for synchronous use. Waits for a reply, populates it | 130 | * A wrapper around command() for synchronous use. Waits for a reply, populates it |
| @@ -121,8 +133,8 @@ public: | @@ -121,8 +133,8 @@ public: | ||
| 121 | * status() will give the error code, and reply() will return the reply data if | 133 | * status() will give the error code, and reply() will return the reply data if |
| 122 | * the call succeeded. | 134 | * the call succeeded. |
| 123 | */ | 135 | */ |
| 124 | - template<class ReplyT> | ||
| 125 | - Command<ReplyT>& command_blocking(const std::string& cmd); | 136 | +// template<class ReplyT> |
| 137 | +// Command<ReplyT>& command_blocking(const std::string& cmd); | ||
| 126 | 138 | ||
| 127 | /** | 139 | /** |
| 128 | * Return the total number of successful commands processed by this Redox instance. | 140 | * Return the total number of successful commands processed by this Redox instance. |
| @@ -146,13 +158,13 @@ public: | @@ -146,13 +158,13 @@ public: | ||
| 146 | * Non-templated version of command in case you really don't care | 158 | * Non-templated version of command in case you really don't care |
| 147 | * about the reply and just want to send something off. | 159 | * about the reply and just want to send something off. |
| 148 | */ | 160 | */ |
| 149 | - void command(const std::string& command); | 161 | +// void command(const std::string& command); |
| 150 | 162 | ||
| 151 | /** | 163 | /** |
| 152 | * Non-templated version of command_blocking in case you really don't | 164 | * Non-templated version of command_blocking in case you really don't |
| 153 | * care about the reply. Returns true if succeeded, false if error. | 165 | * care about the reply. Returns true if succeeded, false if error. |
| 154 | */ | 166 | */ |
| 155 | - bool command_blocking(const std::string& command); | 167 | +// bool command_blocking(const std::string& command); |
| 156 | 168 | ||
| 157 | /** | 169 | /** |
| 158 | * Redis GET command wrapper - return the value for the given key, or throw | 170 | * Redis GET command wrapper - return the value for the given key, or throw |
| @@ -265,6 +277,15 @@ public: | @@ -265,6 +277,15 @@ public: | ||
| 265 | 277 | ||
| 266 | private: | 278 | private: |
| 267 | 279 | ||
| 280 | + template<class ReplyT> | ||
| 281 | + Command<ReplyT>& createCommand( | ||
| 282 | + const std::string& cmd, | ||
| 283 | + const std::function<void(Command<ReplyT>&)>& callback = nullptr, | ||
| 284 | + double repeat = 0.0, | ||
| 285 | + double after = 0.0, | ||
| 286 | + bool free_memory = true | ||
| 287 | + ); | ||
| 288 | + | ||
| 268 | // Setup code for the constructors | 289 | // Setup code for the constructors |
| 269 | void init_ev(); | 290 | void init_ev(); |
| 270 | void init_hiredis(); | 291 | void init_hiredis(); |
| @@ -373,7 +394,7 @@ private: | @@ -373,7 +394,7 @@ private: | ||
| 373 | 394 | ||
| 374 | 395 | ||
| 375 | template<class ReplyT> | 396 | template<class ReplyT> |
| 376 | -Command<ReplyT>& Redox::command( | 397 | +Command<ReplyT>& Redox::createCommand( |
| 377 | const std::string& cmd, | 398 | const std::string& cmd, |
| 378 | const std::function<void(Command<ReplyT>&)>& callback, | 399 | const std::function<void(Command<ReplyT>&)>& callback, |
| 379 | double repeat, | 400 | double repeat, |
| @@ -409,6 +430,24 @@ Command<ReplyT>& Redox::command( | @@ -409,6 +430,24 @@ Command<ReplyT>& Redox::command( | ||
| 409 | } | 430 | } |
| 410 | 431 | ||
| 411 | template<class ReplyT> | 432 | template<class ReplyT> |
| 433 | +void Redox::command( | ||
| 434 | + const std::string& cmd, | ||
| 435 | + const std::function<void(Command<ReplyT>&)>& callback | ||
| 436 | +) { | ||
| 437 | + createCommand(cmd, callback); | ||
| 438 | +} | ||
| 439 | + | ||
| 440 | +template<class ReplyT> | ||
| 441 | +Command<ReplyT>& Redox::command_looping( | ||
| 442 | + const std::string& cmd, | ||
| 443 | + const std::function<void(Command<ReplyT>&)>& callback, | ||
| 444 | + double repeat, | ||
| 445 | + double after | ||
| 446 | +) { | ||
| 447 | + return createCommand(cmd, callback, repeat, after); | ||
| 448 | +} | ||
| 449 | + | ||
| 450 | +template<class ReplyT> | ||
| 412 | Command<ReplyT>& Redox::command_blocking(const std::string& cmd) { | 451 | Command<ReplyT>& Redox::command_blocking(const std::string& cmd) { |
| 413 | 452 | ||
| 414 | std::condition_variable cv; | 453 | std::condition_variable cv; |
| @@ -416,7 +455,7 @@ Command<ReplyT>& Redox::command_blocking(const std::string& cmd) { | @@ -416,7 +455,7 @@ Command<ReplyT>& Redox::command_blocking(const std::string& cmd) { | ||
| 416 | std::unique_lock<std::mutex> lk(m); | 455 | std::unique_lock<std::mutex> lk(m); |
| 417 | std::atomic_bool done = {false}; | 456 | std::atomic_bool done = {false}; |
| 418 | 457 | ||
| 419 | - Command<ReplyT>& c = command<ReplyT>(cmd, | 458 | + Command<ReplyT>& c = createCommand<ReplyT>(cmd, |
| 420 | [&cv, &done](Command<ReplyT>& cmd_obj) { | 459 | [&cv, &done](Command<ReplyT>& cmd_obj) { |
| 421 | done = true; | 460 | done = true; |
| 422 | cv.notify_one(); | 461 | cv.notify_one(); |
test/test.cpp
| @@ -9,8 +9,9 @@ | @@ -9,8 +9,9 @@ | ||
| 9 | 9 | ||
| 10 | namespace { | 10 | namespace { |
| 11 | 11 | ||
| 12 | -using namespace redox; | ||
| 13 | using namespace std; | 12 | using namespace std; |
| 13 | +using redox::Redox; | ||
| 14 | +using redox::Command; | ||
| 14 | 15 | ||
| 15 | // ------------------------------------------ | 16 | // ------------------------------------------ |
| 16 | // The fixture for testing class Redox. | 17 | // The fixture for testing class Redox. |
| @@ -43,8 +44,8 @@ protected: | @@ -43,8 +44,8 @@ protected: | ||
| 43 | mutex cmd_waiter_lock; | 44 | mutex cmd_waiter_lock; |
| 44 | 45 | ||
| 45 | // To make the callback code nicer | 46 | // To make the callback code nicer |
| 46 | - template <class ReplyT> | ||
| 47 | - using Callback = std::function<void(const std::string&, const ReplyT&)>; | 47 | + template<class ReplyT> |
| 48 | + using Callback = std::function<void(Command<ReplyT>&)>; | ||
| 48 | 49 | ||
| 49 | /** | 50 | /** |
| 50 | * Helper function that returns a command callback to print out the | 51 | * Helper function that returns a command callback to print out the |
| @@ -53,8 +54,9 @@ protected: | @@ -53,8 +54,9 @@ protected: | ||
| 53 | template<class ReplyT> | 54 | template<class ReplyT> |
| 54 | Callback<ReplyT> check(const ReplyT& value) { | 55 | Callback<ReplyT> check(const ReplyT& value) { |
| 55 | cmd_count++; | 56 | cmd_count++; |
| 56 | - return [this, value](const string& cmd, const ReplyT& reply) { | ||
| 57 | - EXPECT_EQ(reply, value); | 57 | + return [this, value](Command<ReplyT>& c) { |
| 58 | + EXPECT_TRUE(c.ok()); | ||
| 59 | + if(c.ok()) EXPECT_EQ(c.reply(), value); | ||
| 58 | cmd_count--; | 60 | cmd_count--; |
| 59 | cmd_waiter.notify_all(); | 61 | cmd_waiter.notify_all(); |
| 60 | }; | 62 | }; |
| @@ -65,9 +67,9 @@ protected: | @@ -65,9 +67,9 @@ protected: | ||
| 65 | */ | 67 | */ |
| 66 | template<class ReplyT> | 68 | template<class ReplyT> |
| 67 | Callback<ReplyT> print(Callback<ReplyT> callback) { | 69 | Callback<ReplyT> print(Callback<ReplyT> callback) { |
| 68 | - return [callback](const string& cmd, const ReplyT& reply) { | ||
| 69 | - cout << "[ASYNC] " << cmd << ": " << reply << endl; | ||
| 70 | - callback(cmd, reply); | 70 | + return [callback](Command<ReplyT>& c) { |
| 71 | + if(c.ok()) cout << "[ASYNC] " << c.cmd() << ": " << c.reply() << endl; | ||
| 72 | + callback(c); | ||
| 71 | }; | 73 | }; |
| 72 | } | 74 | } |
| 73 | 75 | ||
| @@ -90,18 +92,18 @@ protected: | @@ -90,18 +92,18 @@ protected: | ||
| 90 | }; | 92 | }; |
| 91 | 93 | ||
| 92 | template<class ReplyT> | 94 | template<class ReplyT> |
| 93 | - void check_sync(Command<ReplyT>* c, const ReplyT& value) { | ||
| 94 | - ASSERT_TRUE(c->ok()); | ||
| 95 | - EXPECT_EQ(c->reply(), value); | ||
| 96 | - c->free(); | 95 | + void check_sync(Command<ReplyT>& c, const ReplyT& value) { |
| 96 | + ASSERT_TRUE(c.ok()); | ||
| 97 | + EXPECT_EQ(c.reply(), value); | ||
| 98 | + c.free(); | ||
| 97 | } | 99 | } |
| 98 | 100 | ||
| 99 | template<class ReplyT> | 101 | template<class ReplyT> |
| 100 | - void print_and_check_sync(Command<ReplyT>* c, const ReplyT& value) { | ||
| 101 | - ASSERT_TRUE(c->ok()); | ||
| 102 | - EXPECT_EQ(c->reply(), value); | ||
| 103 | - cout << "[SYNC] " << c->cmd_ << ": " << c->reply() << endl; | ||
| 104 | - c->free(); | 102 | + void print_and_check_sync(Command<ReplyT>& c, const ReplyT& value) { |
| 103 | + ASSERT_TRUE(c.ok()); | ||
| 104 | + EXPECT_EQ(c.reply(), value); | ||
| 105 | + cout << "[SYNC] " << c.cmd_ << ": " << c.reply() << endl; | ||
| 106 | + c.free(); | ||
| 105 | } | 107 | } |
| 106 | }; | 108 | }; |
| 107 | 109 | ||
| @@ -132,9 +134,9 @@ TEST_F(RedoxTest, Incr) { | @@ -132,9 +134,9 @@ TEST_F(RedoxTest, Incr) { | ||
| 132 | } | 134 | } |
| 133 | 135 | ||
| 134 | TEST_F(RedoxTest, Delayed) { | 136 | TEST_F(RedoxTest, Delayed) { |
| 135 | - Command<int>* c = rdx.command<int>("INCR redox_test:a", check(1), nullptr, 0, 0.1); | 137 | + Command<int>& c = rdx.command_looping<int>("INCR redox_test:a", check(1), 0, 0.1); |
| 136 | this_thread::sleep_for(chrono::milliseconds(150)); | 138 | this_thread::sleep_for(chrono::milliseconds(150)); |
| 137 | - c->cancel(); | 139 | + c.cancel(); |
| 138 | rdx.command<string>("GET redox_test:a", print_and_check(to_string(1))); | 140 | rdx.command<string>("GET redox_test:a", print_and_check(to_string(1))); |
| 139 | wait_and_stop(); | 141 | wait_and_stop(); |
| 140 | } | 142 | } |
| @@ -143,14 +145,16 @@ TEST_F(RedoxTest, Loop) { | @@ -143,14 +145,16 @@ TEST_F(RedoxTest, Loop) { | ||
| 143 | int count = 0; | 145 | int count = 0; |
| 144 | int target_count = 100; | 146 | int target_count = 100; |
| 145 | double dt = 0.001; | 147 | double dt = 0.001; |
| 146 | - Command<int>* c = rdx.command<int>("INCR redox_test:a", | ||
| 147 | - [this, &count](const string& cmd, const int& reply) { | ||
| 148 | - check(++count)(cmd, reply); | ||
| 149 | - }, nullptr, dt); | 148 | + Command<int>& cmd = rdx.command_looping<int>("INCR redox_test:a", |
| 149 | + [this, &count](Command<int>& c) { | ||
| 150 | + check(++count)(c); | ||
| 151 | + }, | ||
| 152 | + dt | ||
| 153 | + ); | ||
| 150 | 154 | ||
| 151 | double wait_time = dt * (target_count - 0.5); | 155 | double wait_time = dt * (target_count - 0.5); |
| 152 | this_thread::sleep_for(std::chrono::duration<double>(wait_time)); | 156 | this_thread::sleep_for(std::chrono::duration<double>(wait_time)); |
| 153 | - c->cancel(); | 157 | + cmd.cancel(); |
| 154 | 158 | ||
| 155 | rdx.command<string>("GET redox_test:a", print_and_check(to_string(target_count))); | 159 | rdx.command<string>("GET redox_test:a", print_and_check(to_string(target_count))); |
| 156 | wait_and_stop(); | 160 | wait_and_stop(); |