From d9f35739a67de46fb3cb672740919ad82723b9f6 Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sat, 21 Jan 2017 00:53:20 +0100 Subject: [PATCH 01/19] remove network module and now compile with tacopie --- .gitmodules | 5 + CMakeLists.txt | 26 +- examples/CMakeLists.txt | 20 +- includes/cpp_redis/future_client.hpp | 4 +- includes/cpp_redis/network/io_service.hpp | 93 ------ .../cpp_redis/network/redis_connection.hpp | 18 +- includes/cpp_redis/network/socket.hpp | 42 --- includes/cpp_redis/network/tcp_client.hpp | 109 ------ .../network/unix_impl/io_service.hpp | 125 ------- .../network/windows_impl/io_service.hpp | 160 --------- includes/cpp_redis/redis_client.hpp | 2 +- includes/cpp_redis/redis_subscriber.hpp | 2 +- includes/cpp_redis/sync_client.hpp | 4 +- sources/future_client.cpp | 38 --- sources/network/io_service.cpp | 69 ---- sources/network/redis_connection.cpp | 62 ++-- sources/network/tcp_client.cpp | 313 ----------------- sources/network/unix_impl/io_service.cpp | 316 ------------------ sources/network/windows_impl/io_service.cpp | 280 ---------------- sources/redis_client.cpp | 5 +- sources/redis_subscriber.cpp | 3 +- sources/sync_client.cpp | 38 --- tacopie | 1 + tests/sources/spec/redis_client_spec.cpp | 1 + tests/sources/spec/redis_subscriber_spec.cpp | 1 + 25 files changed, 102 insertions(+), 1635 deletions(-) create mode 100644 .gitmodules delete mode 100644 includes/cpp_redis/network/io_service.hpp delete mode 100644 includes/cpp_redis/network/socket.hpp delete mode 100644 includes/cpp_redis/network/tcp_client.hpp delete mode 100644 includes/cpp_redis/network/unix_impl/io_service.hpp delete mode 100644 includes/cpp_redis/network/windows_impl/io_service.hpp delete mode 100644 sources/future_client.cpp delete mode 100644 sources/network/io_service.cpp delete mode 100644 sources/network/tcp_client.cpp delete mode 100644 sources/network/unix_impl/io_service.cpp delete mode 100644 sources/network/windows_impl/io_service.cpp delete mode 100644 sources/sync_client.cpp create mode 160000 tacopie diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..5bf2dfcd --- /dev/null +++ b/.gitmodules @@ -0,0 +1,5 @@ +[submodule "tacopie"] + path = tacopie + url = https://github.com/Cylix/tacopie.git + branch = master + shallow = true \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 7184a47e..fb908358 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,16 +34,22 @@ ENDIF (WIN32) ### # variables ### +# Deps (Gtest) set(DEPS_FOLDER ${PROJECT_SOURCE_DIR}/deps/build) set(GTEST_INCLUDES ${DEPS_FOLDER}/gtest/include) set(GTEST_LIBS ${DEPS_FOLDER}/gtest/lib) +# cpp_redis set(CPP_REDIS_INCLUDES ${PROJECT_SOURCE_DIR}/includes) +# tacopie +set(TACOPIE_FOLDER ${PROJECT_SOURCE_DIR}/tacopie) +set(TACOPIE_INCLUDES ${TACOPIE_FOLDER}/includes) ### # includes ### -include_directories(${CPP_REDIS_INCLUDES}) +include_directories(${CPP_REDIS_INCLUDES} + ${TACOPIE_INCLUDES}) ### @@ -90,29 +96,29 @@ configure_file("cpp_redis.pc.in" "${CMAKE_PKGCONFIG_OUTPUT_DIRECTORY}/cpp_redis. add_library(${PROJECT} STATIC ${SOURCES}) IF (WIN32) - target_link_libraries(${PROJECT} ws2_32) + target_link_libraries(${PROJECT} ws2_32 tacopie) ELSE () - target_link_libraries(${PROJECT} pthread) + target_link_libraries(${PROJECT} pthread tacopie) ENDIF (WIN32) # __CPP_REDIS_READ_SIZE IF (READ_SIZE) -set_target_properties(${PROJECT} PROPERTIES COMPILE_DEFINITIONS "__CPP_REDIS_READ_SIZE=${READ_SIZE}") + set_target_properties(${PROJECT} PROPERTIES COMPILE_DEFINITIONS "__CPP_REDIS_READ_SIZE=${READ_SIZE}") ENDIF (READ_SIZE) # __CPP_REDIS_LOGGING_ENABLED IF (LOGGING_ENABLED) -set_target_properties(${PROJECT} PROPERTIES COMPILE_DEFINITIONS "__CPP_REDIS_LOGGING_ENABLED=${LOGGING_ENABLED}") + set_target_properties(${PROJECT} PROPERTIES COMPILE_DEFINITIONS "__CPP_REDIS_LOGGING_ENABLED=${LOGGING_ENABLED}") ENDIF (LOGGING_ENABLED) # _CPP_REDIS_MAX_NB_FDS IF (MAX_NB_FDS) -set_target_properties(${PROJECT} PROPERTIES COMPILE_DEFINITIONS "_CPP_REDIS_MAX_NB_FDS=${MAX_NB_FDS}") + set_target_properties(${PROJECT} PROPERTIES COMPILE_DEFINITIONS "_CPP_REDIS_MAX_NB_FDS=${MAX_NB_FDS}") ENDIF (MAX_NB_FDS) # __CPP_REDIS_DEFAULT_NB_IO_SERVICE_WORKERS IF (DEFAULT_NB_IO_SERVICE_WORKERS) -set_target_properties(${PROJECT} PROPERTIES COMPILE_DEFINITIONS "__CPP_REDIS_DEFAULT_NB_IO_SERVICE_WORKERS=${DEFAULT_NB_IO_SERVICE_WORKERS}") + set_target_properties(${PROJECT} PROPERTIES COMPILE_DEFINITIONS "__CPP_REDIS_DEFAULT_NB_IO_SERVICE_WORKERS=${DEFAULT_NB_IO_SERVICE_WORKERS}") ENDIF (DEFAULT_NB_IO_SERVICE_WORKERS) @@ -128,6 +134,12 @@ install (DIRECTORY ${CMAKE_BINARY_DIR}/bin/ DESTINATION bin USE_SOURCE_PERMISSIO install (DIRECTORY ${CPP_REDIS_INCLUDES}/ DESTINATION include USE_SOURCE_PERMISSIONS) +### +# tacopie +### +add_subdirectory(tacopie) + + ### # examples ### diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 21356641..8e463ee9 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -38,17 +38,17 @@ include_directories(${PROJECT_SOURCE_DIR}/includes ### # executable ### -add_executable(redis_client redis_client.cpp) -target_link_libraries(redis_client cpp_redis) +add_executable(cpp_redis_client redis_client.cpp) +target_link_libraries(cpp_redis_client cpp_redis) -add_executable(future_client future_client.cpp) -target_link_libraries(future_client cpp_redis) +add_executable(cpp_redis_future_client future_client.cpp) +target_link_libraries(cpp_redis_future_client cpp_redis) -add_executable(sync_client sync_client.cpp) -target_link_libraries(sync_client cpp_redis) +add_executable(cpp_redis_sync_client sync_client.cpp) +target_link_libraries(cpp_redis_sync_client cpp_redis) -add_executable(redis_subscriber redis_subscriber.cpp) -target_link_libraries(redis_subscriber cpp_redis) +add_executable(cpp_redis_subscriber redis_subscriber.cpp) +target_link_libraries(cpp_redis_subscriber cpp_redis) -add_executable(logger logger.cpp) -target_link_libraries(logger cpp_redis) +add_executable(cpp_redis_logger logger.cpp) +target_link_libraries(cpp_redis_logger cpp_redis) diff --git a/includes/cpp_redis/future_client.hpp b/includes/cpp_redis/future_client.hpp index a59c4f83..f9720a7d 100644 --- a/includes/cpp_redis/future_client.hpp +++ b/includes/cpp_redis/future_client.hpp @@ -58,8 +58,8 @@ class future_client { public: //! ctor & dtor - future_client(const std::shared_ptr& IO = nullptr); - ~future_client(void); + future_client(void) = default; + ~future_client(void) = default; void connect(const std::string& host = "127.0.0.1", std::size_t port = 6379, diff --git a/includes/cpp_redis/network/io_service.hpp b/includes/cpp_redis/network/io_service.hpp deleted file mode 100644 index 04c1f3cc..00000000 --- a/includes/cpp_redis/network/io_service.hpp +++ /dev/null @@ -1,93 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2015-2017 Simon Ninon -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include -#include -#include -#include - -#include - -#ifndef __CPP_REDIS_DEFAULT_NB_IO_SERVICE_WORKERS -#define __CPP_REDIS_DEFAULT_NB_IO_SERVICE_WORKERS 16 -#endif /* __CPP_REDIS_DEFAULT_NB_IO_SERVICE_WORKERS */ - -namespace cpp_redis { - -namespace network { - -class io_service { -public: - //! get default global instance - static const std::shared_ptr& get_global_instance(void); - //! set default global instance - static void set_global_instance(const std::shared_ptr& instance); - -public: - //! ctor & dtor - io_service(std::size_t nb_workers); - virtual ~io_service(void) = default; - - //! copy ctor & assignment operator - io_service(const io_service&) = default; - io_service& operator=(const io_service&) = default; - -public: - //! disconnection handler declaration - typedef std::function disconnection_handler_t; - - //! add or remove a given fd from the io service - //! untrack should never be called from inside a callback - virtual void track(_sock_t sock, const disconnection_handler_t& handler) = 0; - virtual void untrack(_sock_t sock) = 0; - - //! asynchronously read read_size bytes and append them to the given buffer - //! on completion, call the read_callback to notify of the success or failure of the operation - //! return false if another async_read operation is in progress or fd is not registered - typedef std::function read_callback_t; - virtual bool async_read(_sock_t sock, std::vector& buffer, std::size_t read_size, const read_callback_t& callback) = 0; - - //! asynchronously write write_size bytes from buffer to the specified fd - //! on completion, call the write_callback to notify of the success or failure of the operation - //! return false if another async_write operation is in progress or fd is not registered - typedef std::function write_callback_t; - virtual bool async_write(_sock_t sock, const std::vector& buffer, std::size_t write_size, const write_callback_t& callback) = 0; - -public: - std::size_t get_nb_workers(void) const; - -private: - //! listen for incoming events and notify - virtual void process_io(void) = 0; - -private: - std::size_t m_nb_workers; -}; - -//! multi-platform instance builder -std::shared_ptr create_io_service(std::size_t nb_workers = __CPP_REDIS_DEFAULT_NB_IO_SERVICE_WORKERS); - -} //! network - -} //! cpp_redis diff --git a/includes/cpp_redis/network/redis_connection.hpp b/includes/cpp_redis/network/redis_connection.hpp index a4036b47..e3aa1d5b 100644 --- a/includes/cpp_redis/network/redis_connection.hpp +++ b/includes/cpp_redis/network/redis_connection.hpp @@ -28,7 +28,12 @@ #include #include -#include + +#include + +#ifndef __CPP_REDIS_READ_SIZE +#define __CPP_REDIS_READ_SIZE 4096 +#endif /* __CPP_REDIS_READ_SIZE */ namespace cpp_redis { @@ -37,7 +42,7 @@ namespace network { class redis_connection { public: //! ctor & dtor - redis_connection(const std::shared_ptr& IO); + redis_connection(void); ~redis_connection(void); //! copy ctor & assignment operator @@ -62,14 +67,17 @@ class redis_connection { private: //! receive & disconnection handlers - bool tcp_client_receive_handler(network::tcp_client&, const std::vector& buffer); - void tcp_client_disconnection_handler(network::tcp_client&); + void tcp_client_receive_handler(const tacopie::tcp_client::read_result& result); + void tcp_client_disconnection_handler(void); std::string build_command(const std::vector& redis_cmd); +private: + void call_disconnection_handler(void); + private: //! tcp client for redis connection - network::tcp_client m_client; + tacopie::tcp_client m_client; //! reply callback reply_callback_t m_reply_callback; diff --git a/includes/cpp_redis/network/socket.hpp b/includes/cpp_redis/network/socket.hpp deleted file mode 100644 index 7c97bc21..00000000 --- a/includes/cpp_redis/network/socket.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2015-2017 Simon Ninon -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -namespace cpp_redis { - -namespace network { - -#ifdef _WIN32 -#include -typedef SOCKET _sock_t; -#else -typedef int _sock_t; -#endif /* _WIN32 */ - -#ifndef INVALID_SOCKET -#define INVALID_SOCKET -1 -#endif /* INVALID_SOCKET */ - -} //! network - -} //! cpp_redis diff --git a/includes/cpp_redis/network/tcp_client.hpp b/includes/cpp_redis/network/tcp_client.hpp deleted file mode 100644 index 6f4eff22..00000000 --- a/includes/cpp_redis/network/tcp_client.hpp +++ /dev/null @@ -1,109 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2015-2017 Simon Ninon -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#ifndef __CPP_REDIS_READ_SIZE -#define __CPP_REDIS_READ_SIZE 4096 -#endif /* __CPP_REDIS_READ_SIZE */ - -namespace cpp_redis { - -namespace network { - -//! tcp_client -//! async tcp client based on boost asio -class tcp_client { -public: - //! ctor & dtor - tcp_client(const std::shared_ptr& IO = nullptr); - ~tcp_client(void); - - //! assignment operator & copy ctor - tcp_client(const tcp_client&) = delete; - tcp_client& operator=(const tcp_client&) = delete; - - //! returns whether the client is connected or not - bool is_connected(void); - - //! handle connection & disconnection - typedef std::function disconnection_handler_t; - typedef std::function& buffer)> receive_handler_t; - void connect(const std::string& host, std::size_t port, - const disconnection_handler_t& disconnection_handler = nullptr, - const receive_handler_t& receive_handler = nullptr); - void disconnect(void); - - //! send data - void send(const std::string& buffer); - void send(const std::vector& buffer); - -private: - //! make boost asio async read and write operations - void async_read(void); - void async_write(void); - - //! io service callback - void io_service_disconnection_handler(io_service&); - - void reset_state(void); - void clear_buffer(void); - - void setup_socket(bool is_unix_socket); - -private: - //! io service instance - std::shared_ptr m_io_service; - - //! socket fd - _sock_t m_sock; - - //! is connected - std::atomic_bool m_is_connected; - - //! buffers - std::vector m_read_buffer; - std::list> m_write_buffer; - - //! handlers - receive_handler_t m_receive_handler; - disconnection_handler_t m_disconnection_handler; - - //! thread safety - std::mutex m_write_buffer_mutex; -}; - -} //! network - -} //! cpp_redis diff --git a/includes/cpp_redis/network/unix_impl/io_service.hpp b/includes/cpp_redis/network/unix_impl/io_service.hpp deleted file mode 100644 index af9f7841..00000000 --- a/includes/cpp_redis/network/unix_impl/io_service.hpp +++ /dev/null @@ -1,125 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2015-2017 Simon Ninon -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#ifndef _CPP_REDIS_MAX_NB_FDS -#define _CPP_REDIS_MAX_NB_FDS 1024 -#endif /* _CPP_REDIS_MAX_NB_FDS */ - -namespace cpp_redis { - -namespace network { - -namespace unix_impl { - -class io_service : public network::io_service { -public: - //! ctor & dtor - io_service(std::size_t nb_workers); - ~io_service(void); - - //! copy ctor & assignment operator - io_service(const io_service&) = delete; - io_service& operator=(const io_service&) = delete; - -public: - void track(_sock_t fd, const disconnection_handler_t& handler) override; - void untrack(_sock_t fd) override; - - bool async_read(_sock_t fd, std::vector& buffer, std::size_t read_size, const read_callback_t& callback) override; - bool async_write(_sock_t fd, const std::vector& buffer, std::size_t write_size, const write_callback_t& callback) override; - -private: - //! simple struct to keep track of ongoing operations on a given fd - struct fd_info { - disconnection_handler_t disconnection_handler; - - std::atomic_bool async_read; - std::vector* read_buffer; - std::size_t read_size; - read_callback_t read_callback; - - std::atomic_bool async_write; - std::vector write_buffer; - std::size_t write_size; - write_callback_t write_callback; - - std::atomic_bool callback_running; - std::condition_variable_any callback_notification; - }; - -private: - //! listen for incoming events and notify - void process_io(void) override; - - //! notify the poll call so that it can wake up to process new events - void notify_poll(void); - -private: - //! poll fds sets handling (init, rd/wr handling) - std::size_t init_sets(struct pollfd* fds); - void process_sets(struct pollfd* fds, std::size_t nfds); - - void read_fd(int fd); - void write_fd(int fd); - -private: - //! whether the worker should terminate or not - std::atomic_bool m_should_stop; - - //! worker in the background, listening for events - std::thread m_worker; - - //! tracked fds - std::unordered_map m_fds; - - //! fd associated to the pipe used to wake up the poll call - int m_notif_pipe_fds[2]; - - //! mutex to protect m_fds access against race condition - //! - //! specific mutex for untrack: we dont want someone to untrack a fd while we process it - //! this behavior could cause some issues when executing callbacks in another thread - //! for example, obj is destroyed, in its dtor it untracks the fd, but at the same time - //! a callback is executed from within another thread: the untrack mutex avoid this without being costly - std::recursive_mutex m_fds_mutex; -}; - -} //! unix_impl - -} //! network - -} //! cpp_redis diff --git a/includes/cpp_redis/network/windows_impl/io_service.hpp b/includes/cpp_redis/network/windows_impl/io_service.hpp deleted file mode 100644 index 1a041abf..00000000 --- a/includes/cpp_redis/network/windows_impl/io_service.hpp +++ /dev/null @@ -1,160 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2015-2017 Simon Ninon -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include -#include -#include -#include -#include - -#include - -#include - -namespace cpp_redis { - -namespace network { - -namespace windows_impl { - -typedef enum _enIoOperation { - //IO_OP_ACCEPT, - IO_OP_READ, - IO_OP_WRITE -} enIoOperation; - -class io_service : public network::io_service { -public: - //! ctor & dtor - io_service(std::size_t nb_workers); - ~io_service(void); - -private: - //! copy ctor & assignment operator - io_service(const io_service&) = delete; - io_service& operator=(const io_service&) = delete; - -public: - void track(_sock_t sock, const disconnection_handler_t& handler) override; - void untrack(_sock_t sock) override; - - bool async_read(_sock_t socket, std::vector& buffer, std::size_t read_size, const read_callback_t& callback) override; - bool async_write(_sock_t socket, const std::vector& buffer, std::size_t write_size, const write_callback_t& callback) override; - -private: - //! wait for incoming events and notify - void process_io(void) override; - - //! shutdown - void shutdown(void); - -private: - struct io_context_info : OVERLAPPED { - WSAOVERLAPPED overlapped; - enIoOperation eOperation; - }; - -private: - //! simple struct to keep track of ongoing operations on a given sockeet - class sock_info { - public: - sock_info(void) = default; - virtual ~sock_info(void) { - std::lock_guard socklock(sock_info_mutex); - for (auto it = io_contexts_pool.begin(); it != io_contexts_pool.end(); it++) - delete *it; - - io_contexts_pool.clear(); - } - - SOCKET hsock; - std::size_t sent_bytes; - - //! Must protect the members of our structure from access by multiple threads during IO Completion - std::recursive_mutex sock_info_mutex; - - //! We keep a simple vector of io_context_info structs to reuse for overlapped WSARecv and WSASend operations - //! Since each must have its OWN struct if we issue them at the same time. - //! othewise things get tangled up and borked. - std::vector io_contexts_pool; - - disconnection_handler_t disconnection_handler; - - std::vector* read_buffer; - read_callback_t read_callback; - - std::vector write_buffer; - std::size_t write_size; - write_callback_t write_callback; - - io_context_info* - get_pool_io_context() { - io_context_info* pInfo = NULL; - std::lock_guard socklock(sock_info_mutex); - if (!io_contexts_pool.empty()) { - pInfo = io_contexts_pool.back(); - io_contexts_pool.pop_back(); - } - if (!pInfo) - pInfo = new io_context_info(); - //MUST clear the overlapped structure between IO calls! - memset(&pInfo->overlapped, 0, sizeof(OVERLAPPED)); - return pInfo; - } - - void - return_pool_io_context(io_context_info* p_io) { - std::lock_guard socklock(sock_info_mutex); - io_contexts_pool.push_back(p_io); - } - }; - -private: - //! completion port - HANDLE m_completion_port; - - //! vector containing all the threads we start to service our i/o requests - std::vector m_worker_threads; - -private: - //! whether the worker should terminate or not - std::atomic_bool m_should_stop; - - //! tracked sockets - std::unordered_map m_sockets; - - //! mutex to protect m_notify_socket access against race condition - //! - //! specific mutex for untrack: we dont want someone to untrack a socket while we process it - //! this behavior could cause some issues when executing callbacks in another thread - //! for example, obj is destroyed, in its dtor it untracks the socket, but at the same time - //! a callback is executed from within another thread: the untrack mutex avoid this without being costly - std::recursive_mutex m_socket_mutex; -}; - -} //! windows_impl - -} //! network - -} //! cpp_redis diff --git a/includes/cpp_redis/redis_client.hpp b/includes/cpp_redis/redis_client.hpp index fd7bdad0..732d8e69 100644 --- a/includes/cpp_redis/redis_client.hpp +++ b/includes/cpp_redis/redis_client.hpp @@ -37,7 +37,7 @@ namespace cpp_redis { class redis_client { public: //! ctor & dtor - redis_client(const std::shared_ptr& IO = nullptr); + redis_client(void); ~redis_client(void); //! copy ctor & assignment operator diff --git a/includes/cpp_redis/redis_subscriber.hpp b/includes/cpp_redis/redis_subscriber.hpp index fdb52874..191b70a1 100644 --- a/includes/cpp_redis/redis_subscriber.hpp +++ b/includes/cpp_redis/redis_subscriber.hpp @@ -34,7 +34,7 @@ namespace cpp_redis { class redis_subscriber { public: //! ctor & dtor - redis_subscriber(const std::shared_ptr& IO = nullptr); + redis_subscriber(void); ~redis_subscriber(void); //! copy ctor & assignment operator diff --git a/includes/cpp_redis/sync_client.hpp b/includes/cpp_redis/sync_client.hpp index 282561c5..d7daf3f1 100644 --- a/includes/cpp_redis/sync_client.hpp +++ b/includes/cpp_redis/sync_client.hpp @@ -35,8 +35,8 @@ class sync_client { public: //! ctor & dtor - sync_client(const std::shared_ptr& IO = nullptr); - ~sync_client(void); + sync_client(void) = default; + ~sync_client(void) = default; void connect(const std::string& host = "127.0.0.1", std::size_t port = 6379, diff --git a/sources/future_client.cpp b/sources/future_client.cpp deleted file mode 100644 index 5ad24318..00000000 --- a/sources/future_client.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2015-2017 Simon Ninon -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include - -namespace cpp_redis { - -future_client::future_client(const std::shared_ptr& io_service) -: m_client(io_service) { - __CPP_REDIS_LOG(debug, "cpp_redis::future_client created"); -} - -future_client::~future_client(void) { - __CPP_REDIS_LOG(debug, "cpp_redis::future_client destroyed"); -} - - -} //! cpp_redis diff --git a/sources/network/io_service.cpp b/sources/network/io_service.cpp deleted file mode 100644 index 5f595a53..00000000 --- a/sources/network/io_service.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2015-2017 Simon Ninon -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include - -#ifdef _WIN32 -#include -#else -#include -#endif /* _WIN32 */ - -namespace cpp_redis { - -namespace network { - -static std::shared_ptr global_instance = nullptr; - -const std::shared_ptr& -io_service::get_global_instance(void) { - if (!global_instance) - global_instance = create_io_service(); - - return global_instance; -} - -void -io_service::set_global_instance(const std::shared_ptr& io_service) { - global_instance = io_service; -} - -io_service::io_service(std::size_t nb_workers) -: m_nb_workers(nb_workers) {} - -std::size_t -io_service::get_nb_workers(void) const { - return m_nb_workers; -} - -std::shared_ptr -create_io_service(std::size_t nb_workers) { -#ifdef _WIN32 - return std::make_shared(nb_workers); -#else - return std::make_shared(nb_workers); -#endif /* _WIN32 */ -} - -} //! network - -} //! cpp_redis diff --git a/sources/network/redis_connection.cpp b/sources/network/redis_connection.cpp index 43cd7d33..58efdaf9 100644 --- a/sources/network/redis_connection.cpp +++ b/sources/network/redis_connection.cpp @@ -22,14 +22,14 @@ #include #include +#include namespace cpp_redis { namespace network { -redis_connection::redis_connection(const std::shared_ptr& io_service) -: m_client(io_service) -, m_reply_callback(nullptr) +redis_connection::redis_connection(void) +: m_reply_callback(nullptr) , m_disconnection_handler(nullptr) { __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection created"); } @@ -45,9 +45,16 @@ redis_connection::connect(const std::string& host, std::size_t port, const reply_callback_t& client_reply_callback) { __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection attempts to connect"); - auto disconnection_handler = std::bind(&redis_connection::tcp_client_disconnection_handler, this, std::placeholders::_1); - auto receive_handler = std::bind(&redis_connection::tcp_client_receive_handler, this, std::placeholders::_1, std::placeholders::_2); - m_client.connect(host, port, disconnection_handler, receive_handler); + //! connect client and start to read asynchronously + m_client.set_on_disconnection_handler(std::bind(&redis_connection::tcp_client_disconnection_handler, this)); + try { + m_client.connect(host, port); + } + catch (const tacopie::tacopie_error& e) { + __CPP_REDIS_LOG(error, std::string("cpp_redis::network::redis_connection ") + e.what()); + throw redis_error(e.what()); + } + m_client.async_read({__CPP_REDIS_READ_SIZE, std::bind(&redis_connection::tcp_client_receive_handler, this, std::placeholders::_1)}); __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection connected"); @@ -95,27 +102,44 @@ redis_connection::commit(void) { //! ensure buffer is cleared __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection attempts to send pipelined commands"); std::string buffer = std::move(m_buffer); - m_client.send(buffer); + + try { + m_client.async_write({std::vector{buffer.begin(), buffer.end()}, nullptr}); + } + catch (const tacopie::tacopie_error& e) { + __CPP_REDIS_LOG(error, std::string("cpp_redis::network::redis_connection ") + e.what()); + throw redis_error(e.what()); + } + __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection sent pipelined commands"); return *this; } -bool -redis_connection::tcp_client_receive_handler(network::tcp_client&, const std::vector& buffer) { +void +redis_connection::call_disconnection_handler(void) { + if (m_disconnection_handler) { + __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection calls disconnection handler"); + m_disconnection_handler(*this); + } +} + +void +redis_connection::tcp_client_receive_handler(const tacopie::tcp_client::read_result& result) { + if (!result.success) { + __CPP_REDIS_LOG(error, "cpp_redis::network::redis_connection unsuccessful async_read, disconnecting"); + call_disconnection_handler(); + return; + } + try { __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection receives packet, attempts to build reply"); - m_builder << std::string(buffer.begin(), buffer.end()); + m_builder << std::string(result.buffer.begin(), result.buffer.end()); } catch (const redis_error&) { __CPP_REDIS_LOG(error, "cpp_redis::network::redis_connection could not build reply (invalid format), disconnecting"); - - if (m_disconnection_handler) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection calls disconnection handler"); - m_disconnection_handler(*this); - } - - return false; + call_disconnection_handler(); + return; } while (m_builder.reply_available()) { @@ -130,11 +154,11 @@ redis_connection::tcp_client_receive_handler(network::tcp_client&, const std::ve } } - return true; + m_client.async_read({__CPP_REDIS_READ_SIZE, std::bind(&redis_connection::tcp_client_receive_handler, this, std::placeholders::_1)}); } void -redis_connection::tcp_client_disconnection_handler(network::tcp_client&) { +redis_connection::tcp_client_disconnection_handler(void) { __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection has been disconnected"); if (m_disconnection_handler) { diff --git a/sources/network/tcp_client.cpp b/sources/network/tcp_client.cpp deleted file mode 100644 index 4608acba..00000000 --- a/sources/network/tcp_client.cpp +++ /dev/null @@ -1,313 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2015-2017 Simon Ninon -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include - -//! Disable "The POSIX name for this item is deprecated" warnings for gethostbyname() -#ifdef _WIN32 -#pragma warning(disable : 4996) -#endif /* _WIN32 */ -#include - -#ifdef _WIN32 -#include -#else -#include -#include -#include -#endif /* _WIN32 */ - -#include -#include - -namespace cpp_redis { - -namespace network { - -tcp_client::tcp_client(const std::shared_ptr& io_service) -: m_io_service(io_service ? io_service : io_service::get_global_instance()) -, m_sock(-1) -, m_is_connected(false) -, m_receive_handler(nullptr) -, m_disconnection_handler(nullptr) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::tcp_client created"); -} - -tcp_client::~tcp_client(void) { - disconnect(); - __CPP_REDIS_LOG(debug, "cpp_redis::network::tcp_client destroyed"); -} - -void -tcp_client::setup_socket(bool is_unix_socket) { -#ifdef _WIN32 - //! throw an exception if trying to open a unix socket on windows - if (is_unix_socket) { - throw redis_error("Can't create a unix socket on Windows"); - } - - //! create the socket - //! Enable socket for overlapped i/o - m_sock = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED); - if (m_sock < 0) { - __CPP_REDIS_LOG(error, "cpp_redis::network::tcp_client could not create socket"); - throw redis_error("Can't open a socket"); - } - - //! Instruct the TCP stack to directly perform I/O using the buffer provided in our I/O call. - //! The advantage is performance because we save a buffer copy between the TCP stack buffer - //! and our user buffer for each I/O call. - //! BUT we have to make sure we don't access the buffer once it's submitted for overlapped operation and before the overlapped operation completes! - int nZero = 0; - if (setsockopt(m_sock, SOL_SOCKET, SO_SNDBUF, (char*) &nZero, sizeof(nZero))) { - __CPP_REDIS_LOG(warn, "cpp_redis::network::tcp_client could not disable buffering"); - } -#else - //! create the socket - if (is_unix_socket) { - m_sock = socket(AF_UNIX, SOCK_STREAM, 0); - } - else { - m_sock = socket(AF_INET, SOCK_STREAM, 0); - } - - if (m_sock < 0) { - __CPP_REDIS_LOG(error, "cpp_redis::network::tcp_client could not create socket"); - throw redis_error("Can't open a socket"); - } -#endif /* _WIN32 */ -} - -void -tcp_client::connect(const std::string& host, std::size_t port, - const disconnection_handler_t& disconnection_handler, - const receive_handler_t& receive_handler) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::tcp_client attempts to connect"); - - if (m_is_connected) { - __CPP_REDIS_LOG(warn, "cpp_redis::network::tcp_client is already connected"); - return throw cpp_redis::redis_error("Client already connected"); - } - - //! Setup the socket. If the port is 0, try to setup a unix socket. - bool is_unix_socket = (port == 0); - setup_socket(is_unix_socket); - -#ifndef _WIN32 - if (is_unix_socket) { - //! build the unix socket address - struct sockaddr_un server_addr; - std::memset(&server_addr, 0, sizeof(server_addr)); - server_addr.sun_family = AF_UNIX; - strncpy(server_addr.sun_path, host.c_str(), sizeof(server_addr.sun_path) - 1); - - //! create a connection with the server - if (::connect(m_sock, reinterpret_cast(&server_addr), sizeof(server_addr)) < 0) { - __CPP_REDIS_LOG(error, "cpp_redis::network::tcp_client could not connect"); - throw redis_error("Fail to connect unix socket at " + host); - } - } - else { -#endif /* _WIN32 */ - //! get the server's DNS entry - struct hostent* server = gethostbyname(host.c_str()); - if (!server) { - __CPP_REDIS_LOG(error, "cpp_redis::network::tcp_client could not resolve DNS"); - throw redis_error("No such host: " + host); - } - - //! build the server's Internet address - struct sockaddr_in server_addr; - std::memset(&server_addr, 0, sizeof(server_addr)); - std::memcpy(&server_addr.sin_addr.s_addr, server->h_addr, server->h_length); - server_addr.sin_port = htons(port); - server_addr.sin_family = AF_INET; - - //! create a connection with the server - if (::connect(m_sock, reinterpret_cast(&server_addr), sizeof(server_addr)) < 0) { - __CPP_REDIS_LOG(error, "cpp_redis::network::tcp_client could not connect"); - throw redis_error("Fail to connect to " + host + ":" + std::to_string(port)); - } -#ifndef _WIN32 - } -#endif /* _WIN32 */ - -#ifdef _WIN32 - //! Set socket to non blocking. - //! Must only be done once connected - u_long ulValue = 1; - if (ioctlsocket(m_sock, FIONBIO, &ulValue)) { - __CPP_REDIS_LOG(warn, "cpp_redis::network::tcp_client could not enable non-blocking mode on socket"); - } -#endif /* _WIN32 */ - - //! add fd to the io_service and set the disconnection & recv handlers - m_disconnection_handler = disconnection_handler; - m_receive_handler = receive_handler; - m_io_service->track(m_sock, std::bind(&tcp_client::io_service_disconnection_handler, this, std::placeholders::_1)); - m_is_connected = true; - - __CPP_REDIS_LOG(debug, "cpp_redis::network::tcp_client connected"); - - //! start async read - async_read(); -} - -void -tcp_client::disconnect(void) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::tcp_client attemps to disconnect"); - - if (!m_is_connected) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::tcp_client already disconnected"); - return; - } - - m_io_service->untrack(m_sock); - reset_state(); - - __CPP_REDIS_LOG(debug, "cpp_redis::network::tcp_client disconnected"); -} - -void -tcp_client::send(const std::string& buffer) { - send(std::vector{buffer.begin(), buffer.end()}); -} - -void -tcp_client::send(const std::vector& buffer) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::tcp_client attemps to send data"); - - if (!m_is_connected) { - __CPP_REDIS_LOG(error, "cpp_redis::network::tcp_client is not connected"); - throw redis_error("Not connected"); - } - - if (!buffer.size()) { - __CPP_REDIS_LOG(warn, "cpp_redis::network::tcp_client has nothing to send"); - return; - } - - std::lock_guard lock(m_write_buffer_mutex); - - //! concat buffer - m_write_buffer.push_back(buffer); - - //! if there were already bytes in buffer, simply return - //! otherwise async_write calls itself recursively until the write buffer is empty - if (m_write_buffer.size() > 1) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::tcp_client is already processing an async_write"); - return; - } - - async_write(); -} - -void -tcp_client::async_read(void) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::tcp_client starts async_read"); - - m_io_service->async_read(m_sock, m_read_buffer, __CPP_REDIS_READ_SIZE, - [&](std::size_t length) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::tcp_client received data"); - - if (m_receive_handler) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::tcp_client calls receive_handler"); - - if (!m_receive_handler(*this, {m_read_buffer.begin(), m_read_buffer.begin() + length})) { - __CPP_REDIS_LOG(warn, "cpp_redis::network::tcp_client has been asked for disconnection by receive_handler"); - disconnect(); - return; - } - } - - //! clear read buffer keep waiting for incoming bytes - m_read_buffer.clear(); - - if (m_is_connected) - async_read(); - }); -} - -void -tcp_client::async_write(void) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::tcp_client starts async_write"); - - m_io_service->async_write(m_sock, m_write_buffer.front(), m_write_buffer.front().size(), - [&](std::size_t length) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::tcp_client wrote data and cleans write_buffer"); - std::lock_guard lock(m_write_buffer_mutex); - - //! Remove what has already been sent and see if we have any more to send - if (length >= m_write_buffer.front().size()) - m_write_buffer.pop_front(); - else - m_write_buffer.front().erase(m_write_buffer.front().begin(), m_write_buffer.front().begin() + length); - - //! If we still have data to write the call ourselves recursivly until the buffer is completely sent - if (m_is_connected && m_write_buffer.size()) - async_write(); - }); -} - -bool -tcp_client::is_connected(void) { - return m_is_connected; -} - -void -tcp_client::io_service_disconnection_handler(network::io_service&) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::tcp_client has been disconnected"); - - reset_state(); - - if (m_disconnection_handler) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::tcp_client calls disconnection handler"); - m_disconnection_handler(*this); - } -} - -void -tcp_client::reset_state(void) { - m_is_connected = false; - - if (m_sock != INVALID_SOCKET) { -#ifdef _WIN32 - closesocket(m_sock); -#else - close(m_sock); -#endif /* _WIN32 */ - m_sock = INVALID_SOCKET; - } - - clear_buffer(); -} - -void -tcp_client::clear_buffer(void) { - std::lock_guard lock(m_write_buffer_mutex); - m_write_buffer.clear(); - m_read_buffer.clear(); -} - -} //! network - -} //! cpp_redis diff --git a/sources/network/unix_impl/io_service.cpp b/sources/network/unix_impl/io_service.cpp deleted file mode 100644 index 1dbdfb5f..00000000 --- a/sources/network/unix_impl/io_service.cpp +++ /dev/null @@ -1,316 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2015-2017 Simon Ninon -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include - -#include -#include -#include - -namespace cpp_redis { - -namespace network { - -namespace unix_impl { - -io_service::io_service(std::size_t nb_workers) -: network::io_service(nb_workers) -, m_should_stop(false) -, m_notif_pipe_fds{1, 1} { - if (pipe(m_notif_pipe_fds) == -1) { - __CPP_REDIS_LOG(error, "cpp_redis::network::io_service could not create pipe"); - throw cpp_redis::redis_error("Could not init cpp_redis::io_service, pipe() failure"); - } - - int flags = fcntl(m_notif_pipe_fds[1], F_GETFL, 0); - if (flags == -1 || fcntl(m_notif_pipe_fds[1], F_SETFL, flags | O_NONBLOCK) == -1) { - __CPP_REDIS_LOG(error, "cpp_redis::network::io_service could not configure pipe"); - throw cpp_redis::redis_error("Could not init cpp_redis::io_service, fcntl() failure"); - } - - m_worker = std::thread(&io_service::process_io, this); - - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service created"); -} - -io_service::~io_service(void) { - m_should_stop = true; - notify_poll(); - - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service waiting for worker completion"); - m_worker.join(); - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service finished to wait for worker completion"); - - if (m_notif_pipe_fds[0] != -1) - close(m_notif_pipe_fds[0]); - if (m_notif_pipe_fds[1] != -1) - close(m_notif_pipe_fds[1]); - - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service destroyed"); -} - -std::size_t -io_service::init_sets(struct pollfd* fds) { - fds[0].fd = m_notif_pipe_fds[0]; - fds[0].events = POLLIN; - - std::lock_guard lock(m_fds_mutex); - std::size_t nfds = 1; - for (const auto& fd : m_fds) { - fds[nfds].fd = fd.first; - fds[nfds].events = 0; - - if (fd.second.async_read) - fds[nfds].events |= POLLIN; - - if (fd.second.async_write) - fds[nfds].events |= POLLOUT; - - ++nfds; - } - - return nfds; -} - -void -io_service::read_fd(int fd) { - std::unique_lock lock(m_fds_mutex); - - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service non-blocking read available for fd #" + std::to_string(fd)); - - auto fd_it = m_fds.find(fd); - if (fd_it == m_fds.end()) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service does not track fd #" + std::to_string(fd)); - return; - } - - auto& buffer = *fd_it->second.read_buffer; - int original_buffer_size = static_cast(buffer.size()); - buffer.resize(original_buffer_size + fd_it->second.read_size); - - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service reading data for fd #" + std::to_string(fd)); - int nb_bytes_read = static_cast(recv(fd_it->first, buffer.data() + original_buffer_size, fd_it->second.read_size, 0)); - fd_it->second.async_read = false; - - if (nb_bytes_read <= 0) { - __CPP_REDIS_LOG(error, "cpp_redis::network::io_service read error for fd #" + std::to_string(fd)); - buffer.resize(original_buffer_size); - fd_it->second.disconnection_handler(*this); - m_fds.erase(fd_it); - } - else { - buffer.resize(original_buffer_size + nb_bytes_read); - - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service calling read callback for fd #" + std::to_string(fd)); - - fd_it->second.callback_running = true; - lock.unlock(); - fd_it->second.read_callback(nb_bytes_read); - fd_it->second.callback_running = false; - fd_it->second.callback_notification.notify_all(); - } -} - -void -io_service::write_fd(int fd) { - std::unique_lock lock(m_fds_mutex); - - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service non-blocking write available for fd #" + std::to_string(fd)); - - auto fd_it = m_fds.find(fd); - if (fd_it == m_fds.end()) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service does not track fd #" + std::to_string(fd)); - return; - } - - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service writing data for fd #" + std::to_string(fd)); - int nb_bytes_written = static_cast(send(fd_it->first, fd_it->second.write_buffer.data(), fd_it->second.write_size, 0)); - fd_it->second.async_write = false; - - if (nb_bytes_written <= 0) { - __CPP_REDIS_LOG(error, "cpp_redis::network::io_service write error for fd #" + std::to_string(fd)); - fd_it->second.disconnection_handler(*this); - m_fds.erase(fd_it); - } - else { - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service calling write callback for fd #" + std::to_string(fd)); - - fd_it->second.callback_running = true; - lock.unlock(); - fd_it->second.write_callback(nb_bytes_written); - fd_it->second.callback_running = false; - fd_it->second.callback_notification.notify_all(); - } -} - -void -io_service::process_sets(struct pollfd* fds, std::size_t nfds) { - std::vector fds_to_read; - std::vector fds_to_write; - - //! Quickly fetch fds that are readable/writeable - //! This reduce lock time and avoid possible deadlock in callbacks - { - std::lock_guard lock(m_fds_mutex); - - for (std::size_t i = 0; i < nfds; ++i) { - if (fds[i].revents & POLLIN && m_fds[fds[i].fd].async_read) - fds_to_read.push_back(fds[i].fd); - - if (fds[i].revents & POLLOUT && m_fds[fds[i].fd].async_write) - fds_to_write.push_back(fds[i].fd); - } - } - - for (int fd : fds_to_read) { read_fd(fd); } - for (int fd : fds_to_write) { write_fd(fd); } - - if (fds[0].revents & POLLIN) { - char buf[1024]; - (void) read(m_notif_pipe_fds[0], buf, 1024); - } -} - -void -io_service::process_io(void) { - struct pollfd fds[_CPP_REDIS_MAX_NB_FDS]; - - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service starts poll loop in worker thread"); - - while (!m_should_stop) { - std::size_t nfds = init_sets(fds); - - if (poll(fds, static_cast(nfds), -1) > 0) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service woke up by poll"); - process_sets(fds, nfds); - } - else { - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service woke up by poll, but nothing to process"); - } - } - - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service ends poll loop in worker thread"); -} - -void -io_service::track(_sock_t fd, const disconnection_handler_t& handler) { - std::lock_guard lock(m_fds_mutex); - - auto& info = m_fds[fd]; - info.async_read = false; - info.async_write = false; - info.disconnection_handler = handler; - - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service now tracks fd #" + std::to_string(fd)); - - notify_poll(); -} - -void -io_service::untrack(_sock_t fd) { - std::unique_lock lock(m_fds_mutex); - - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service requests to untrack fd #" + std::to_string(fd)); - - auto fd_it = m_fds.find(fd); - if (fd_it == m_fds.end()) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service does not track fd #" + std::to_string(fd)); - return; - } - - if (fd_it->second.callback_running) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service waits for callbacks to complete before untracking fd #" + std::to_string(fd)); - fd_it->second.callback_notification.wait(lock, [=] { return !fd_it->second.callback_running; }); - } - - m_fds.erase(fd_it); - - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service now untracks fd #" + std::to_string(fd)); -} - -bool -io_service::async_read(_sock_t fd, std::vector& buffer, std::size_t read_size, const read_callback_t& callback) { - std::lock_guard lock(m_fds_mutex); - - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service is requested async_read for fd #" + std::to_string(fd)); - - auto reg_fd_it = m_fds.find(fd); - if (reg_fd_it == m_fds.end()) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service does not track fd #" + std::to_string(fd)); - return false; - } - - auto& reg_fd = reg_fd_it->second; - bool expected = false; - if (!reg_fd.async_read.compare_exchange_strong(expected, true)) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service already doing async_read for fd #" + std::to_string(fd)); - return false; - } - - reg_fd.read_buffer = &buffer; - reg_fd.read_size = read_size; - reg_fd.read_callback = callback; - - notify_poll(); - - return true; -} - -bool -io_service::async_write(_sock_t fd, const std::vector& buffer, std::size_t write_size, const write_callback_t& callback) { - std::lock_guard lock(m_fds_mutex); - - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service is requested async_write for fd #" + std::to_string(fd)); - - auto reg_fd_it = m_fds.find(fd); - if (reg_fd_it == m_fds.end()) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service does not track fd #" + std::to_string(fd)); - return false; - } - - auto& reg_fd = reg_fd_it->second; - bool expected = false; - if (!reg_fd.async_write.compare_exchange_strong(expected, true)) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service already doing async_write for fd #" + std::to_string(fd)); - return false; - } - - reg_fd.write_buffer = buffer; - reg_fd.write_size = write_size; - reg_fd.write_callback = callback; - - notify_poll(); - - return true; -} - -void -io_service::notify_poll(void) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::io_service notifies poll to wake up"); - (void) write(m_notif_pipe_fds[1], "a", 1); -} - -} //! unix_impl - -} //! network - -} //! cpp_redis diff --git a/sources/network/windows_impl/io_service.cpp b/sources/network/windows_impl/io_service.cpp deleted file mode 100644 index 97a66f92..00000000 --- a/sources/network/windows_impl/io_service.cpp +++ /dev/null @@ -1,280 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2015-2017 Simon Ninon -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include - -namespace cpp_redis { - -namespace network { - -namespace windows_impl { - -io_service::io_service(std::size_t nb_workers) -: network::io_service(nb_workers) -, m_should_stop(false) { - //! Start winsock before any other socket calls. - WSADATA wsaData; - int nRet = WSAStartup(0x202, &wsaData); - if (nRet) - throw cpp_redis::redis_error("Could not init cpp_redis::io_service, WSAStartup() failure"); - - //Create completion port. Pass 0 for parameter 4 to allow as many threads as there are processors in the system - m_completion_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); - if (m_completion_port == INVALID_HANDLE_VALUE) - throw cpp_redis::redis_error("Could not init cpp_redis::io_service, CreateIoCompletionPort() failure"); - - //! Now startup worker thread pool which will service our async io requests - for (std::size_t i = 0; i < get_nb_workers(); ++i) - m_worker_threads.push_back(std::thread(&io_service::process_io, this)); -} - -io_service::~io_service(void) { - shutdown(); -} - -void -io_service::shutdown() { - m_should_stop = true; - - //! Post for each of our worker threads. - for (std::size_t i = 0; i < get_nb_workers(); i++) { - if (m_sockets.size() > 0) { - //! Iterate all of our sockets and shutdown any IO worker threads by posting a issuing a special - //! message to the thread to tell them to wake up and shut down. - for (const auto& sock : m_sockets) { - //! Use nullptr for the completion key to wake them up. - PostQueuedCompletionStatus(m_completion_port, 0, NULL, NULL); - } - } - else { - //! Or just signal once in case socket map is empty to avoid deadlock on shutdown. - PostQueuedCompletionStatus(m_completion_port, 0, NULL, NULL); - } - } - - //! Wait for the threads to finish - for (auto& worker : m_worker_threads) - worker.join(); - - //! close the completion port otherwise the worker threads will all be waiting on GetQueuedCompletionStatus() - if (m_completion_port) { - CloseHandle(m_completion_port); - m_completion_port = nullptr; - } -} - -//! add or remove a given socket from the io service -//! untrack should never be called from inside a callback -void -io_service::track(SOCKET sock, const disconnection_handler_t& handler) { - std::lock_guard lock(m_socket_mutex); - - //Add the socket to our map and return the allocated struct - auto& info = m_sockets[sock]; - - info.hsock = sock; - info.disconnection_handler = handler; - - //Associate the socket with our io completion port. - if (NULL == CreateIoCompletionPort((HANDLE) sock, m_completion_port, (DWORD_PTR) &m_sockets[sock], 0)) { - throw cpp_redis::redis_error("Track() failed to create CreateIoCompletionPort"); - } -} - -void -io_service::untrack(SOCKET sock) { - std::lock_guard lock(m_socket_mutex); - m_sockets.erase(sock); -} - -bool -io_service::async_read(SOCKET sock, std::vector& buffer, std::size_t read_size, const read_callback_t& callback) { - std::lock_guard lock(m_socket_mutex); - - auto sock_it = m_sockets.find(sock); - if (sock_it == m_sockets.end()) - return false; - - //Resize the buffer to the correct size if we need it to be to hold the incoming data. - if (buffer.size() < read_size) - buffer.resize(read_size); - - auto& sockinfo = sock_it->second; - sockinfo.read_buffer = &buffer; - sockinfo.read_callback = callback; - - DWORD dwRecvNumBytes = 0; - DWORD dwFlags = 0; - WSABUF buffRecv; - - buffRecv.buf = sockinfo.read_buffer->data(); - buffRecv.len = read_size; - - //We need a new overlapped struct for EACH overlapped operation. - //we reuse them over and over. - io_context_info* p_io_info = sockinfo.get_pool_io_context(); - p_io_info->eOperation = IO_OP_READ; - - int nRet = WSARecv(sock, &buffRecv, 1, &dwRecvNumBytes, &dwFlags, &(p_io_info->overlapped), NULL); - - if (nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError())) { - //Fire the disconnect handler - sockinfo.disconnection_handler(*this); - return false; - } - - return true; -} - -bool -io_service::async_write(SOCKET sock, const std::vector& buffer, std::size_t write_size, const write_callback_t& callback) { - std::lock_guard lock(m_socket_mutex); - - auto sock_it = m_sockets.find(sock); - if (sock_it == m_sockets.end()) - return false; - - auto& sockinfo = sock_it->second; - sockinfo.write_buffer = buffer; - sockinfo.write_size = write_size; - sockinfo.write_callback = callback; - - WSABUF buffSend; - DWORD dwSendNumBytes = 0; - DWORD dwFlags = 0; - buffSend.buf = sockinfo.write_buffer.data(); - buffSend.len = write_size; - - //We need a new overlapped struct for EACH overlapped operation. - //we reuse them over and over. - io_context_info* p_io_info = sockinfo.get_pool_io_context(); - p_io_info->eOperation = IO_OP_WRITE; - - int nRet = WSASend(sock, &buffSend, 1, &dwSendNumBytes, dwFlags, &(p_io_info->overlapped), NULL); - if (SOCKET_ERROR == nRet) { - int error = WSAGetLastError(); - if (error != ERROR_IO_PENDING) { - //Fire the disconnect handler - sockinfo.disconnection_handler(*this); - return false; - } - } - - return true; -} - -//function used by worker thread(s) used to process io requests -void -io_service::process_io(void) { - BOOL bSuccess = FALSE; - int nRet = 0; - LPWSAOVERLAPPED pOverlapped = NULL; - sock_info* psock_info = NULL; - io_context_info* pio_info = NULL; - - DWORD dwRecvNumBytes = 0; - DWORD dwSendNumBytes = 0; - DWORD dwFlags = 0; - DWORD io_size = 0; - enIoOperation e_op; - int sock_error = 0; - - while (!m_should_stop) { - // continually loop to service io completion packets - psock_info = NULL; - pOverlapped = NULL; - - bSuccess = GetQueuedCompletionStatus(m_completion_port, &io_size, (PDWORD_PTR) &psock_info, (LPOVERLAPPED*) &pOverlapped, INFINITE); - if (!bSuccess) { - //client socket must have died. continue... - if (0 == io_size) { - sock_error = WSAGetLastError(); - //Fire the disconnect handler if we can. - if (psock_info && sock_error != ERROR_CONNECTION_ABORTED) //Seems the psock_info structure is crap when ERROR_CONNECTION_ABORTED is captured. - psock_info->disconnection_handler(*this); - continue; - } - if (m_should_stop) - return; - } - - //get the base address of the struct holding lpOverlapped (the io_context_info) pointer. - if (pOverlapped) - pio_info = CONTAINING_RECORD(pOverlapped, io_context_info, overlapped); - else - pio_info = NULL; - - // Somebody used PostQueuedCompletionStatus to post an I/O packet with - // a NULL CompletionKey (or if we get one for any reason). It is time to exit. - if (!psock_info || !pOverlapped) - return; - - e_op = pio_info->eOperation; - - //Push it to the pool so another IO operation can use it again later - if (pio_info) - psock_info->return_pool_io_context(pio_info); - pio_info = NULL; - - // First check to see if an error has occurred on the socket and if so - // then close the socket and cleanup the SOCKET_INFORMATION structure associated with the socket - if (0 == io_size) { - //Fire the disconnect handler. Check if stopped first because of some weird timing issues that may get us here. - if (bSuccess && false == m_should_stop && psock_info) - psock_info->disconnection_handler(*this); - continue; - } - - // determine what type of IO packet has completed by checking the io_context_info - // associated with this io operation. This will determine what action to take. - switch (e_op) { - case IO_OP_READ: // a read operation has completed - //TODO Resize Read buffer to the size that we read? - { - std::lock_guard lock(m_socket_mutex); - if (m_sockets.find(psock_info->hsock) != m_sockets.end()) - psock_info->read_callback(io_size); - } - break; - - case IO_OP_WRITE: // a write operation has completed - //Lock our socket structure before updating the write_size - { - std::lock_guard socklock(psock_info->sock_info_mutex); - psock_info->write_size -= io_size; - } - { - std::lock_guard lock(m_socket_mutex); - if (m_sockets.find(psock_info->hsock) != m_sockets.end()) - psock_info->write_callback(io_size); - } - break; - } //switch - } //while -} - -} //! windows_impl - -} //! network - -} //! cpp_redis diff --git a/sources/redis_client.cpp b/sources/redis_client.cpp index 0bbc505b..16bb6f67 100644 --- a/sources/redis_client.cpp +++ b/sources/redis_client.cpp @@ -25,9 +25,8 @@ namespace cpp_redis { -redis_client::redis_client(const std::shared_ptr& io_service) -: m_client(io_service) -, m_callbacks_running(0) { +redis_client::redis_client(void) +: m_callbacks_running(0) { __CPP_REDIS_LOG(debug, "cpp_redis::redis_client created"); } diff --git a/sources/redis_subscriber.cpp b/sources/redis_subscriber.cpp index 75c656d0..9a280354 100644 --- a/sources/redis_subscriber.cpp +++ b/sources/redis_subscriber.cpp @@ -26,8 +26,7 @@ namespace cpp_redis { -redis_subscriber::redis_subscriber(const std::shared_ptr& io_service) -: m_client(io_service) { +redis_subscriber::redis_subscriber(void) { __CPP_REDIS_LOG(debug, "cpp_redis::redis_subscriber created"); } diff --git a/sources/sync_client.cpp b/sources/sync_client.cpp deleted file mode 100644 index ecb2c144..00000000 --- a/sources/sync_client.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2015-2017 Simon Ninon -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include - -namespace cpp_redis { - -sync_client::sync_client(const std::shared_ptr& io_service) -: m_client(io_service) { - __CPP_REDIS_LOG(debug, "cpp_redis::sync_client created"); -} - -sync_client::~sync_client(void) { - __CPP_REDIS_LOG(debug, "cpp_redis::sync_client destroyed"); -} - - -} //! cpp_redis diff --git a/tacopie b/tacopie new file mode 160000 index 00000000..c974f31a --- /dev/null +++ b/tacopie @@ -0,0 +1 @@ +Subproject commit c974f31a2d4d9e69678b441c9ca9ded17f9a873f diff --git a/tests/sources/spec/redis_client_spec.cpp b/tests/sources/spec/redis_client_spec.cpp index 59fc6f3c..6f9e74dc 100644 --- a/tests/sources/spec/redis_client_spec.cpp +++ b/tests/sources/spec/redis_client_spec.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include +#include #include TEST(RedisClient, ValidConnectionDefaultParams) { diff --git a/tests/sources/spec/redis_subscriber_spec.cpp b/tests/sources/spec/redis_subscriber_spec.cpp index a5568019..ed27ab8f 100644 --- a/tests/sources/spec/redis_subscriber_spec.cpp +++ b/tests/sources/spec/redis_subscriber_spec.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include +#include #include #include From e9d617410604badacf78b0e79b739f2fa5dcf941 Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sat, 21 Jan 2017 01:26:41 +0100 Subject: [PATCH 02/19] only async_read recursively if the client is still connected --- sources/network/redis_connection.cpp | 2 +- tacopie | 2 +- tests/sources/main.cpp | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sources/network/redis_connection.cpp b/sources/network/redis_connection.cpp index 58efdaf9..a54e6af9 100644 --- a/sources/network/redis_connection.cpp +++ b/sources/network/redis_connection.cpp @@ -154,7 +154,7 @@ redis_connection::tcp_client_receive_handler(const tacopie::tcp_client::read_res } } - m_client.async_read({__CPP_REDIS_READ_SIZE, std::bind(&redis_connection::tcp_client_receive_handler, this, std::placeholders::_1)}); + if (is_connected()) { m_client.async_read({__CPP_REDIS_READ_SIZE, std::bind(&redis_connection::tcp_client_receive_handler, this, std::placeholders::_1)}); } } void diff --git a/tacopie b/tacopie index c974f31a..bc3f8dea 160000 --- a/tacopie +++ b/tacopie @@ -1 +1 @@ -Subproject commit c974f31a2d4d9e69678b441c9ca9ded17f9a873f +Subproject commit bc3f8deab6dc951f41e50dd61f49a4e86db274b2 diff --git a/tests/sources/main.cpp b/tests/sources/main.cpp index fb52e649..c25c4525 100644 --- a/tests/sources/main.cpp +++ b/tests/sources/main.cpp @@ -23,13 +23,15 @@ #include //! For debugging purpose, uncomment -// #include // #include +// #include +// #include int main(int argc, char** argv) { //! For debugging purpose, uncomment // cpp_redis::active_logger = std::unique_ptr(new cpp_redis::logger(cpp_redis::logger::log_level::debug)); + // tacopie::active_logger = std::unique_ptr(new tacopie::logger(tacopie::logger::log_level::debug)); ::testing::InitGoogleTest(&argc, argv); From f2acfa613807bab4abb612d769fa30c96af441f1 Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sat, 21 Jan 2017 10:51:41 +0100 Subject: [PATCH 03/19] disable shallow cloning for git submodules --- .gitmodules | 1 - tacopie | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 5bf2dfcd..34cc6551 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,4 +2,3 @@ path = tacopie url = https://github.com/Cylix/tacopie.git branch = master - shallow = true \ No newline at end of file diff --git a/tacopie b/tacopie index bc3f8dea..e8ace836 160000 --- a/tacopie +++ b/tacopie @@ -1 +1 @@ -Subproject commit bc3f8deab6dc951f41e50dd61f49a4e86db274b2 +Subproject commit e8ace836ddcbaf70a9a540759a4ba07bea5942b5 From de898fe8a9a2527fb964ba6bfe2c38ce0a6d881d Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sun, 22 Jan 2017 18:25:20 +0100 Subject: [PATCH 04/19] ensure callbacks are cleared (finished to execute) before finishing to execute destructors --- .../cpp_redis/network/redis_connection.hpp | 2 +- sources/network/redis_connection.cpp | 27 +++++++++++-------- sources/redis_client.cpp | 2 +- sources/redis_subscriber.cpp | 2 +- tacopie | 2 +- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/includes/cpp_redis/network/redis_connection.hpp b/includes/cpp_redis/network/redis_connection.hpp index e3aa1d5b..c8decb3b 100644 --- a/includes/cpp_redis/network/redis_connection.hpp +++ b/includes/cpp_redis/network/redis_connection.hpp @@ -56,7 +56,7 @@ class redis_connection { void connect(const std::string& host = "127.0.0.1", std::size_t port = 6379, const disconnection_handler_t& disconnection_handler = nullptr, const reply_callback_t& reply_callback = nullptr); - void disconnect(void); + void disconnect(bool wait_for_removal = false); bool is_connected(void); //! send cmd diff --git a/sources/network/redis_connection.cpp b/sources/network/redis_connection.cpp index a54e6af9..68c73faf 100644 --- a/sources/network/redis_connection.cpp +++ b/sources/network/redis_connection.cpp @@ -35,7 +35,7 @@ redis_connection::redis_connection(void) } redis_connection::~redis_connection(void) { - m_client.disconnect(); + m_client.disconnect(true); __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection destroyed"); } @@ -43,29 +43,29 @@ void redis_connection::connect(const std::string& host, std::size_t port, const disconnection_handler_t& client_disconnection_handler, const reply_callback_t& client_reply_callback) { - __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection attempts to connect"); - - //! connect client and start to read asynchronously - m_client.set_on_disconnection_handler(std::bind(&redis_connection::tcp_client_disconnection_handler, this)); try { + __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection attempts to connect"); + + //! connect client and start to read asynchronously m_client.connect(host, port); + m_client.async_read({__CPP_REDIS_READ_SIZE, std::bind(&redis_connection::tcp_client_receive_handler, this, std::placeholders::_1)}); + m_client.set_on_disconnection_handler(std::bind(&redis_connection::tcp_client_disconnection_handler, this)); + + __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection connected"); } catch (const tacopie::tacopie_error& e) { __CPP_REDIS_LOG(error, std::string("cpp_redis::network::redis_connection ") + e.what()); throw redis_error(e.what()); } - m_client.async_read({__CPP_REDIS_READ_SIZE, std::bind(&redis_connection::tcp_client_receive_handler, this, std::placeholders::_1)}); - - __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection connected"); m_reply_callback = client_reply_callback; m_disconnection_handler = client_disconnection_handler; } void -redis_connection::disconnect(void) { +redis_connection::disconnect(bool wait_for_removal) { __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection attempts to disconnect"); - m_client.disconnect(); + m_client.disconnect(wait_for_removal); __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection disconnected"); } @@ -154,7 +154,12 @@ redis_connection::tcp_client_receive_handler(const tacopie::tcp_client::read_res } } - if (is_connected()) { m_client.async_read({__CPP_REDIS_READ_SIZE, std::bind(&redis_connection::tcp_client_receive_handler, this, std::placeholders::_1)}); } + try { + m_client.async_read({__CPP_REDIS_READ_SIZE, std::bind(&redis_connection::tcp_client_receive_handler, this, std::placeholders::_1)}); + } + catch (const tacopie::tacopie_error&) { + //! Client disconnected in the meantime + } } void diff --git a/sources/redis_client.cpp b/sources/redis_client.cpp index 16bb6f67..dee8d7b1 100644 --- a/sources/redis_client.cpp +++ b/sources/redis_client.cpp @@ -31,7 +31,7 @@ redis_client::redis_client(void) } redis_client::~redis_client(void) { - m_client.disconnect(); + m_client.disconnect(true); __CPP_REDIS_LOG(debug, "cpp_redis::redis_client destroyed"); } diff --git a/sources/redis_subscriber.cpp b/sources/redis_subscriber.cpp index 9a280354..57cebd9b 100644 --- a/sources/redis_subscriber.cpp +++ b/sources/redis_subscriber.cpp @@ -31,7 +31,7 @@ redis_subscriber::redis_subscriber(void) { } redis_subscriber::~redis_subscriber(void) { - m_client.disconnect(); + m_client.disconnect(true); __CPP_REDIS_LOG(debug, "cpp_redis::redis_subscriber destroyed"); } diff --git a/tacopie b/tacopie index e8ace836..801474f0 160000 --- a/tacopie +++ b/tacopie @@ -1 +1 @@ -Subproject commit e8ace836ddcbaf70a9a540759a4ba07bea5942b5 +Subproject commit 801474f03e9b6c26b4d4992ee11a0ef0c73f8a40 From 493c92517d92eaae9b1c88a6a5d80f1620a0c909 Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sun, 22 Jan 2017 20:20:11 +0100 Subject: [PATCH 05/19] add header files to CMakelist (for VC++) --- CMakeLists.txt | 38 ++++++++++++++++++++++++++++---------- tacopie | 2 +- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fb908358..ebb37331 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,25 @@ +# The MIT License (MIT) +# +# Copyright (c) 2015-2017 Simon Ninon +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + ### # config ### @@ -61,19 +83,15 @@ link_directories(${GTEST_LIBS}) ### # sources ### -set(SRC_DIRS "sources" "sources/network" "sources/builders") - -IF (WIN32) - set(SRC_DIRS ${SRC_DIRS} "sources/network/windows_impl") -ELSE () - set(SRC_DIRS ${SRC_DIRS} "sources/network/unix_impl") -ENDIF (WIN32) - +set(SRC_DIRS "sources" "sources/network" "sources/builders" "includes/cpp_redis" "includes/cpp_redis/builders" "includes/cpp_redis/network") foreach(dir ${SRC_DIRS}) - # get directory sources + # get directory sources and headers file(GLOB s_${dir} "${dir}/*.cpp") + file(GLOB h_${dir} "${dir}/*.hpp") + file(GLOB i_${dir} "${dir}/*.ipp") + # set sources - set(SOURCES ${SOURCES} ${s_${dir}}) + set(SOURCES ${SOURCES} ${s_${dir}} ${h_${dir}} ${i_${dir}}) endforeach() diff --git a/tacopie b/tacopie index 801474f0..e1de5f2c 160000 --- a/tacopie +++ b/tacopie @@ -1 +1 @@ -Subproject commit 801474f03e9b6c26b4d4992ee11a0ef0c73f8a40 +Subproject commit e1de5f2cddf3abea812dfd5026b578a3d2850e35 From b6902328ba299b30388b5e7467fb7a1894e69771 Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sat, 28 Jan 2017 02:05:07 +0100 Subject: [PATCH 06/19] ensure notify_all is called in all appropriate contexts --- sources/redis_client.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sources/redis_client.cpp b/sources/redis_client.cpp index dee8d7b1..10e6e7ff 100644 --- a/sources/redis_client.cpp +++ b/sources/redis_client.cpp @@ -140,8 +140,8 @@ redis_client::connection_receive_handler(network::redis_connection&, reply& repl { std::lock_guard lock(m_callbacks_mutex); m_callbacks_running -= 1; + m_sync_condvar.notify_all(); } - m_sync_condvar.notify_all(); } void @@ -150,6 +150,8 @@ redis_client::clear_callbacks(void) { std::queue empty; std::swap(m_callbacks, empty); + + m_sync_condvar.notify_all(); } void From 8557012fd25ce3dd392494462dede088bda3632f Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sat, 28 Jan 2017 13:55:40 +0100 Subject: [PATCH 07/19] do not call disconnection handler in receive handler: will be called by tacopie --- sources/network/redis_connection.cpp | 6 +----- tacopie | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/sources/network/redis_connection.cpp b/sources/network/redis_connection.cpp index 68c73faf..0f17c761 100644 --- a/sources/network/redis_connection.cpp +++ b/sources/network/redis_connection.cpp @@ -126,11 +126,7 @@ redis_connection::call_disconnection_handler(void) { void redis_connection::tcp_client_receive_handler(const tacopie::tcp_client::read_result& result) { - if (!result.success) { - __CPP_REDIS_LOG(error, "cpp_redis::network::redis_connection unsuccessful async_read, disconnecting"); - call_disconnection_handler(); - return; - } + if (!result.success) { return; } try { __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection receives packet, attempts to build reply"); diff --git a/tacopie b/tacopie index e1de5f2c..018ccf53 160000 --- a/tacopie +++ b/tacopie @@ -1 +1 @@ -Subproject commit e1de5f2cddf3abea812dfd5026b578a3d2850e35 +Subproject commit 018ccf53e9bae84dcca7428a9008ad6d5b857ad8 From 986cb4fb438a80ee59a0cf3f514c2524434936fa Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sun, 29 Jan 2017 17:11:25 +0100 Subject: [PATCH 08/19] update ref to tacopie submodule --- tacopie | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tacopie b/tacopie index 018ccf53..593eb321 160000 --- a/tacopie +++ b/tacopie @@ -1 +1 @@ -Subproject commit 018ccf53e9bae84dcca7428a9008ad6d5b857ad8 +Subproject commit 593eb321baee234f555c619c78367e7e47b1f738 From dd0051642ea581d3de72a5e1e7527cb46e22b675 Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sun, 29 Jan 2017 17:19:36 +0100 Subject: [PATCH 09/19] remove CMake variable not used anymore --- CMakeLists.txt | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ebb37331..64702593 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,7 +66,6 @@ set(CPP_REDIS_INCLUDES ${PROJECT_SOURCE_DIR}/includes) set(TACOPIE_FOLDER ${PROJECT_SOURCE_DIR}/tacopie) set(TACOPIE_INCLUDES ${TACOPIE_FOLDER}/includes) - ### # includes ### @@ -129,16 +128,6 @@ IF (LOGGING_ENABLED) set_target_properties(${PROJECT} PROPERTIES COMPILE_DEFINITIONS "__CPP_REDIS_LOGGING_ENABLED=${LOGGING_ENABLED}") ENDIF (LOGGING_ENABLED) -# _CPP_REDIS_MAX_NB_FDS -IF (MAX_NB_FDS) - set_target_properties(${PROJECT} PROPERTIES COMPILE_DEFINITIONS "_CPP_REDIS_MAX_NB_FDS=${MAX_NB_FDS}") -ENDIF (MAX_NB_FDS) - -# __CPP_REDIS_DEFAULT_NB_IO_SERVICE_WORKERS -IF (DEFAULT_NB_IO_SERVICE_WORKERS) - set_target_properties(${PROJECT} PROPERTIES COMPILE_DEFINITIONS "__CPP_REDIS_DEFAULT_NB_IO_SERVICE_WORKERS=${DEFAULT_NB_IO_SERVICE_WORKERS}") -ENDIF (DEFAULT_NB_IO_SERVICE_WORKERS) - ### # install From c8225bbd197ed46027b1438883b69a796276ed46 Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sun, 29 Jan 2017 17:53:29 +0100 Subject: [PATCH 10/19] windows tests: WSAStartup & Cleanup --- tests/sources/main.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tests/sources/main.cpp b/tests/sources/main.cpp index c25c4525..c1f25435 100644 --- a/tests/sources/main.cpp +++ b/tests/sources/main.cpp @@ -22,6 +22,10 @@ #include +#ifdef _WIN32 +#include +#endif /* _WIN32 */ + //! For debugging purpose, uncomment // #include // #include @@ -33,7 +37,25 @@ main(int argc, char** argv) { // cpp_redis::active_logger = std::unique_ptr(new cpp_redis::logger(cpp_redis::logger::log_level::debug)); // tacopie::active_logger = std::unique_ptr(new tacopie::logger(tacopie::logger::log_level::debug)); +#ifdef _WIN32 + //! Windows netword DLL init + WORD version = MAKEWORD(2, 2); + WSADATA data; + + if (WSAStartup(version, &data) != 0) { + std::cerr << "WSAStartup() failure" << std::endl; + return -1; + } + +#endif /* _WIN32 */ + ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); + int ret = RUN_ALL_TESTS(); + +#ifdef _WIN32 + WSACleanup(); +#endif /* _WIN32 */ + + return ret; } From 6d3f9d9fb4c2d7007a7762cda872ac0ef9793a9a Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sun, 29 Jan 2017 19:15:10 +0100 Subject: [PATCH 11/19] full integration of gtest+tacopie inside cpp_redis solution on windows --- CMakeLists.txt | 20 +++++++++----------- examples/future_client.cpp | 19 +++++++++++++++++++ examples/redis_client.cpp | 21 ++++++++++++++++++++- examples/redis_subscriber.cpp | 19 +++++++++++++++++++ examples/sync_client.cpp | 19 +++++++++++++++++++ install_deps.sh | 11 +++++++---- tacopie | 2 +- tests/CMakeLists.txt | 4 +--- tests/sources/main.cpp | 1 - 9 files changed, 95 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 64702593..33c71052 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,13 @@ project(${PROJECT} CXX) ### IF (WIN32) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /O2") + + # was causing conflics with gtest build + string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) + foreach (flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE) + string(REPLACE "/MD" "-MT" ${flag_var} "${${flag_var}}") + endforeach() + add_definitions(-D_UNICODE) add_definitions(-DUNICODE) add_definitions(-DWIN32_LEAN_AND_MEAN) @@ -56,10 +63,6 @@ ENDIF (WIN32) ### # variables ### -# Deps (Gtest) -set(DEPS_FOLDER ${PROJECT_SOURCE_DIR}/deps/build) -set(GTEST_INCLUDES ${DEPS_FOLDER}/gtest/include) -set(GTEST_LIBS ${DEPS_FOLDER}/gtest/lib) # cpp_redis set(CPP_REDIS_INCLUDES ${PROJECT_SOURCE_DIR}/includes) # tacopie @@ -73,12 +76,6 @@ include_directories(${CPP_REDIS_INCLUDES} ${TACOPIE_INCLUDES}) -### -# link -### -link_directories(${GTEST_LIBS}) - - ### # sources ### @@ -153,11 +150,12 @@ add_subdirectory(tacopie) IF (BUILD_EXAMPLES) add_subdirectory(examples) ENDIF(BUILD_EXAMPLES) - +set (flag_var "") ### # tests ### IF (BUILD_TESTS) add_subdirectory(tests) + add_subdirectory(${PROJECT_SOURCE_DIR}/deps/src/googletest) ENDIF(BUILD_TESTS) diff --git a/examples/future_client.cpp b/examples/future_client.cpp index c8770f91..6e13ebb8 100644 --- a/examples/future_client.cpp +++ b/examples/future_client.cpp @@ -24,8 +24,23 @@ #include +#ifdef _WIN32 +#include +#endif /* _WIN32 */ + int main(void) { +#ifdef _WIN32 + //! Windows netword DLL init + WORD version = MAKEWORD(2, 2); + WSADATA data; + + if (WSAStartup(version, &data) != 0) { + std::cerr << "WSAStartup() failure" << std::endl; + return -1; + } +#endif /* _WIN32 */ + //! Enable logging cpp_redis::active_logger = std::unique_ptr(new cpp_redis::logger); @@ -48,5 +63,9 @@ main(void) { tok = client.get("hello"); std::cout << "get 'hello': " << tok.get() << std::endl; +#ifdef _WIN32 + WSACleanup(); +#endif /* _WIN32 */ + return 0; } diff --git a/examples/redis_client.cpp b/examples/redis_client.cpp index c39013b5..ffb97ae5 100644 --- a/examples/redis_client.cpp +++ b/examples/redis_client.cpp @@ -24,9 +24,24 @@ #include +#ifdef _WIN32 +#include +#endif /* _WIN32 */ + int main(void) { - //! Enable logging +#ifdef _WIN32 + //! Windows netword DLL init + WORD version = MAKEWORD(2, 2); + WSADATA data; + + if (WSAStartup(version, &data) != 0) { + std::cerr << "WSAStartup() failure" << std::endl; + return -1; + } +#endif /* _WIN32 */ + + //! Enable logging cpp_redis::active_logger = std::unique_ptr(new cpp_redis::logger); cpp_redis::redis_client client; @@ -65,5 +80,9 @@ main(void) { // synchronous commit, timeout // client.sync_commit(std::chrono::milliseconds(100)); +#ifdef _WIN32 + WSACleanup(); +#endif /* _WIN32 */ + return 0; } diff --git a/examples/redis_subscriber.cpp b/examples/redis_subscriber.cpp index 6b3df352..09bcce53 100644 --- a/examples/redis_subscriber.cpp +++ b/examples/redis_subscriber.cpp @@ -25,6 +25,10 @@ #include #include +#ifdef _WIN32 +#include +#endif /* _WIN32 */ + volatile std::atomic_bool should_exit(false); void @@ -34,6 +38,17 @@ sigint_handler(int) { int main(void) { +#ifdef _WIN32 + //! Windows netword DLL init + WORD version = MAKEWORD(2, 2); + WSADATA data; + + if (WSAStartup(version, &data) != 0) { + std::cerr << "WSAStartup() failure" << std::endl; + return -1; + } +#endif /* _WIN32 */ + //! Enable logging cpp_redis::active_logger = std::unique_ptr(new cpp_redis::logger); @@ -55,5 +70,9 @@ main(void) { signal(SIGINT, &sigint_handler); while (!should_exit) {} +#ifdef _WIN32 + WSACleanup(); +#endif /* _WIN32 */ + return 0; } diff --git a/examples/sync_client.cpp b/examples/sync_client.cpp index 11301432..fab2a010 100644 --- a/examples/sync_client.cpp +++ b/examples/sync_client.cpp @@ -24,8 +24,23 @@ #include +#ifdef _WIN32 +#include +#endif /* _WIN32 */ + int main(void) { +#ifdef _WIN32 + //! Windows netword DLL init + WORD version = MAKEWORD(2, 2); + WSADATA data; + + if (WSAStartup(version, &data) != 0) { + std::cerr << "WSAStartup() failure" << std::endl; + return -1; + } +#endif /* _WIN32 */ + //! Enable logging cpp_redis::active_logger = std::unique_ptr(new cpp_redis::logger); @@ -44,5 +59,9 @@ main(void) { r = client.get("hello"); std::cout << "get 'hello': " << r << std::endl; +#ifdef _WIN32 + WSACleanup(); +#endif /* _WIN32 */ + return 0; } diff --git a/install_deps.sh b/install_deps.sh index 4ca56120..26f281c4 100755 --- a/install_deps.sh +++ b/install_deps.sh @@ -10,7 +10,10 @@ mkdir -p $DEPS_SRC_DIR $DEPS_BUILD_DIR # GoogleTest cd $DEPS_SRC_DIR ## Fetch the GoogleTest sources -git clone https://github.com/google/googletest.git && cd googletest/googletest && \ -mkdir build && cd build && \ -cmake .. -DCMAKE_INSTALL_PREFIX=$DEPS_BUILD_DIR/gtest && \ -make && make install +git clone https://github.com/google/googletest.git && cd googletest/googletest + +if [ !"$(expr substr $(uname -s) 1 10)" == "MINGW32_NT" ]; then + mkdir build && cd build + cmake .. -DCMAKE_INSTALL_PREFIX=$DEPS_BUILD_DIR/gtest + make && make install +fi diff --git a/tacopie b/tacopie index 593eb321..9ebad2d7 160000 --- a/tacopie +++ b/tacopie @@ -1 +1 @@ -Subproject commit 593eb321baee234f555c619c78367e7e47b1f738 +Subproject commit 9ebad2d7f8a6170ba8af1ca6a64a0f01f15b0a9a diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c71fc479..504e4df2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -34,13 +34,11 @@ IF (NOT WIN32) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") ENDIF (NOT WIN32) - ### # includes ### include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/includes - ${CPP_REDIS_INCLUDES} - ${GTEST_INCLUDES}) + ${CPP_REDIS_INCLUDES}) ### diff --git a/tests/sources/main.cpp b/tests/sources/main.cpp index c1f25435..c0a66ff2 100644 --- a/tests/sources/main.cpp +++ b/tests/sources/main.cpp @@ -46,7 +46,6 @@ main(int argc, char** argv) { std::cerr << "WSAStartup() failure" << std::endl; return -1; } - #endif /* _WIN32 */ ::testing::InitGoogleTest(&argc, argv); From 0d573e25af5d8b20772d25818b3608c001c50464 Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sun, 29 Jan 2017 19:36:23 +0100 Subject: [PATCH 12/19] fix Cmake warnings under unix (cpp_redis integration) --- CMakeLists.txt | 6 ++++-- install_deps.sh | 6 +++--- tacopie | 2 +- tests/sources/spec/reply_spec.cpp | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 33c71052..0220131a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,7 @@ set(CPP_REDIS_INCLUDES ${PROJECT_SOURCE_DIR}/includes) set(TACOPIE_FOLDER ${PROJECT_SOURCE_DIR}/tacopie) set(TACOPIE_INCLUDES ${TACOPIE_FOLDER}/includes) + ### # includes ### @@ -150,12 +151,13 @@ add_subdirectory(tacopie) IF (BUILD_EXAMPLES) add_subdirectory(examples) ENDIF(BUILD_EXAMPLES) -set (flag_var "") ### # tests ### IF (BUILD_TESTS) add_subdirectory(tests) - add_subdirectory(${PROJECT_SOURCE_DIR}/deps/src/googletest) + IF (EXISTS ${PROJECT_SOURCE_DIR}/deps/src/googletest) + add_subdirectory(${PROJECT_SOURCE_DIR}/deps/src/googletest) + ENDIF () ENDIF(BUILD_TESTS) diff --git a/install_deps.sh b/install_deps.sh index 26f281c4..36fd2528 100755 --- a/install_deps.sh +++ b/install_deps.sh @@ -13,7 +13,7 @@ cd $DEPS_SRC_DIR git clone https://github.com/google/googletest.git && cd googletest/googletest if [ !"$(expr substr $(uname -s) 1 10)" == "MINGW32_NT" ]; then - mkdir build && cd build - cmake .. -DCMAKE_INSTALL_PREFIX=$DEPS_BUILD_DIR/gtest - make && make install +# mkdir build && cd build +# cmake .. -DCMAKE_INSTALL_PREFIX=$DEPS_BUILD_DIR/gtest +# make && make install fi diff --git a/tacopie b/tacopie index 9ebad2d7..1aac48a8 160000 --- a/tacopie +++ b/tacopie @@ -1 +1 @@ -Subproject commit 9ebad2d7f8a6170ba8af1ca6a64a0f01f15b0a9a +Subproject commit 1aac48a83d0adbf0577ca9e9538da43e257696a2 diff --git a/tests/sources/spec/reply_spec.cpp b/tests/sources/spec/reply_spec.cpp index 4a5d1752..1a8fb4b5 100644 --- a/tests/sources/spec/reply_spec.cpp +++ b/tests/sources/spec/reply_spec.cpp @@ -141,7 +141,7 @@ TEST(Reply, Array) { EXPECT_THROW(r.error(), cpp_redis::redis_error); EXPECT_EQ((bool) r, true); auto arr = r.as_array(); - EXPECT_EQ(arr.size(), 2); + EXPECT_EQ(arr.size(), 2U); EXPECT_EQ(arr[0].is_integer(), true); EXPECT_EQ(arr[0].as_integer(), 42); EXPECT_EQ(arr[1].is_simple_string(), true); From 52264ca17d43e89385c78fcf6f7ae903a1273ddd Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sun, 29 Jan 2017 19:38:21 +0100 Subject: [PATCH 13/19] install_deps update --- install_deps.sh | 11 ++--------- tacopie | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/install_deps.sh b/install_deps.sh index 36fd2528..c48fc987 100755 --- a/install_deps.sh +++ b/install_deps.sh @@ -2,18 +2,11 @@ DEPS_DIR=`pwd`/deps DEPS_SRC_DIR=$DEPS_DIR/src -DEPS_BUILD_DIR=$DEPS_DIR/build # Create deps folder -mkdir -p $DEPS_SRC_DIR $DEPS_BUILD_DIR +mkdir -p $DEPS_SRC_DIR # GoogleTest cd $DEPS_SRC_DIR ## Fetch the GoogleTest sources -git clone https://github.com/google/googletest.git && cd googletest/googletest - -if [ !"$(expr substr $(uname -s) 1 10)" == "MINGW32_NT" ]; then -# mkdir build && cd build -# cmake .. -DCMAKE_INSTALL_PREFIX=$DEPS_BUILD_DIR/gtest -# make && make install -fi +git clone https://github.com/google/googletest.git diff --git a/tacopie b/tacopie index 1aac48a8..ecdd236d 160000 --- a/tacopie +++ b/tacopie @@ -1 +1 @@ -Subproject commit 1aac48a83d0adbf0577ca9e9538da43e257696a2 +Subproject commit ecdd236ddf6d56841e6109c8daa9214cc85876d3 From d3d0ed63abf95bb2802a43f344b4e66ee56c05eb Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sun, 29 Jan 2017 20:30:37 +0100 Subject: [PATCH 14/19] fix cmakelist --- CMakeLists.txt | 2 ++ tacopie | 2 +- tests/CMakeLists.txt | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0220131a..f35f9d5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,8 @@ ENDIF (WIN32) ### # variables ### +# gtest +set(GTEST_INCLUDES ${PROJECT_SOURCE_DIR}/deps/src/googletest/googletest/include) # cpp_redis set(CPP_REDIS_INCLUDES ${PROJECT_SOURCE_DIR}/includes) # tacopie diff --git a/tacopie b/tacopie index ecdd236d..05f56458 160000 --- a/tacopie +++ b/tacopie @@ -1 +1 @@ -Subproject commit ecdd236ddf6d56841e6109c8daa9214cc85876d3 +Subproject commit 05f56458e17bc7cd5c76294e33b43fe63919f352 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 504e4df2..2ad399a3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -38,8 +38,8 @@ ENDIF (NOT WIN32) # includes ### include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/includes - ${CPP_REDIS_INCLUDES}) - + ${CPP_REDIS_INCLUDES} + ${GTEST_INCLUDES}) ### # sources From 35db8b5430e13fbdec44a94955a40e2f5508b50b Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sun, 29 Jan 2017 20:34:30 +0100 Subject: [PATCH 15/19] fix cmakelist --- tacopie | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tacopie b/tacopie index 05f56458..59664a3a 160000 --- a/tacopie +++ b/tacopie @@ -1 +1 @@ -Subproject commit 05f56458e17bc7cd5c76294e33b43fe63919f352 +Subproject commit 59664a3aabb202bfe95d5cb75673b36e3daebd73 From 3c51c6b25f9f211ad13c43c8fabe6b69c8787d58 Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sun, 29 Jan 2017 21:01:23 +0100 Subject: [PATCH 16/19] clang-format --- examples/future_client.cpp | 16 ++++++++-------- examples/redis_client.cpp | 24 ++++++++++++------------ examples/redis_subscriber.cpp | 16 ++++++++-------- examples/sync_client.cpp | 14 +++++++------- sources/redis_client.cpp | 2 +- tests/sources/main.cpp | 18 +++++++++--------- 6 files changed, 45 insertions(+), 45 deletions(-) diff --git a/examples/future_client.cpp b/examples/future_client.cpp index 6e13ebb8..60d88cbd 100644 --- a/examples/future_client.cpp +++ b/examples/future_client.cpp @@ -31,16 +31,16 @@ int main(void) { #ifdef _WIN32 - //! Windows netword DLL init - WORD version = MAKEWORD(2, 2); - WSADATA data; + //! Windows netword DLL init + WORD version = MAKEWORD(2, 2); + WSADATA data; - if (WSAStartup(version, &data) != 0) { - std::cerr << "WSAStartup() failure" << std::endl; - return -1; - } + if (WSAStartup(version, &data) != 0) { + std::cerr << "WSAStartup() failure" << std::endl; + return -1; + } #endif /* _WIN32 */ - + //! Enable logging cpp_redis::active_logger = std::unique_ptr(new cpp_redis::logger); diff --git a/examples/redis_client.cpp b/examples/redis_client.cpp index ffb97ae5..8225540f 100644 --- a/examples/redis_client.cpp +++ b/examples/redis_client.cpp @@ -31,17 +31,17 @@ int main(void) { #ifdef _WIN32 - //! Windows netword DLL init - WORD version = MAKEWORD(2, 2); - WSADATA data; - - if (WSAStartup(version, &data) != 0) { - std::cerr << "WSAStartup() failure" << std::endl; - return -1; - } + //! Windows netword DLL init + WORD version = MAKEWORD(2, 2); + WSADATA data; + + if (WSAStartup(version, &data) != 0) { + std::cerr << "WSAStartup() failure" << std::endl; + return -1; + } #endif /* _WIN32 */ - - //! Enable logging + + //! Enable logging cpp_redis::active_logger = std::unique_ptr(new cpp_redis::logger); cpp_redis::redis_client client; @@ -77,8 +77,8 @@ main(void) { // synchronous commit, no timeout client.sync_commit(); - // synchronous commit, timeout - // client.sync_commit(std::chrono::milliseconds(100)); +// synchronous commit, timeout +// client.sync_commit(std::chrono::milliseconds(100)); #ifdef _WIN32 WSACleanup(); diff --git a/examples/redis_subscriber.cpp b/examples/redis_subscriber.cpp index 09bcce53..6e2fc237 100644 --- a/examples/redis_subscriber.cpp +++ b/examples/redis_subscriber.cpp @@ -39,16 +39,16 @@ sigint_handler(int) { int main(void) { #ifdef _WIN32 - //! Windows netword DLL init - WORD version = MAKEWORD(2, 2); - WSADATA data; + //! Windows netword DLL init + WORD version = MAKEWORD(2, 2); + WSADATA data; - if (WSAStartup(version, &data) != 0) { - std::cerr << "WSAStartup() failure" << std::endl; - return -1; - } + if (WSAStartup(version, &data) != 0) { + std::cerr << "WSAStartup() failure" << std::endl; + return -1; + } #endif /* _WIN32 */ - + //! Enable logging cpp_redis::active_logger = std::unique_ptr(new cpp_redis::logger); diff --git a/examples/sync_client.cpp b/examples/sync_client.cpp index fab2a010..884bf5fc 100644 --- a/examples/sync_client.cpp +++ b/examples/sync_client.cpp @@ -31,14 +31,14 @@ int main(void) { #ifdef _WIN32 - //! Windows netword DLL init - WORD version = MAKEWORD(2, 2); - WSADATA data; + //! Windows netword DLL init + WORD version = MAKEWORD(2, 2); + WSADATA data; - if (WSAStartup(version, &data) != 0) { - std::cerr << "WSAStartup() failure" << std::endl; - return -1; - } + if (WSAStartup(version, &data) != 0) { + std::cerr << "WSAStartup() failure" << std::endl; + return -1; + } #endif /* _WIN32 */ //! Enable logging diff --git a/sources/redis_client.cpp b/sources/redis_client.cpp index 10e6e7ff..da4692b7 100644 --- a/sources/redis_client.cpp +++ b/sources/redis_client.cpp @@ -140,7 +140,7 @@ redis_client::connection_receive_handler(network::redis_connection&, reply& repl { std::lock_guard lock(m_callbacks_mutex); m_callbacks_running -= 1; - m_sync_condvar.notify_all(); + m_sync_condvar.notify_all(); } } diff --git a/tests/sources/main.cpp b/tests/sources/main.cpp index c0a66ff2..008be4ae 100644 --- a/tests/sources/main.cpp +++ b/tests/sources/main.cpp @@ -33,19 +33,19 @@ int main(int argc, char** argv) { - //! For debugging purpose, uncomment - // cpp_redis::active_logger = std::unique_ptr(new cpp_redis::logger(cpp_redis::logger::log_level::debug)); - // tacopie::active_logger = std::unique_ptr(new tacopie::logger(tacopie::logger::log_level::debug)); +//! For debugging purpose, uncomment +// cpp_redis::active_logger = std::unique_ptr(new cpp_redis::logger(cpp_redis::logger::log_level::debug)); +// tacopie::active_logger = std::unique_ptr(new tacopie::logger(tacopie::logger::log_level::debug)); #ifdef _WIN32 //! Windows netword DLL init - WORD version = MAKEWORD(2, 2); - WSADATA data; + WORD version = MAKEWORD(2, 2); + WSADATA data; - if (WSAStartup(version, &data) != 0) { - std::cerr << "WSAStartup() failure" << std::endl; - return -1; - } + if (WSAStartup(version, &data) != 0) { + std::cerr << "WSAStartup() failure" << std::endl; + return -1; + } #endif /* _WIN32 */ ::testing::InitGoogleTest(&argc, argv); From b042bf559ea5e00ad6a5789b9ad15b0975961592 Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sun, 29 Jan 2017 21:44:46 +0100 Subject: [PATCH 17/19] update ref to tacopie --- tacopie | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tacopie b/tacopie index 59664a3a..55269076 160000 --- a/tacopie +++ b/tacopie @@ -1 +1 @@ -Subproject commit 59664a3aabb202bfe95d5cb75673b36e3daebd73 +Subproject commit 552690766d0c646f1a025a5816e9a15f875a4d31 From c5ef09b26634045d338aa747f304aab2fcf8b560 Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sun, 29 Jan 2017 21:50:11 +0100 Subject: [PATCH 18/19] update ref to tacopie --- tacopie | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tacopie b/tacopie index 55269076..925fea9c 160000 --- a/tacopie +++ b/tacopie @@ -1 +1 @@ -Subproject commit 552690766d0c646f1a025a5816e9a15f875a4d31 +Subproject commit 925fea9c19f71c4295d40f12816b25904994ac8f From 1309340450d0f97e108511819930f3d8240b3a5e Mon Sep 17 00:00:00 2001 From: Simon Ninon Date: Sun, 29 Jan 2017 22:34:23 +0100 Subject: [PATCH 19/19] update CHANGELOG --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a17817ea..21bed83b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## [v3.0](https://github.com/Cylix/cpp_redis/releases/tag/3.0) +### Changes +* Rewrite the network side of cpp_redis by using the [tacopie library](https://github.com/Cylix/tacopie) + +### Additions +* Tacopie is now a submodule of cpp_redis + +### Removals +* All network related code + + ## [v2.2](https://github.com/Cylix/cpp_redis/releases/tag/2.2) ### Changes * Bug patch