/** * Redis C++11 wrapper. */ #include #include #include #include "command.hpp" #include "redox.hpp" using namespace std; namespace redox { template Command::Command( Redox* rdx, long id, const std::string& cmd, const std::function&)>& callback, double repeat, double after, bool free_memory, log::Logger& logger ) : rdx_(rdx), id_(id), cmd_(cmd), repeat_(repeat), after_(after), free_memory_(free_memory), callback_(callback), logger_(logger) { timer_guard_.lock(); } template void Command::wait() { std::unique_lock lk(waiter_lock_); waiter_.wait(lk, [this]() { return waiting_done_.load(); }); waiting_done_ = {false}; } template void Command::processReply(redisReply* r) { free_guard_.lock(); reply_obj_ = r; reply_guard_.lock(); parseReplyObject(); reply_guard_.unlock(); invoke(); pending_--; waiting_done_ = true; waiter_.notify_all(); // Allow free() method to free memory if (!free_memory_) { free_guard_.unlock(); return; } freeReply(); // Handle memory if all pending replies have arrived if (pending_ == 0) { // Just free non-repeating commands if (repeat_ == 0) { freeCommand(this); return; // Free repeating commands if timer is stopped } else { if ((long)(timer_.data) == 0) { freeCommand(this); return; } } } free_guard_.unlock(); } template void Command::free() { free_guard_.lock(); freeReply(); free_guard_.unlock(); freeCommand(this); } template void Command::freeReply() { if (reply_obj_ == nullptr) { logger_.error() << cmd_ << ": Attempting to double free reply object."; return; } freeReplyObject(reply_obj_); reply_obj_ = nullptr; } template void Command::freeCommand(Command* c) { c->rdx_->template remove_active_command(c->id_); delete c; } /** * Create a copy of the reply and return it. Use a guard * to make sure we don't return a reply while it is being * modified. */ template ReplyT Command::reply() { std::lock_guard lg(reply_guard_); if (!ok()) { logger_.warning() << cmd_ << ": Accessing reply value while status != OK."; } return reply_val_; } template bool Command::isExpectedReply(int type) { if(reply_obj_->type == type) { reply_status_ = OK_REPLY; return true; } if(checkErrorReply() || checkNilReply()) return false; logger_.error() << cmd_ << ": Received reply of type " << reply_obj_->type << ", expected type " << type << "."; reply_status_ = WRONG_TYPE; return false; } template bool Command::isExpectedReply(int typeA, int typeB) { if((reply_obj_->type == typeA) || (reply_obj_->type == typeB)) { reply_status_ = OK_REPLY; return true; } if(checkErrorReply() || checkNilReply()) return false; logger_.error() << cmd_ << ": Received reply of type " << reply_obj_->type << ", expected type " << typeA << " or " << typeB << "."; reply_status_ = WRONG_TYPE; return false; } template bool Command::checkErrorReply() { if (reply_obj_->type == REDIS_REPLY_ERROR) { logger_.error() << cmd_ << ": " << reply_obj_->str; reply_status_ = ERROR_REPLY; return true; } return false; } template bool Command::checkNilReply() { if (reply_obj_->type == REDIS_REPLY_NIL) { logger_.warning() << cmd_ << ": Nil reply."; reply_status_ = NIL_REPLY; return true; } return false; } // ---------------------------------------------------------------------------- // Specializations of parseReplyObject for all expected return types // ---------------------------------------------------------------------------- template<> void Command::parseReplyObject() { if(!checkErrorReply()) reply_status_ = OK_REPLY; reply_val_ = reply_obj_; } template<> void Command::parseReplyObject() { if(!isExpectedReply(REDIS_REPLY_STRING, REDIS_REPLY_STATUS)) return; reply_val_ = {reply_obj_->str, static_cast(reply_obj_->len)}; } template<> void Command::parseReplyObject() { if(!isExpectedReply(REDIS_REPLY_STRING, REDIS_REPLY_STATUS)) return; reply_val_ = reply_obj_->str; } template<> void Command::parseReplyObject() { if(!isExpectedReply(REDIS_REPLY_INTEGER)) return; reply_val_ = (int) reply_obj_->integer; } template<> void Command::parseReplyObject() { if(!isExpectedReply(REDIS_REPLY_INTEGER)) return; reply_val_ = reply_obj_->integer; } template<> void Command::parseReplyObject() { if(!isExpectedReply(REDIS_REPLY_NIL)) return; reply_val_ = nullptr; } template<> void Command>::parseReplyObject() { if(!isExpectedReply(REDIS_REPLY_ARRAY)) return; for(size_t i = 0; i < reply_obj_->elements; i++) { redisReply* r = *(reply_obj_->element + i); reply_val_.emplace_back(r->str, r->len); } } template<> void Command>::parseReplyObject() { if(!isExpectedReply(REDIS_REPLY_ARRAY)) return; for(size_t i = 0; i < reply_obj_->elements; i++) { redisReply* r = *(reply_obj_->element + i); reply_val_.emplace(r->str, r->len); } } template<> void Command>::parseReplyObject() { if(!isExpectedReply(REDIS_REPLY_ARRAY)) return; for(size_t i = 0; i < reply_obj_->elements; i++) { redisReply* r = *(reply_obj_->element + i); reply_val_.emplace(r->str, r->len); } } // Explicit template instantiation for available types, so that the generated // library contains them and we can keep the method definitions out of the // header file. template class Command; template class Command; template class Command; template class Command; template class Command; template class Command; template class Command>; template class Command>; template class Command>; } // End namespace redox