Skip to content
This repository was archived by the owner on Apr 6, 2019. It is now read-only.

Commit 77800b0

Browse files
committed
merge: provide windows support for cpp_redis. #15 #16
2 parents 928569d + bd7e574 commit 77800b0

20 files changed

+830
-27
lines changed

.gitignore

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,24 @@
2222
*.a
2323
*.lib
2424

25+
build
26+
deps
27+
2528
# Executables
2629
*.exe
2730
*.out
2831
*.app
32+
*.sln
33+
*.vcxproj
34+
*.suo
35+
*.user
36+
*.filters
37+
*.VC.db
38+
*.opendb
39+
.gitignore
40+
cmake_install.cmake
2941

30-
build
31-
deps
42+
CMakeCache.txt
43+
CMakeFiles/
44+
Release/
45+
Debug/

CMakeLists.txt

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
cmake_minimum_required(VERSION 2.8.7)
55
set(CMAKE_MACOSX_RPATH 1)
66

7+
78
###
89
# verbose make
910
###
@@ -21,7 +22,10 @@ project(${PROJECT} CXX)
2122
# compilation options
2223
###
2324
IF (WIN32)
24-
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /O2 -D '_UNICODE' -D 'UNICODE' -D 'WIN32_LEAN_AND_MEAN'")
25+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /O2")
26+
add_definitions(-D_UNICODE)
27+
add_definitions(-DUNICODE)
28+
add_definitions(-DWIN32_LEAN_AND_MEAN)
2529
ELSE ()
2630
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -W -Wall -Wextra -O3")
2731
ENDIF (WIN32)
@@ -51,8 +55,15 @@ link_directories(${GTEST_LIBS})
5155
###
5256
# sources
5357
###
54-
set(DIRS "sources" "sources/network" "sources/builders")
55-
foreach(dir ${DIRS})
58+
set(SRC_DIRS "sources" "sources/network" "sources/builders")
59+
60+
IF (WIN32)
61+
set(SRC_DIRS ${SRC_DIRS} "sources/network/windows")
62+
ELSE ()
63+
set(SRC_DIRS ${SRC_DIRS} "sources/network/unix")
64+
ENDIF (WIN32)
65+
66+
foreach(dir ${SRC_DIRS})
5667
# get directory sources
5768
file(GLOB s_${dir} "${dir}/*.cpp")
5869
# set sources
@@ -72,7 +83,12 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
7283
# executable
7384
###
7485
add_library(${PROJECT} STATIC ${SOURCES})
75-
target_link_libraries(${PROJECT} pthread)
86+
87+
IF (WIN32)
88+
target_link_libraries(${PROJECT} ws2_32)
89+
ELSE ()
90+
target_link_libraries(${PROJECT} pthread)
91+
ENDIF (WIN32)
7692

7793
IF (READ_SIZE)
7894
set_target_properties(${PROJECT}
@@ -86,6 +102,7 @@ set_target_properties(${PROJECT}
86102
COMPILE_DEFINITIONS "__CPP_REDIS_NO_LOGGING=${NO_LOGGING}")
87103
ENDIF (NO_LOGGING)
88104

105+
89106
###
90107
# install
91108
###

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ A [Wiki](https://github.com/Cylix/cpp_redis/wiki) is available and provides docu
1818
Some examples are provided in this repository:
1919
* [redis_client.cpp](examples/redis_client.cpp) shows how to use the redis client class.
2020
* [redis_subscriber.cpp](examples/redis_subscriber.cpp) shows how to use the redis subscriber class.
21-
* [logger.cpp](examples/logger.cpp) shows how to setup a logger for cpp_redis.
2221

2322
These examples can also be found inside the [Wiki](https://github.com/Cylix/cpp_redis/wiki/Examples).
2423

includes/cpp_redis/network/redis_connection.hpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@
55
#include <vector>
66
#include <functional>
77

8-
#include <cpp_redis/network/tcp_client.hpp>
8+
#ifdef _MSC_VER
9+
# include <cpp_redis/network/windows/tcp_client.hpp>
10+
#else
11+
# include <cpp_redis/network/unix/tcp_client.hpp>
12+
#endif /* _MSC_VER */
13+
914
#include <cpp_redis/builders/reply_builder.hpp>
1015

1116
namespace cpp_redis {
@@ -15,7 +20,7 @@ namespace network {
1520
class redis_connection {
1621
public:
1722
//! ctor & dtor
18-
redis_connection(void);
23+
redis_connection(const std::shared_ptr<io_service>& IO);
1924
~redis_connection(void);
2025

2126
//! copy ctor & assignment operator

includes/cpp_redis/network/tcp_client.hpp renamed to includes/cpp_redis/network/unix/tcp_client.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#include <atomic>
88
#include <stdexcept>
99

10-
#include <cpp_redis/network/io_service.hpp>
10+
#include <cpp_redis/network/unix/io_service.hpp>
1111
#include <cpp_redis/redis_error.hpp>
1212

1313
#ifndef __CPP_REDIS_READ_SIZE
@@ -23,7 +23,7 @@ namespace network {
2323
class tcp_client {
2424
public:
2525
//! ctor & dtor
26-
tcp_client(void);
26+
tcp_client(const std::shared_ptr<io_service>& IO = nullptr);
2727
~tcp_client(void);
2828

2929
//! assignment operator & copy ctor
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#pragma once
2+
3+
#include <thread>
4+
#include <atomic>
5+
#include <unordered_map>
6+
#include <vector>
7+
#include <mutex>
8+
9+
#include <WinSock2.h>
10+
11+
#define MAX_BUFF_SIZE __CPP_REDIS_READ_SIZE
12+
#define MAX_WORKER_THREADS 16
13+
14+
namespace cpp_redis {
15+
16+
namespace network {
17+
18+
typedef enum _enIoOperation {
19+
//IO_OP_ACCEPT,
20+
IO_OP_READ,
21+
IO_OP_WRITE
22+
} enIoOperation;
23+
24+
25+
class io_service {
26+
public:
27+
//! instance getter (singleton pattern)
28+
static const std::shared_ptr<io_service>& get_instance(void);
29+
io_service(size_t max_worker_threads = MAX_WORKER_THREADS);
30+
~io_service(void);
31+
32+
void shutdown();
33+
34+
private:
35+
//! copy ctor & assignment operator
36+
io_service(const io_service&) = delete;
37+
io_service& operator=(const io_service&) = delete;
38+
39+
public:
40+
//! disconnection handler declaration
41+
typedef std::function<void(io_service&)> disconnection_handler_t;
42+
43+
//! add or remove a given socket from the io service
44+
//! untrack should never be called from inside a callback
45+
void track(SOCKET sock, const disconnection_handler_t& handler);
46+
void untrack(SOCKET sock);
47+
48+
//! asynchronously read read_size bytes and append them to the given buffer
49+
//! on completion, call the read_callback to notify of the success or failure of the operation
50+
//! return false if another async_read operation is in progress or socket is not registered
51+
typedef std::function<void(std::size_t)> read_callback_t;
52+
bool async_read(SOCKET socket, std::vector<char>& buffer, std::size_t read_size, const read_callback_t& callback);
53+
54+
//! asynchronously write write_size bytes from buffer to the specified fd
55+
//!on completion, call the write_callback to notify of the success or failure of the operation
56+
//! return false if another async_write operation is in progress or socket is not registered
57+
typedef std::function<void(std::size_t)> write_callback_t;
58+
bool async_write(SOCKET socket, const std::vector<char>& buffer, std::size_t write_size, const write_callback_t& callback);
59+
60+
private:
61+
62+
struct io_context_info : OVERLAPPED {
63+
WSAOVERLAPPED overlapped;
64+
enIoOperation eOperation;
65+
};
66+
67+
//! simple struct to keep track of ongoing operations on a given sockeet
68+
class sock_info
69+
{
70+
public:
71+
sock_info(void) = default;
72+
virtual ~sock_info(void)
73+
{
74+
std::lock_guard<std::recursive_mutex> socklock(sock_info_mutex);
75+
for (auto it = io_contexts_pool.begin(); it != io_contexts_pool.end(); it++)
76+
delete *it;
77+
78+
io_contexts_pool.clear();
79+
}
80+
81+
SOCKET hsock;
82+
std::size_t sent_bytes;
83+
84+
//Must protect the members of our structure from access by multiple threads during IO Completion
85+
std::recursive_mutex sock_info_mutex;
86+
87+
//We keep a simple vector of io_context_info structs to reuse for overlapped WSARecv and WSASend operations
88+
//Since each must have its OWN struct if we issue them at the same time.
89+
//othewise things get tangled up and borked.
90+
std::vector<io_context_info*> io_contexts_pool;
91+
92+
disconnection_handler_t disconnection_handler;
93+
94+
std::vector<char>* read_buffer;
95+
read_callback_t read_callback;
96+
97+
std::vector<char> write_buffer;
98+
std::size_t write_size;
99+
write_callback_t write_callback;
100+
101+
io_context_info* get_pool_io_context() {
102+
io_context_info* pInfo = NULL;
103+
std::lock_guard<std::recursive_mutex> socklock(sock_info_mutex);
104+
if (!io_contexts_pool.empty())
105+
{
106+
pInfo = io_contexts_pool.back();
107+
io_contexts_pool.pop_back();
108+
}
109+
if (!pInfo)
110+
pInfo = new io_context_info();
111+
//MUST clear the overlapped structure between IO calls!
112+
memset(&pInfo->overlapped, 0, sizeof(OVERLAPPED));
113+
return pInfo;
114+
}
115+
116+
void return_pool_io_context(io_context_info* p_io) {
117+
std::lock_guard<std::recursive_mutex> socklock(sock_info_mutex);
118+
io_contexts_pool.push_back(p_io);
119+
}
120+
};
121+
122+
typedef std::function<void()> callback_t;
123+
124+
//! wait for incoming events and notify
125+
int process_io(void);
126+
127+
HANDLE m_completion_port;
128+
unsigned int m_worker_thread_pool_size;
129+
std::vector<std::thread> m_worker_threads; //vector containing all the threads we start to service our i/o requests
130+
131+
private:
132+
//! whether the worker should terminate or not
133+
std::atomic_bool m_should_stop;
134+
135+
//! tracked sockets
136+
std::unordered_map<SOCKET, sock_info> m_sockets;
137+
138+
//! mutex to protect m_notify_socket access against race condition
139+
//!
140+
//! specific mutex for untrack: we dont want someone to untrack a socket while we process it
141+
//! this behavior could cause some issues when executing callbacks in another thread
142+
//! for example, obj is destroyed, in its dtor it untracks the socket, but at the same time
143+
//! a callback is executed from within another thread: the untrack mutex avoid this without being costly
144+
std::recursive_mutex m_socket_mutex;
145+
};
146+
147+
} //! network
148+
149+
} //! cpp_redis
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#pragma once
2+
3+
#include <list>
4+
#include <string>
5+
#include <vector>
6+
#include <mutex>
7+
#include <thread>
8+
#include <atomic>
9+
#include <stdexcept>
10+
11+
#include <cpp_redis/network/windows/io_service.hpp>
12+
#include <cpp_redis/redis_error.hpp>
13+
14+
#ifndef __CPP_REDIS_READ_SIZE
15+
# define __CPP_REDIS_READ_SIZE 4096
16+
#endif /* __CPP_REDIS_READ_SIZE */
17+
18+
namespace cpp_redis {
19+
20+
namespace network {
21+
22+
//! tcp_client
23+
//! async tcp client based on boost asio
24+
class tcp_client {
25+
public:
26+
//! ctor & dtor
27+
tcp_client(const std::shared_ptr<io_service>& IO = nullptr);
28+
~tcp_client(void);
29+
30+
//! assignment operator & copy ctor
31+
tcp_client(const tcp_client&) = delete;
32+
tcp_client& operator=(const tcp_client&) = delete;
33+
34+
//! returns whether the client is connected or not
35+
bool is_connected(void);
36+
37+
//! handle connection & disconnection
38+
typedef std::function<void(tcp_client&)> disconnection_handler_t;
39+
typedef std::function<bool(tcp_client&, const std::vector<char>& buffer)> receive_handler_t;
40+
void connect(const std::string& host, unsigned int port,
41+
const disconnection_handler_t& disconnection_handler = nullptr,
42+
const receive_handler_t& receive_handler = nullptr);
43+
void disconnect(void);
44+
45+
//! send data
46+
void send(const std::string& buffer);
47+
void send(const std::vector<char>& buffer);
48+
49+
private:
50+
//! make async read and write operations
51+
void async_read(void);
52+
void async_write(void);
53+
54+
//! io service callback
55+
void io_service_disconnection_handler(io_service&);
56+
57+
void reset_state(void);
58+
void clear_buffer(void);
59+
60+
private:
61+
//! io service instance
62+
const std::shared_ptr<network::io_service> m_io_service;
63+
64+
//! socket
65+
SOCKET m_sock;
66+
67+
//! is connected
68+
std::atomic_bool m_is_connected;
69+
70+
//! buffers
71+
static const unsigned int READ_SIZE = __CPP_REDIS_READ_SIZE;
72+
std::vector<char> m_read_buffer;
73+
std::list<std::vector<char>> m_write_buffer;
74+
75+
//! handlers
76+
receive_handler_t m_receive_handler;
77+
disconnection_handler_t m_disconnection_handler;
78+
79+
//! thread safety
80+
std::mutex m_write_buffer_mutex;
81+
};
82+
83+
} //! network
84+
85+
} //! cpp_redis

includes/cpp_redis/redis_client.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace cpp_redis {
1515
class redis_client {
1616
public:
1717
//! ctor & dtor
18-
redis_client(void);
18+
redis_client(const std::shared_ptr<network::io_service>& IO = nullptr);
1919
~redis_client(void);
2020

2121
//! copy ctor & assignment operator

includes/cpp_redis/redis_subscriber.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace cpp_redis {
1212
class redis_subscriber {
1313
public:
1414
//! ctor & dtor
15-
redis_subscriber(void);
15+
redis_subscriber(const std::shared_ptr<network::io_service>& IO = nullptr);
1616
~redis_subscriber(void);
1717

1818
//! copy ctor & assignment operator

0 commit comments

Comments
 (0)