/** * Redis C++11 wrapper. */ #pragma once #include #include #include #include #include #include #include #include "utils/logger.hpp" namespace redox { class Redox; /** * The Command class represents a single command string to be sent to * a Redis server, for both synchronous and asynchronous usage. It manages * all of the state relevant to a single command string. A Command can also * represent a deferred or looping command, in which case the success or * error callbacks are invoked more than once. */ template class Command { public: // Reply codes static const int NO_REPLY = -1; // No reply yet static const int OK_REPLY = 0; // Successful reply of the expected type static const int NIL_REPLY = 1; // Got a nil reply static const int ERROR_REPLY = 2; // Got an error reply static const int SEND_ERROR = 3; // Could not send to server static const int WRONG_TYPE = 4; // Got reply, but it was not the expected type static const int TIMEOUT = 5; // No reply, timed out /** * Frees memory allocated by this command. Commands with free_memory = false * must be freed by the user. */ void free(); /** * Cancels a repeating or delayed command. */ void cancel() { canceled_ = true; } /** * This method returns once this command's callback has been invoked * (or would have been invoked if there is none) since the last call * to block(). If it is the first call, then returns once the callback * is invoked for the first time. */ Command& block(); /** * Returns true if the command has been canceled. */ bool canceled() const { return canceled_; } /** * Returns the reply status of this command. */ int status() const { return reply_status_; } /** * Returns true if this command got a successful reply. */ bool ok() const { return reply_status_ == OK_REPLY; } /** * Returns the reply value, if the reply was successful (ok() == true). */ const ReplyT& reply() const; const std::string& cmd() const { return cmd_; }; // Allow public access to constructed data Redox* const rdx_; const long id_; const std::string cmd_; const double repeat_; const double after_; const bool free_memory_; private: Command( Redox* rdx, long id, const std::string& cmd, const std::function&)>& callback, double repeat, double after, bool free_memory, log::Logger& logger ); // Handles a new reply from the server void processReply(redisReply* r); // Invoke a user callback from the reply object. This method is specialized // for each ReplyT of Command. void parseReplyObject(); // Directly invoke the user callback if it exists void invoke() { if(callback_) callback_(*this); } bool checkErrorReply(); bool checkNilReply(); bool isExpectedReply(int type); bool isExpectedReply(int typeA, int typeB); // Delete the provided Command object and deregister as an active // command from its Redox instance. static void freeCommand(Command* c); // If needed, free the redisReply void freeReply(); // The last server reply redisReply* reply_obj_ = nullptr; // User callback const std::function&)> callback_; // Place to store the reply value and status. // ONLY for blocking commands ReplyT reply_val_; int reply_status_; // How many messages sent to server but not received reply std::atomic_int pending_ = {0}; // Whether a repeating or delayed command is canceled std::atomic_bool canceled_ = {false}; // libev timer watcher ev_timer timer_; std::mutex timer_guard_; // Make sure we don't free resources until details taken care of std::mutex free_guard_; // For synchronous use std::condition_variable blocker_; std::mutex blocker_lock_; std::atomic_bool blocking_done_ = {false}; // Passed on from Redox class log::Logger& logger_; friend class Redox; }; } // End namespace redis