-
Added GTest under test/. Great for unit testing commands. Added some nice helper functions to make writing tests easy. Added options to CMakeLists to build the library, tests, and examples separately, configurable through ccmake. Also default the build type to RelWithDebInfo, but don't override user settings.
-
Consolidated user callbacks into one, improved some logic.
-
Created an atomic_int connect_state that keeps track of not yet connected, connected, disconnected, and errors. Used to implement good error behavior when the server is down and you start() a Redox instance, or when the server goes down in the middle of running commands. Now, nothing should hang, but should return errors (or throw exceptions) when the server goes down. Also added hooks for a user connect/disconnect callback in the constructor, which is helpful for client programs. Added an example with three Redox clients in one thread.
-
Finally getting rid of the busy loop in the event thread. Now, we use an ev_async watcher that processes commands in the queue, which we can signal in a thread-safe manner using ev_async_send.
-
Instead of busy looping event thread, use a condition variable that is notified every time a command is sent or received, or every 100 milliseconds anyway.
-
Better than using a laptop, because it can be reproduced by others much more easily
-
submit_command_callback
-
Also plan to add in set<string> and unordered_set<string>. Maybe queue<string>. Looking at likely removing char* as an option, if it shows to be about the same speed as string.
-
Thoroughly cleaned up redox.hpp to expose a minimal public API, and commented those well. Generally cleaned up a bunch of stuff. Added wrapper function DEL, which could be useful.
-
Easy few lines for get and set commands that are easy to use. Implemented a condition variable that blocks run() until the event loop is running and Redis is connected. Also added a check to command() that will throw an exception if you try to add a command before calling run().
-
Fixed a couple of bugs found with a test case of 100 parallel asynchronous clients. As of now, there are no known memory leaks, segfaults, or deadlocks in Redox.
-
Ran into a nasty segfault when using 100 parallel asynchronous command loops, where a command's address would match up with a previous command's address. To circumvent the whole issue, Redox keeps a counter of how many Commands it has created and assigns each Command a unique id in the order they are created. Changed all data structures and methods to pass around Command IDs, so nothing uses void* anymore. I consider this a very good thing, but it did seem to slow down performance 20-30% because of the extra lookups. Will see if there's a solution later.
-
Take that, hours of debugging memory leaks! Did lots of structural tweaks to more smartly keep track of commands and make sure everything is evenutally freed from the heap.
-
Created a pointer Command.rdx (Redox*) to allow commands to increment Redox.cmd_count, and simplify a couple of other things. Did a little bit of moving and renaming context objects, so the context is always .ctx and commands can always be called c.
-
Added Command<nullptr_t> which successfully returns NIL replies from Redis, with REDOX_OK. Also renamed constants from REDISX_ -> REDOX_. Moved a bunch of code from redox.cpp into command.cpp, where it logically makes more sense.
-
Move as much as possible into the .cpp file, and clean things up.
-
Found out that ev_timer watchers have a void* data that allows currying information to the libev callbacks! That means I could stick the Command* in there and completely get rid of the global timer_callbacks map that was needed before. Additionally, we can move ev_timer into the Command object instead of separately allocating it on the heap. Simplifies management. Implemented a synchronous loop speed test based on the latest and greatest command_blocking(). Happy to see it is around 30k/s, which is 100% network delays.
-
Several hours of multithreaded debugging to find a nasty extra ampersand. command_blocking() now returns a Command object with the value and status accessible through methods. Added the option to free memory or not for Command objects when command() is called. If free_memory = true, then the Command object is freed after the callback or error callback returns. If free_memory = false, then the user must call cmd->free(). To implement this, we have to pass in a blank function to the freeObject entry in the hiredis redisReader, because otherwise it automatically frees memory. This was not cool for the blocking case.
-
Implemented error callbacks for command(). Now, either the success or error callbacks are guaranteed to return for any command. Added constants for error types. Many improvements and fixes for memory management. Command objects keep track of pending replies and delete themselves when completed. Changed ReplyT value to remove the const and reference requirements. Now, you do command<ReplyT> and the callback should take a const ReplyT&, whatever the type is. Makes things more uniform.
-
Renamed CommandAsync to Command to make things simpler, and refactored into its own file. command() now returns a pointer to the created Command object, which the client can pass into cancel() to stop any delayed or repeating calls. This is very important for an asynchronous client.
-
Uses libev timer watchers to queue commands on a regular interval. Very fast. Still need to handle deactivation of timers from the user side.
-
num_commands_processed(), trarcks using cmd_count and guarded with queue_guard.
-
Used to block the main thread until the event loop is stopped by calling the .stop() method. Implemented using a condition_variable, and very useful for clients.
-
Improved examples, added a threaded getter/setter example and cleaned up the other. Generally refactored code in the core files and cleaned up the details to make the slickest possible API. Implemented a to_exit variable as a stop condition for the event thread.
-
The event loop (now libev) now runs in a separate detached thread, which is abstracted away from the user, who only calls a nonblocking .start() method. This thread loops continously, alternating telling Redis about asynchronous commands ready to send, and running one iteration of the event loop, where all pending events are taken care of. This greatly simplifies the user code. Additionally, some clever tricker is implemented now to handle memory management well with the templated command types. The difficulty comes from the fact that we pass redis a void pointer only, and must retreive everything from that (so, we can't use shared_ptr for the whole thing). Thus, we use maps of void pointers to their templated data structure pointers, where the memory address of the pointer serves as the key.
-
Basic CMake setup, redisx implementation, and one example program.