├── .gitignore ├── CMakeLists.txt ├── License ├── README.md ├── bench ├── 100.png ├── 1000.png ├── CMakeLists.txt └── bench.cc ├── demo ├── CMakeLists.txt ├── echo_client.cc └── echo_server.cc ├── src ├── network │ ├── acceptor.cc │ ├── acceptor.h │ ├── async_client.cc │ ├── async_client.h │ ├── async_connection.cc │ ├── async_connection.h │ ├── async_server.cc │ ├── async_server.h │ ├── buffer.cc │ ├── buffer.h │ ├── callbacks.h │ ├── channel.cc │ ├── channel.h │ ├── connector.cc │ ├── connector.h │ ├── epoller.cc │ ├── epoller.h │ ├── event_pool.cc │ ├── event_pool.h │ ├── inet_address.cc │ ├── inet_address.h │ ├── socket.cc │ └── socket.h └── share │ ├── atomic.h │ ├── clock.cc │ ├── clock.h │ ├── condvar.h │ ├── log.cc │ ├── log.h │ ├── mutex.h │ ├── queue.h │ ├── slice.h │ ├── thread.cc │ ├── thread.h │ ├── threadpool.cc │ ├── threadpool.h │ └── utils.h └── test ├── CMakeLists.txt ├── net_buffer_test.cc ├── net_inet_address_test.cc ├── share_atomic_test.cc ├── share_queue_test.cc └── share_threadpool_test.cc /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | 3 | project(yohub CXX) 4 | 5 | if(NOT CMAKE_BUILD_TYPE) 6 | set(CMAKE_BUILD_TYPE "Debug") 7 | endif() 8 | 9 | set(CXX_FLAGS 10 | -g 11 | -Wall 12 | -march=native 13 | -rdynamic 14 | -std=c++0x 15 | ) 16 | 17 | string(REPLACE ";" " " CMAKE_CXX_FLAGS "${CXX_FLAGS}") 18 | 19 | set(CMAKE_CXX_COMPILER "g++") 20 | set(CMAKE_CXX_FLAGS_DEBUG "-O2") 21 | 22 | string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE) 23 | message(STATUS "CXX_FLAGS = " ${CMAKE_CXX_FLAGS} " " ${CMAKE_CXX_FLAGS_${BUILD_TYPE}}) 24 | 25 | find_package(Boost REQUIRED) 26 | 27 | include_directories(${Boost_INCLUDE_DIRS}) 28 | include_directories(${PROJECT_SOURCE_DIR}/src) 29 | 30 | set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) 31 | set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 32 | 33 | file(GLOB_RECURSE SOURCE 34 | ${CMAKE_CURRENT_SOURCE_DIR} 35 | ${CMAKE_CURRENT_SOURCE_DIR}/src/share/*.cc 36 | ${CMAKE_CURRENT_SOURCE_DIR}/src/network/*.cc 37 | ) 38 | 39 | add_library(yohub ${SOURCE}) 40 | target_link_libraries(yohub pthread rt) 41 | set_target_properties(yohub PROPERTIES OUTPUT_NAME yohub) 42 | install(TARGETS yohub DESTINATION lib) 43 | 44 | add_subdirectory(test) 45 | add_subdirectory(demo) 46 | add_subdirectory(bench) 47 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 kedebug 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | YoHub [![Total views](https://sourcegraph.com/api/repos/github.com/kedebug/yohub/counters/views.png)](https://sourcegraph.com/github.com/kedebug/yohub) 2 | ====== 3 | *A lightweight, asynchronous event-driven network application framework in C++.* 4 | 5 | ### Features 6 | * _Reactors with worker pool_. YoHub supports multiple reactors, each reactor will deliver its active events to the worker pool for execution. Reactor and worker pool are both configurable that allow you to build your robust network application. 7 | * _Asynchronous model_. The asynchronous programming model is inspired from [kylin](http://dirlt.com/kylin.html), yohub only notifies the client after the completion of I/O operations. Since the event loop is separated from the worker pool, edge-triggered readiness notification model is chosen for yohub. Nevertheless, edge-triggered readiness notification makes real-time response be possible and makes event handling be more efficient when cooperated with worker pool. 8 | * _Thread safe_. Synchronous id (`sync-id` for short) is introduced to yuhub. Each `sockfd` is associated with such a `sync-id`, and each `sync-id` is binded to a consistent thread. In this way, yohub is able to reduce the usage of synchronization primitives (ie. `mutex lock`), thus has improved the throughput. 9 | * _Smart pointer for connections_. For most scenarios, yohub uses `shared_ptr` to automatic memory management, which is inspired from [muduo](http://code.google.com/p/muduo/). YoHub takes advantage of smart pointers which can help us take care of the life cycle of those asychronous connections, and has greatly reduced our programming burden. 10 | 11 | ### Performance 12 | YoHub is designed to be a high-performance network application framework, which can be used to solve the C100K problem. The benchmark pragram is token from the [libevent-1.4.14b](http://libevent.org/) distribution, modified to compatible with the event pool in yohub. Both libevent and yohub are configured to use the epoll interface and run on a Core4 Quad CPU at 2.5GHz with Ubuntu 12.04 operating system. 13 | 14 | * __100 active clients.__ 15 | 16 | ![100](https://raw.githubusercontent.com/kedebug/yohub/master/bench/100.png) 17 | 18 | * __1000 active clients.__ 19 | 20 | ![1000](https://raw.githubusercontent.com/kedebug/yohub/master/bench/1000.png) 21 | 22 | The benchmark program takes the paramter `-n` equals to `-w`, which means every client will be notified to read and wirte. The report of our benchmark shows that yohub has lower costs than libevent-1.4.14b. With the increment of the file descriptor, this advantage can be obviously seen in above pictures. 23 | 24 | ### Usage 25 | To use yohub, boost library is required to be installed in your machine. After that: 26 | ```bash 27 | $ git clone git@github.com:kedebug/yohub.git 28 | $ cd yohub 29 | $ cmake . && make 30 | ``` 31 | Please see the files under [yohub/demo/](https://github.com/kedebug/yohub/tree/master/demo) directory for further usage. 32 | 33 | ### License 34 | The MIT License (MIT) 35 | 36 | Copyright (c) 2014 kedebug 37 | 38 | Permission is hereby granted, free of charge, to any person obtaining a copy 39 | of this software and associated documentation files (the "Software"), to deal 40 | in the Software without restriction, including without limitation the rights 41 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 42 | copies of the Software, and to permit persons to whom the Software is 43 | furnished to do so, subject to the following conditions: 44 | 45 | The above copyright notice and this permission notice shall be included in 46 | all copies or substantial portions of the Software. 47 | 48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 49 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 50 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 51 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 52 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 53 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 54 | THE SOFTWARE. 55 | -------------------------------------------------------------------------------- /bench/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kedebug/yohub/5cf68da73259c6fb75d35925ef44b1824efecc3f/bench/100.png -------------------------------------------------------------------------------- /bench/1000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kedebug/yohub/5cf68da73259c6fb75d35925ef44b1824efecc3f/bench/1000.png -------------------------------------------------------------------------------- /bench/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(bench bench.cc) 2 | target_link_libraries(bench yohub) 3 | -------------------------------------------------------------------------------- /bench/bench.cc: -------------------------------------------------------------------------------- 1 | #include "share/clock.h" 2 | #include "network/channel.h" 3 | #include "network/event_pool.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | using namespace yohub; 13 | 14 | int count, writes, fired; 15 | int num_pipes, num_active, num_writes; 16 | 17 | std::vector pipes; 18 | boost::ptr_vector chans; 19 | EventPool* event_pool; 20 | 21 | Mutex mu; 22 | CondVar cv(&mu); 23 | 24 | void ReadCallback(int fd, int idx) { 25 | char ch; 26 | count += ::recv(fd, &ch, sizeof(ch), 0); 27 | if (writes > 0) { 28 | int widx = idx + 1; 29 | if (widx >= num_pipes) { 30 | widx -= num_pipes; 31 | } 32 | ::send(pipes[2 * widx + 1], "m", 1, 0); 33 | writes--; 34 | fired++; 35 | } 36 | if (fired == count) { 37 | event_pool->Stop(); 38 | cv.Signal(); 39 | } 40 | } 41 | 42 | void RunOnce() { 43 | int64_t before = Clock::NowMicros(); 44 | 45 | for (int i = 0; i < num_pipes; i++) { 46 | Channel& chan = chans[i]; 47 | chan.SetReadCallback( 48 | boost::bind(ReadCallback, chan.fd(), i)); 49 | chan.Register(); 50 | } 51 | 52 | int space = num_pipes / num_active; 53 | space *= 2; 54 | for (int i = 0; i < num_active; i++) { 55 | ::send(pipes[i * space + 1], "m", 1, 0); 56 | } 57 | 58 | fired = num_active; 59 | count = 0; 60 | writes = num_writes; 61 | 62 | int64_t start = Clock::NowMicros(); 63 | 64 | event_pool->Run(); 65 | cv.Wait(); 66 | 67 | int64_t end = Clock::NowMicros(); 68 | 69 | fprintf(stdout, "%8ld %8ld\n", end - before, end - start); 70 | } 71 | 72 | int main(int argc, char* argv[]) { 73 | log::SetLogLevel(log::FATAL); 74 | 75 | num_pipes = 100; 76 | num_active = 1; 77 | num_writes = 100; 78 | 79 | int c; 80 | while ((c = getopt(argc, argv, "n:a:w:")) != -1) { 81 | switch (c) { 82 | case 'n': 83 | num_pipes = atoi(optarg); 84 | break; 85 | case 'a': 86 | num_active = atoi(optarg); 87 | break; 88 | case 'w': 89 | num_writes = atoi(optarg); 90 | break; 91 | default: 92 | fprintf(stderr, "Illegal argument `%c`\n", c); 93 | return 1; 94 | } 95 | } 96 | 97 | struct rlimit rl; 98 | rl.rlim_cur = rl.rlim_max = num_pipes * 2 + 50; 99 | if (::setrlimit(RLIMIT_NOFILE, &rl) == -1) { 100 | fprintf(stderr, "setrlimit error: %s", strerror(errno)); 101 | return 1; 102 | } 103 | 104 | pipes.resize(2 * num_pipes); 105 | for (int i = 0; i < num_pipes; i++) { 106 | if (::socketpair(AF_UNIX, SOCK_STREAM, 0, &pipes[i * 2]) == -1) { 107 | fprintf(stderr, "socketpair error: %s", strerror(errno)); 108 | return 1; 109 | } 110 | } 111 | 112 | EventPool pool(1, 1); 113 | event_pool = &pool; 114 | 115 | for (int i = 0; i < num_pipes; i++) { 116 | chans.push_back(new Channel(event_pool, pipes[i * 2])); 117 | } 118 | 119 | for (int i = 0; i < 25; i++) { 120 | RunOnce(); 121 | fprintf(stdout, "writes=%d, fired=%d, recv=%d\n", writes, fired, count); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /demo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(demo_echo_server echo_server.cc) 2 | target_link_libraries(demo_echo_server yohub) 3 | 4 | add_executable(demo_echo_client echo_client.cc) 5 | target_link_libraries(demo_echo_client yohub) 6 | -------------------------------------------------------------------------------- /demo/echo_client.cc: -------------------------------------------------------------------------------- 1 | #include "share/log.h" 2 | #include "network/event_pool.h" 3 | #include "network/async_client.h" 4 | #include "network/async_connection.h" 5 | #include "network/buffer.h" 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace yohub; 11 | 12 | static bool stop = false; 13 | 14 | class EchoClient { 15 | public: 16 | EchoClient(EventPool* event_pool, const InetAddress& remote) 17 | : event_pool_(event_pool), 18 | async_client_(event_pool_, remote) 19 | { 20 | async_client_.SetConnectionCallback( 21 | boost::bind(&EchoClient::OnConnection, this, _1)); 22 | async_client_.SetReadCompletionCallback( 23 | boost::bind(&EchoClient::OnReadCompletion, this, _1, _2)); 24 | } 25 | 26 | ~EchoClient() { 27 | async_client_.Disconnect(); 28 | } 29 | 30 | void Connect() { 31 | async_client_.Connect(); 32 | } 33 | 34 | private: 35 | void OnConnection(const AsyncConnectionPtr& conn) { 36 | LOG_TRACE("local=%s:%d, peer=%s:%d, %s", 37 | conn->local_addr().ip().c_str(), conn->local_addr().port(), 38 | conn->peer_addr().ip().c_str(), conn->peer_addr().port(), 39 | conn->connected() ? "connected" : "disconnected"); 40 | 41 | conn->Write(std::string(" world ")); 42 | } 43 | 44 | void OnReadCompletion(const AsyncConnectionPtr& conn, Buffer* buffer) { 45 | std::string s(buffer->TakeAsString()); 46 | LOG_TRACE("received: %s", s.c_str()); 47 | conn->Write(s.data(), s.size()); 48 | } 49 | 50 | EventPool* const event_pool_; 51 | AsyncClient async_client_; 52 | }; 53 | 54 | void SignalStop(int) { 55 | LOG_TRACE("Stop running..."); 56 | stop = true; 57 | } 58 | 59 | int main() { 60 | ::signal(SIGINT, SignalStop); 61 | 62 | log::SetLogLevel(log::TRACE); 63 | 64 | EventPool event_pool(1, 1); 65 | event_pool.Run(); 66 | 67 | InetAddress remote("127.0.0.1", 19910); 68 | EchoClient client(&event_pool, remote); 69 | client.Connect(); 70 | 71 | while (true) { 72 | if (stop) { 73 | event_pool.Stop(); 74 | break; 75 | } 76 | ::usleep(1000); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /demo/echo_server.cc: -------------------------------------------------------------------------------- 1 | #include "share/log.h" 2 | #include "network/event_pool.h" 3 | #include "network/async_server.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace yohub; 10 | 11 | class EchoServer { 12 | public: 13 | EchoServer(EventPool* event_pool, const InetAddress& bindaddr) 14 | : event_pool_(event_pool), 15 | async_server_(event_pool_, bindaddr) 16 | { 17 | async_server_.SetConnectionCallback( 18 | boost::bind(&EchoServer::OnConnection, this, _1)); 19 | async_server_.SetReadCompletionCallback( 20 | boost::bind(&EchoServer::OnReadCompletion, this, _1, _2)); 21 | } 22 | 23 | void Start() { 24 | async_server_.Start(); 25 | } 26 | 27 | private: 28 | void OnConnection(const AsyncConnectionPtr& conn) { 29 | LOG_TRACE("local=%s:%d, peer=%s:%d, %s", 30 | conn->local_addr().ip().c_str(), conn->local_addr().port(), 31 | conn->peer_addr().ip().c_str(), conn->peer_addr().port(), 32 | conn->connected() ? "connected" : "disconnected"); 33 | 34 | conn->Write(std::string(" hello ")); 35 | } 36 | 37 | void OnReadCompletion(const AsyncConnectionPtr& conn, Buffer* buffer) { 38 | std::string s(buffer->TakeAsString()); 39 | LOG_TRACE("received : %s", s.c_str()); 40 | conn->Write(s.data(), s.size()); 41 | } 42 | 43 | EventPool* event_pool_; 44 | AsyncServer async_server_; 45 | }; 46 | 47 | static bool stop = false; 48 | 49 | void SignalStop(int) { 50 | LOG_TRACE("Stop running..."); 51 | stop = true; 52 | } 53 | 54 | int main() { 55 | ::signal(SIGINT, SignalStop); 56 | 57 | log::SetLogLevel(log::TRACE); 58 | 59 | EventPool event_pool(1, 1); 60 | InetAddress bindaddr("127.0.0.1", 19910); 61 | event_pool.Run(); 62 | EchoServer server(&event_pool, bindaddr); 63 | server.Start(); 64 | 65 | while (true) { 66 | if (stop) { 67 | event_pool.Stop(); 68 | break; 69 | } 70 | ::usleep(1000); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/network/acceptor.cc: -------------------------------------------------------------------------------- 1 | #include "network/acceptor.h" 2 | #include "share/log.h" 3 | #include "share/atomic.h" 4 | #include 5 | #include 6 | 7 | using namespace yohub; 8 | 9 | Acceptor::Acceptor(EventPool* event_pool) 10 | : bind_(0), 11 | event_pool_(event_pool), 12 | accept_socket_(Socket::CreateNonblockingSocket()), 13 | accept_channel_(event_pool, accept_socket_.fd()), 14 | idle_fd_(::open("/dev/null", O_RDONLY | O_CLOEXEC)) 15 | { 16 | accept_channel_.SetReadCallback(boost::bind(&Acceptor::OnAccept, this)); 17 | } 18 | 19 | Acceptor::~Acceptor() { 20 | accept_channel_.Unregister(); 21 | } 22 | 23 | void Acceptor::SetAndBind(const InetAddress& bindaddr) { 24 | AtomicSetValue(bind_, 1); 25 | 26 | accept_socket_.SetReuseAddr(true); 27 | accept_socket_.SetTcpNoDelay(true); 28 | accept_socket_.Bind(bindaddr); 29 | } 30 | 31 | void Acceptor::Listen() { 32 | assert(AtomicGetValue(bind_)); 33 | 34 | accept_socket_.Listen(); 35 | accept_channel_.Register(); 36 | } 37 | 38 | void Acceptor::OnAccept() { 39 | InetAddress peeraddr(0); 40 | int newfd = accept_socket_.Accept(&peeraddr); 41 | 42 | if (newfd >= 0) { 43 | LOG_TRACE("new connection, fd=%d", newfd); 44 | if (on_new_connection_cb_) { 45 | on_new_connection_cb_(newfd, peeraddr); 46 | } else { 47 | ::close(newfd); 48 | LOG_WARN("the new connection callback should set"); 49 | } 50 | } else { 51 | LOG_WARN("OnAccept failed, fd=%d", accept_socket_.fd()); 52 | if (errno == EMFILE) { 53 | ::close(idle_fd_); 54 | idle_fd_ = ::accept(accept_socket_.fd(), NULL, NULL); 55 | ::close(idle_fd_); 56 | idle_fd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC); 57 | LOG_WARN("reached max file descriptor"); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/network/acceptor.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_NETWORK_ACCEPTOR_H_ 2 | #define _YOHUB_NETWORK_ACCEPTOR_H_ 3 | 4 | #include "network/channel.h" 5 | #include "network/socket.h" 6 | #include "network/inet_address.h" 7 | #include 8 | #include 9 | 10 | namespace yohub { 11 | 12 | class EventPool; 13 | 14 | class Acceptor : boost::noncopyable { 15 | public: 16 | typedef boost::function< 17 | void (int newfd, const InetAddress& peeraddr)> NewConnectionCallback; 18 | 19 | Acceptor(EventPool* event_pool); 20 | ~Acceptor(); 21 | 22 | void Listen(); 23 | void OnAccept(); 24 | 25 | void SetAndBind(const InetAddress& bindaddr); 26 | 27 | void SetNewConnectionCallback(const NewConnectionCallback& callback) { 28 | on_new_connection_cb_ = callback; 29 | } 30 | 31 | private: 32 | volatile int bind_; 33 | EventPool* event_pool_; 34 | Socket accept_socket_; 35 | Channel accept_channel_; 36 | int idle_fd_; 37 | 38 | NewConnectionCallback on_new_connection_cb_; 39 | }; 40 | 41 | } // namespace yohub 42 | 43 | #endif // _YOHUB_NETWORK_ACCEPTOR_H_ 44 | -------------------------------------------------------------------------------- /src/network/async_client.cc: -------------------------------------------------------------------------------- 1 | #include "share/atomic.h" 2 | #include "network/connector.h" 3 | #include "network/async_client.h" 4 | #include "network/socket.h" 5 | #include "network/async_connection.h" 6 | #include 7 | 8 | using namespace yohub; 9 | 10 | AsyncClient::AsyncClient(EventPool* event_pool, 11 | const InetAddress& remote) 12 | : event_pool_(event_pool), 13 | connector_(event_pool_, remote), 14 | connect_(0) 15 | { 16 | connector_.SetNewConnectionCallback( 17 | boost::bind(&AsyncClient::OnNewConnection, this, _1)); 18 | } 19 | 20 | AsyncClient::~AsyncClient() { 21 | MutexLock l(&mu_); 22 | if (client_conn_) { 23 | client_conn_->Destroy(); 24 | } 25 | } 26 | 27 | void AsyncClient::Connect() { 28 | AtomicSetValue(connect_, 1); 29 | connector_.Connect(); 30 | } 31 | 32 | void AsyncClient::Disconnect() { 33 | AtomicSetValue(connect_, 0); 34 | 35 | MutexLock l(&mu_); 36 | if (client_conn_) { 37 | client_conn_->Shutdown(); 38 | } 39 | } 40 | 41 | void AsyncClient::OnNewConnection(int sockfd) { 42 | InetAddress local(Socket::GetLocalSockAddr(sockfd)); 43 | InetAddress remote(Socket::GetPeerSockAddr(sockfd)); 44 | 45 | AsyncConnectionPtr new_conn( 46 | new AsyncConnection(event_pool_, sockfd, 0, local, remote)); 47 | 48 | new_conn->SetConnectionCallback(on_connection_cb_); 49 | new_conn->SetReadCompletionCallback(on_read_completion_cb_); 50 | new_conn->SetWriteCompletionCallback(on_write_completion_cb_); 51 | new_conn->SetCloseCallback( 52 | boost::bind(&AsyncClient::OnCloseConnection, this, _1)); 53 | 54 | new_conn->Establish(); 55 | 56 | MutexLock l(&mu_); 57 | client_conn_ = new_conn; 58 | } 59 | 60 | void AsyncClient::OnCloseConnection(const AsyncConnectionPtr& conn) { 61 | mu_.Lock(); 62 | assert(client_conn_ == conn); 63 | client_conn_.reset(); 64 | mu_.Unlock(); 65 | 66 | conn->Destroy(); 67 | } 68 | -------------------------------------------------------------------------------- /src/network/async_client.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_NETWORK_ASYNCCLIENT_H_ 2 | #define _YOHUB_NETWORK_ASYNCCLIENT_H_ 3 | 4 | #include "network/connector.h" 5 | #include "network/inet_address.h" 6 | #include "network/callbacks.h" 7 | #include "share/mutex.h" 8 | #include 9 | 10 | namespace yohub { 11 | 12 | class Connector; 13 | class EventPool; 14 | 15 | class AsyncClient : boost::noncopyable { 16 | public: 17 | AsyncClient(EventPool* event_pool, const InetAddress& remote); 18 | ~AsyncClient(); 19 | 20 | void Connect(); 21 | void Disconnect(); 22 | 23 | void SetConnectionCallback(const ConnectionCallback& cb) { 24 | on_connection_cb_ = cb; 25 | } 26 | void SetReadCompletionCallback(const ReadCompletionCallback& cb) { 27 | on_read_completion_cb_ = cb; 28 | } 29 | void SetWriteCompletionCallback(const WriteCompletionCallback& cb) { 30 | on_write_completion_cb_ = cb; 31 | } 32 | 33 | private: 34 | void OnNewConnection(int sockfd); 35 | void OnCloseConnection(const AsyncConnectionPtr& conn); 36 | 37 | EventPool* const event_pool_; 38 | Connector connector_; 39 | volatile int connect_; 40 | 41 | Mutex mu_; 42 | AsyncConnectionPtr client_conn_; 43 | 44 | ConnectionCallback on_connection_cb_; 45 | WriteCompletionCallback on_write_completion_cb_; 46 | ReadCompletionCallback on_read_completion_cb_; 47 | }; 48 | 49 | } // namespace yohub 50 | 51 | #endif // _YOHUB_NETWORK_ASYNCCLIENT_H_ 52 | -------------------------------------------------------------------------------- /src/network/async_connection.cc: -------------------------------------------------------------------------------- 1 | #include "network/async_connection.h" 2 | #include "network/event_pool.h" 3 | #include 4 | 5 | using namespace yohub; 6 | 7 | AsyncConnection::AsyncConnection(EventPool* event_pool, 8 | int socket_fd, 9 | int num_conns, 10 | const InetAddress& local_addr, 11 | const InetAddress& peer_addr) 12 | : event_pool_(event_pool), 13 | socket_(socket_fd), 14 | channel_(event_pool_, socket_fd), 15 | local_addr_(local_addr), 16 | peer_addr_(peer_addr), 17 | id_(num_conns), 18 | is_connected_(0) 19 | { 20 | socket_.SetKeepAlive(true); 21 | 22 | channel_.SetReadCallback(boost::bind(&AsyncConnection::OnRead, this)); 23 | channel_.SetWriteCallback(boost::bind(&AsyncConnection::OnWrite, this)); 24 | channel_.SetCloseCallback(boost::bind(&AsyncConnection::OnClose, this)); 25 | } 26 | 27 | AsyncConnection::~AsyncConnection() { 28 | channel_.Unregister(); 29 | LOG_TRACE("Connection destructor, %s:%d", 30 | peer_addr_.ip().c_str(), peer_addr_.port()); 31 | } 32 | 33 | void AsyncConnection::Write(const std::string& s) { 34 | Write(s.data(), s.size()); 35 | } 36 | 37 | void AsyncConnection::Write(const char* data, size_t size) { 38 | std::string s(data, size); 39 | event_pool_->PostJob( 40 | boost::bind(&AsyncConnection::QueueWrite, shared_from_this(), s), channel_); 41 | } 42 | 43 | void AsyncConnection::Establish() { 44 | if (AtomicSetValue(is_connected_, 1) == 0) { 45 | channel_.Register(); 46 | channel_.TieUp(shared_from_this()); 47 | on_connection_cb_(shared_from_this()); 48 | } else { 49 | LOG_WARN("Connection already established"); 50 | } 51 | } 52 | 53 | void AsyncConnection::Destroy() { 54 | if (AtomicSetValue(is_connected_, 0) == 1) { 55 | channel_.DisableAll(); 56 | on_connection_cb_(shared_from_this()); 57 | } 58 | } 59 | 60 | void AsyncConnection::Shutdown() { 61 | if (::shutdown(channel_.fd(), SHUT_WR) < 0) { 62 | LOG_WARN("shutdown error: %s", strerror(errno)); 63 | } 64 | } 65 | 66 | void AsyncConnection::QueueWrite(const std::string& s) { 67 | if (AtomicGetValue(is_connected_) == 0) { 68 | LOG_WARN("Stop writing: connection already destroyed."); 69 | return; 70 | } 71 | const char* data = s.data(); 72 | size_t size = s.size(); 73 | 74 | if (out_buffer_.ReadableBytes() == 0) { 75 | int result = ::write(channel_.fd(), data, size); 76 | if (result >= 0) { 77 | size -= result; 78 | if (size == 0 && on_write_completion_cb_) { 79 | event_pool_->PostJob(boost::bind( 80 | on_write_completion_cb_, shared_from_this()), channel_); 81 | } 82 | } else { 83 | if (errno == EAGAIN) { 84 | LOG_TRACE("Waiting for next write."); 85 | } else { 86 | LOG_WARN("write error: %s", strerror(errno)); 87 | } 88 | } 89 | } 90 | if (size > 0) { 91 | out_buffer_.Append(data + s.size() - size, size); 92 | } 93 | } 94 | 95 | void AsyncConnection::OnRead() { 96 | int saved_errno; 97 | int result = in_buffer_.ReadFd(channel_.fd(), &saved_errno); 98 | if (result > 0) { 99 | event_pool_->PostJob(boost::bind( 100 | on_read_completion_cb_, shared_from_this(), &in_buffer_), channel_); 101 | } else if (result == 0) { 102 | OnClose(); 103 | } else { 104 | if (saved_errno != EAGAIN) 105 | LOG_WARN("OnRead error occur, please check it."); 106 | } 107 | } 108 | 109 | void AsyncConnection::OnWrite() { 110 | if (out_buffer_.ReadableBytes() == 0) { 111 | return; 112 | } 113 | Slice slice = out_buffer_.ToSlice(); 114 | int result = ::write(channel_.fd(), slice.data(), slice.size()); 115 | 116 | if (result > 0) { 117 | out_buffer_.ReadableForward(result); 118 | if (out_buffer_.ReadableBytes() == 0) { 119 | if (on_write_completion_cb_) 120 | event_pool_->PostJob(boost::bind( 121 | on_write_completion_cb_, shared_from_this()), channel_); 122 | } 123 | } else { 124 | LOG_WARN("OnWrite failed, error: %s", strerror(errno)); 125 | } 126 | } 127 | 128 | void AsyncConnection::OnClose() { 129 | if (AtomicSetValue(is_connected_, 0) == 1) { 130 | channel_.DisableAll(); 131 | on_connection_cb_(shared_from_this()); 132 | on_close_cb_(shared_from_this()); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/network/async_connection.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_NETWORK_ASYNC_CONNECTION_H_ 2 | #define _YOHUB_NETWORK_ASYNC_CONNECTION_H_ 3 | 4 | #include "network/inet_address.h" 5 | #include "network/socket.h" 6 | #include "network/callbacks.h" 7 | #include "network/channel.h" 8 | #include "network/buffer.h" 9 | #include "share/atomic.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace yohub { 16 | 17 | class EventPool; 18 | 19 | class AsyncConnection : boost::noncopyable, 20 | public boost::enable_shared_from_this { 21 | public: 22 | AsyncConnection(EventPool* event_pool, 23 | int socket_fd, 24 | int num_conns, 25 | const InetAddress& local_addr, 26 | const InetAddress& peer_addr); 27 | ~AsyncConnection(); 28 | 29 | void Write(const std::string& s); 30 | void Write(const char* data, size_t size); 31 | 32 | void Establish(); 33 | void Destroy(); 34 | void Shutdown(); 35 | 36 | void SetConnectionCallback(const ConnectionCallback& callback) { 37 | on_connection_cb_ = callback; 38 | } 39 | 40 | void SetWriteCompletionCallback(const WriteCompletionCallback& callback) { 41 | on_write_completion_cb_ = callback; 42 | } 43 | 44 | void SetReadCompletionCallback(const ReadCompletionCallback& callback) { 45 | on_read_completion_cb_ = callback; 46 | } 47 | 48 | void SetCloseCallback(const CloseCallback& callback) { 49 | on_close_cb_ = callback; 50 | } 51 | 52 | const InetAddress& local_addr() { return local_addr_; } 53 | const InetAddress& peer_addr() { return peer_addr_; } 54 | 55 | int id() const { return id_; } 56 | bool connected() { return AtomicGetValue(is_connected_) == 1; } 57 | 58 | private: 59 | void QueueWrite(const std::string& s); 60 | 61 | void OnRead(); 62 | void OnWrite(); 63 | void OnClose(); 64 | 65 | EventPool* event_pool_; 66 | Socket socket_; 67 | Channel channel_; 68 | InetAddress local_addr_; 69 | InetAddress peer_addr_; 70 | 71 | const int id_; 72 | volatile int is_connected_; 73 | 74 | Buffer in_buffer_; 75 | Buffer out_buffer_; 76 | 77 | ConnectionCallback on_connection_cb_; 78 | WriteCompletionCallback on_write_completion_cb_; 79 | ReadCompletionCallback on_read_completion_cb_; 80 | CloseCallback on_close_cb_; 81 | }; 82 | 83 | } // namespace yohub 84 | 85 | #endif // _YOHUB_NETWORK_ASYNC_CONNECTION_H_ 86 | -------------------------------------------------------------------------------- /src/network/async_server.cc: -------------------------------------------------------------------------------- 1 | #include "share/log.h" 2 | #include "network/async_server.h" 3 | #include 4 | 5 | using namespace yohub; 6 | 7 | AsyncServer::AsyncServer(EventPool* event_pool, 8 | const InetAddress& bindaddr) 9 | : event_pool_(event_pool), 10 | started_(0), 11 | num_connections_(0), 12 | acceptor_(event_pool_), 13 | bind_addr_(bindaddr) 14 | { 15 | acceptor_.SetNewConnectionCallback( 16 | boost::bind(&AsyncServer::OnNewConnection, this, _1, _2)); 17 | } 18 | 19 | AsyncServer::~AsyncServer() { 20 | ConnectionMap connections; 21 | { 22 | MutexLock l(&mu_); 23 | connections.swap(connections_); 24 | } 25 | for (ConnectionMap::iterator iter = connections.begin(); 26 | iter != connections.end(); iter++) { 27 | iter->second->Destroy(); 28 | iter->second.reset(); 29 | } 30 | } 31 | 32 | void AsyncServer::Start() { 33 | if (AtomicSetValue(started_, 1) == 0) { 34 | acceptor_.SetAndBind(bind_addr_); 35 | acceptor_.Listen(); 36 | LOG_TRACE("server started, ip:port=%s:%u", 37 | bind_addr_.ip().c_str(), bind_addr_.port()); 38 | } 39 | } 40 | 41 | void AsyncServer::OnNewConnection(int sockfd, 42 | const InetAddress& peer) { 43 | InetAddress local(Socket::GetSocketName(sockfd)); 44 | AsyncConnectionPtr new_conn(new AsyncConnection( 45 | event_pool_, sockfd, AtomicInc(num_connections_), local, peer)); 46 | 47 | new_conn->SetConnectionCallback(on_connection_cb_); 48 | new_conn->SetWriteCompletionCallback(on_write_completion_cb_); 49 | new_conn->SetReadCompletionCallback(on_read_completion_cb_); 50 | new_conn->SetCloseCallback( 51 | boost::bind(&AsyncServer::OnCloseConnection, this, _1)); 52 | 53 | new_conn->Establish(); 54 | 55 | MutexLock l(&mu_); 56 | connections_[new_conn->id()] = new_conn; 57 | } 58 | 59 | void AsyncServer::OnCloseConnection(const AsyncConnectionPtr& conn) { 60 | { 61 | MutexLock l(&mu_); 62 | ConnectionMap::iterator iter = connections_.find(conn->id()); 63 | assert(iter != connections_.end()); 64 | connections_.erase(iter); 65 | } 66 | conn->Destroy(); 67 | } 68 | -------------------------------------------------------------------------------- /src/network/async_server.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_NETWORK_ASYNC_SERVER_H_ 2 | #define _YOHUB_NETWORK_ASYNC_SERVER_H_ 3 | 4 | #include "network/event_pool.h" 5 | #include "network/async_connection.h" 6 | #include "network/acceptor.h" 7 | #include "network/callbacks.h" 8 | #include "share/mutex.h" 9 | #include 10 | #include 11 | 12 | namespace yohub { 13 | 14 | class AsyncServer : boost::noncopyable { 15 | public: 16 | typedef std::map ConnectionMap; 17 | 18 | AsyncServer(EventPool* event_pool, const InetAddress& bindaddr); 19 | ~AsyncServer(); 20 | 21 | void Start(); 22 | 23 | void SetConnectionCallback(const ConnectionCallback& callback) { 24 | on_connection_cb_ = callback; 25 | } 26 | 27 | void SetWriteCompletionCallback(const WriteCompletionCallback& callback) { 28 | on_write_completion_cb_ = callback; 29 | } 30 | 31 | void SetReadCompletionCallback(const ReadCompletionCallback& callback) { 32 | on_read_completion_cb_ = callback; 33 | } 34 | 35 | private: 36 | // our acceptor only runs on a fixed thread. 37 | void OnNewConnection(int sockfd, const InetAddress& peeraddr); 38 | void OnCloseConnection(const AsyncConnectionPtr& conn); 39 | 40 | EventPool* event_pool_; 41 | volatile int started_; 42 | volatile int num_connections_; 43 | Acceptor acceptor_; 44 | InetAddress bind_addr_; 45 | Mutex mu_; 46 | ConnectionMap connections_; 47 | 48 | ConnectionCallback on_connection_cb_; 49 | WriteCompletionCallback on_write_completion_cb_; 50 | ReadCompletionCallback on_read_completion_cb_; 51 | }; 52 | 53 | } // namespace yohub 54 | 55 | #endif // _YOHUB_NETWORK_ASYNC_SERVER_H_ 56 | -------------------------------------------------------------------------------- /src/network/buffer.cc: -------------------------------------------------------------------------------- 1 | #include "network/buffer.h" 2 | #include "share/log.h" 3 | #include 4 | #include 5 | 6 | using namespace yohub; 7 | 8 | Buffer::Buffer() 9 | : mem_(1024), 10 | reader_idx_(0), 11 | writer_idx_(0) 12 | { } 13 | 14 | int Buffer::ReadFd(int fd, int* saved_errno) { 15 | char reserve[65536]; 16 | int total = 0; 17 | 18 | while (true) { 19 | int result = ::read(fd, reserve, sizeof(reserve)); 20 | 21 | if (result == -1) { 22 | *saved_errno = errno; 23 | if (errno != EAGAIN) { 24 | LOG_WARN("read error occur: %s", strerror(errno)); 25 | } 26 | return total > 0 ? total : result; 27 | } else if (result == 0) { 28 | return result; 29 | } else { 30 | assert(result > 0); 31 | total += result; 32 | Append(reserve, result); 33 | } 34 | } 35 | } 36 | 37 | void Buffer::Append(const char* data, size_t size) { 38 | EnsureWritableBytes(size); 39 | std::copy(data, data + size, begin() + writer_idx_); 40 | writer_idx_ += size; 41 | } 42 | 43 | void Buffer::EnsureWritableBytes(size_t require) { 44 | if (WritableBytes() >= require) { 45 | return; 46 | } 47 | 48 | if (reader_idx_ + WritableBytes() >= require) { 49 | int temp = ReadableBytes(); 50 | std::copy(begin() + reader_idx_, begin() + writer_idx_, begin()); 51 | reader_idx_ = 0; 52 | writer_idx_ = temp; 53 | } else { 54 | mem_.resize(writer_idx_ + require); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/network/buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_NETWORK_BUFFER_H_ 2 | #define _YOHUB_NETWORK_BUFFER_H_ 3 | 4 | #include 5 | #include 6 | #include "share/slice.h" 7 | 8 | namespace yohub { 9 | 10 | class Buffer { 11 | public: 12 | Buffer(); 13 | 14 | int ReadFd(int fd, int* saved_errno); 15 | 16 | void Append(const char* data, size_t bytes); 17 | 18 | void ReadableForward(size_t n) { 19 | assert(reader_idx_ + n < writer_idx_); 20 | reader_idx_ += n; 21 | } 22 | 23 | size_t ReadableBytes() const { 24 | return writer_idx_ - reader_idx_; 25 | } 26 | 27 | size_t WritableBytes() const { 28 | return mem_.size() - writer_idx_; 29 | } 30 | 31 | std::string TakeAsString() { 32 | std::string result(begin() + reader_idx_, ReadableBytes()); 33 | reader_idx_ = 0; 34 | writer_idx_ = 0; 35 | return result; 36 | } 37 | 38 | const Slice ToSlice() const { 39 | return Slice(begin() + reader_idx_, ReadableBytes()); 40 | } 41 | 42 | private: 43 | void EnsureWritableBytes(size_t require); 44 | 45 | char* begin() { return &*mem_.begin(); } 46 | const char* begin() const { return &*mem_.begin(); } 47 | 48 | std::vector mem_; 49 | size_t reader_idx_; 50 | size_t writer_idx_; 51 | }; 52 | 53 | } // namespace yohub 54 | 55 | #endif // _YOHUB_NETWORK_BUFFER_H_ 56 | -------------------------------------------------------------------------------- /src/network/callbacks.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_NETWORK_CALLBACKS_H_ 2 | #define _YOHUB_NETWORK_CALLBACKS_H_ 3 | 4 | #include 5 | #include 6 | 7 | namespace yohub { 8 | 9 | class Buffer; 10 | class AsyncConnection; 11 | 12 | typedef boost::shared_ptr AsyncConnectionPtr; 13 | 14 | typedef boost::function ConnectionCallback; 15 | typedef boost::function WriteCompletionCallback; 16 | typedef boost::function ReadCompletionCallback; 17 | typedef boost::function CloseCallback; 18 | 19 | } // namespace yohub 20 | 21 | #endif // _YOHUB_NETWORK_CALLBACKS_H_ 22 | -------------------------------------------------------------------------------- /src/network/channel.cc: -------------------------------------------------------------------------------- 1 | #include "network/channel.h" 2 | #include "network/event_pool.h" 3 | #include 4 | 5 | using namespace yohub; 6 | 7 | volatile uint32_t Channel::s_sequence_number_ = 0; 8 | 9 | Channel::Channel(EventPool* event_pool, int fd) 10 | : id_(AtomicInc(s_sequence_number_)), 11 | fd_(fd), 12 | events_(EPOLLIN | EPOLLOUT | EPOLLRDHUP | EPOLLERR | EPOLLET), 13 | revents_(0), 14 | status_(0), 15 | tied_(false), 16 | event_pool_(event_pool) 17 | { 18 | } 19 | 20 | Channel::~Channel() { 21 | LOG_TRACE("channel destructor, id=%d, fd=%d", id_, fd_); 22 | } 23 | 24 | void Channel::Register() { 25 | event_pool_->AttachChannel(this); 26 | } 27 | 28 | void Channel::Unregister() { 29 | event_pool_->DetachChannel(this); 30 | } 31 | 32 | void Channel::DisableAll() { 33 | event_pool_->DisableChannel(this); 34 | } 35 | 36 | void Channel::TieUp(const boost::shared_ptr& obj) { 37 | tied_ = true; 38 | obj_tied_ = obj; 39 | } 40 | 41 | void Channel::SetStatus(int status) { 42 | AtomicSetValue(status_, status); 43 | } 44 | 45 | void Channel::SetReadyEvents(int revents) { 46 | AtomicSetValue(revents_, revents); 47 | } 48 | 49 | void Channel::EventHandler() { 50 | if (tied_) { 51 | boost::shared_ptr guard = obj_tied_.lock(); 52 | if (guard) 53 | SafeEventHandler(); 54 | } else { 55 | SafeEventHandler(); 56 | } 57 | } 58 | 59 | void Channel::SafeEventHandler() { 60 | int revents = AtomicGetValue(revents_); 61 | 62 | LOG_TRACE("id=%u, fd=%d: %s", 63 | id_, fd_, EventsToString(revents).c_str()); 64 | 65 | if (revents & EPOLLRDHUP) { 66 | if (close_callback_) close_callback_(); 67 | } 68 | if (revents & (EPOLLIN | EPOLLPRI)) { 69 | if (read_callback_) read_callback_(); 70 | } 71 | if (revents & EPOLLOUT) { 72 | if (write_callback_) write_callback_(); 73 | } 74 | } 75 | 76 | std::string Channel::EventsToString(int events) { 77 | std::string result; 78 | 79 | if (events & EPOLLIN) result += "IN "; 80 | if (events & EPOLLOUT) result += "OUT "; 81 | if (events & EPOLLPRI) result += "PRI "; 82 | if (events & EPOLLHUP) result += "HUP "; 83 | if (events & EPOLLRDHUP) result += "RDHUP "; 84 | if (events & EPOLLERR) result += "ERR "; 85 | 86 | return result; 87 | } 88 | -------------------------------------------------------------------------------- /src/network/channel.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_NETWORK_CHANNEL_H_ 2 | #define _YOHUB_NETWORK_CHANNEL_H_ 3 | 4 | #include "share/atomic.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace yohub { 12 | 13 | class EventPool; 14 | 15 | class Channel : boost::noncopyable { 16 | public: 17 | typedef boost::function CallbackFn; 18 | 19 | Channel(EventPool* event_pool, int fd); 20 | ~Channel(); 21 | 22 | void Register(); 23 | void Unregister(); 24 | void DisableAll(); 25 | 26 | void TieUp(const boost::shared_ptr& obj); 27 | 28 | void EventHandler(); 29 | 30 | void SetStatus(int status); 31 | void SetReadyEvents(int revents); 32 | 33 | int fd() const { return fd_; } 34 | uint32_t id() const { return id_; } 35 | int events() const { return events_; } 36 | int status() { return AtomicGetValue(status_); } 37 | 38 | void SetReadCallback(const CallbackFn& callback_fn) { 39 | read_callback_ = callback_fn; 40 | } 41 | 42 | void SetWriteCallback(const CallbackFn& callback_fn) { 43 | write_callback_ = callback_fn; 44 | } 45 | 46 | void SetCloseCallback(const CallbackFn& callback_fn) { 47 | close_callback_ = callback_fn; 48 | } 49 | 50 | static std::string EventsToString(int events); 51 | 52 | private: 53 | void SafeEventHandler(); 54 | 55 | static volatile uint32_t s_sequence_number_; 56 | 57 | const uint32_t id_; 58 | const int fd_; 59 | const int events_; 60 | volatile int revents_; 61 | volatile int status_; 62 | boost::weak_ptr obj_tied_; 63 | bool tied_; 64 | EventPool* event_pool_; 65 | 66 | CallbackFn read_callback_; 67 | CallbackFn write_callback_; 68 | CallbackFn close_callback_; 69 | }; 70 | 71 | } // namespace yohub 72 | 73 | #endif // _YOHUB_NETWORK_CHANNEL_H_ 74 | -------------------------------------------------------------------------------- /src/network/connector.cc: -------------------------------------------------------------------------------- 1 | #include "network/connector.h" 2 | #include "network/event_pool.h" 3 | #include "network/socket.h" 4 | #include 5 | 6 | using namespace yohub; 7 | 8 | Connector::Connector(EventPool* event_pool, 9 | const InetAddress& remote_addr) 10 | : event_pool_(event_pool), 11 | remote_addr_(remote_addr), 12 | connect_(0), 13 | status_(kDisconnected) 14 | { 15 | } 16 | 17 | Connector::~Connector() { 18 | } 19 | 20 | void Connector::Connect() { 21 | if (AtomicGetValue(status_) == kDisconnected) { 22 | AtomicSetValue(connect_, 1); 23 | event_pool_->PostJob(boost::bind(&Connector::QueueConnect, this)); 24 | } 25 | } 26 | 27 | void Connector::QueueConnect() { 28 | int newfd = Socket::CreateNonblockingSocket(); 29 | int result = Socket::Connect(newfd, remote_addr_.sockaddr_in()); 30 | if (result == 0 || errno == EINPROGRESS) { 31 | Connecting(newfd); 32 | } else { 33 | ::close(newfd); 34 | LOG_WARN("connect error: %s", strerror(errno)); 35 | } 36 | } 37 | 38 | void Connector::Connecting(int sockfd) { 39 | AtomicSetValue(status_, kConnecting); 40 | 41 | conn_channel_.reset(new Channel(event_pool_, sockfd)); 42 | conn_channel_->SetWriteCallback( 43 | boost::bind(&Connector::OnConnect, this)); 44 | 45 | conn_channel_->Register(); 46 | } 47 | 48 | void Connector::OnConnect() { 49 | if (AtomicSetValue(status_, kConnected) == kConnecting) { 50 | conn_channel_->Unregister(); 51 | int fd = conn_channel_->fd(); 52 | if (AtomicGetValue(connect_) == 1) 53 | on_new_connection_cb_(fd); 54 | else 55 | ::close(fd); 56 | } else { 57 | LOG_WARN("OnConnect error occur"); 58 | } 59 | } 60 | 61 | void Connector::Retry() { 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/network/connector.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_NETWORK_CONNECTOR_H_ 2 | #define _YOHUB_NETWORK_CONNECTOR_H_ 3 | 4 | #include "network/inet_address.h" 5 | #include 6 | #include 7 | #include 8 | 9 | namespace yohub { 10 | 11 | class Channel; 12 | class EventPool; 13 | 14 | class Connector : boost::noncopyable { 15 | public: 16 | typedef boost::function NewConnectionCallback; 17 | 18 | Connector(EventPool* event_pool, const InetAddress& remote); 19 | ~Connector(); 20 | 21 | void Connect(); 22 | void Retry(); 23 | 24 | void SetNewConnectionCallback(const NewConnectionCallback& cb) { 25 | on_new_connection_cb_ = cb; 26 | } 27 | 28 | private: 29 | enum { kDisconnected, kConnecting, kConnected }; 30 | 31 | void OnConnect(); 32 | void QueueConnect(); 33 | void Connecting(int sockfd); 34 | 35 | EventPool* event_pool_; 36 | InetAddress remote_addr_; 37 | volatile int connect_; 38 | volatile int status_; 39 | 40 | boost::scoped_ptr conn_channel_; 41 | NewConnectionCallback on_new_connection_cb_; 42 | }; 43 | 44 | } // namespace yohub 45 | 46 | #endif // _YOHUB_NETWORK_CONNECTOR_H_ 47 | -------------------------------------------------------------------------------- /src/network/epoller.cc: -------------------------------------------------------------------------------- 1 | #include "share/log.h" 2 | #include "network/epoller.h" 3 | #include "network/channel.h" 4 | #include 5 | 6 | using namespace yohub; 7 | 8 | namespace { 9 | const int kNew = 0; 10 | const int kAdded = 1; 11 | const int kDeleted = 2; 12 | } 13 | 14 | EPoller::EPoller() 15 | : epoll_fd_(::epoll_create1(EPOLL_CLOEXEC)), 16 | events_(1024) 17 | { 18 | } 19 | 20 | EPoller::~EPoller() { 21 | ::close(epoll_fd_); 22 | } 23 | 24 | void EPoller::Poll(int timeout_ms, ChannelList* active_channels) { 25 | int num_events = ::epoll_wait( 26 | epoll_fd_, &*events_.begin(), events_.size(), timeout_ms); 27 | 28 | if (num_events > 0) { 29 | active_channels->reserve(num_events); 30 | for (int i = 0; i < num_events; i++) { 31 | Channel* channel = static_cast(events_[i].data.ptr); 32 | active_channels->push_back(channel); 33 | channel->SetReadyEvents(events_[i].events); 34 | } 35 | if (num_events == static_cast(events_.size())) { 36 | events_.resize(num_events << 1); 37 | } 38 | } else if (num_events == 0) { 39 | LOG_TRACE("nothing happened in epoll_wait"); 40 | } 41 | } 42 | 43 | void EPoller::AttachChannel(Channel* channel) { 44 | struct epoll_event ev; 45 | 46 | memset(&ev, 0, sizeof(ev)); 47 | ev.events = channel->events(); 48 | ev.data.ptr = channel; 49 | 50 | channel->SetStatus(kAdded); 51 | 52 | if (::epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, channel->fd(), &ev) < 0) { 53 | LOG_WARN("epoll_ctl_add error: %s", strerror(errno)); 54 | } 55 | } 56 | 57 | void EPoller::DetachChannel(Channel* channel) { 58 | channel->SetStatus(kDeleted); 59 | 60 | if (::epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, channel->fd(), NULL) < 0) { 61 | LOG_WARN("epoll_ctl_del error: %s", strerror(errno)); 62 | } 63 | } 64 | 65 | void EPoller::DisableChannel(Channel* channel) { 66 | if (channel->status() == kAdded) { 67 | struct epoll_event ev; 68 | 69 | memset(&ev, 0, sizeof(ev)); 70 | ev.events = 0; 71 | ev.data.ptr = channel; 72 | 73 | if (::epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, channel->fd(), &ev) < 0) { 74 | LOG_WARN("epoll_ctl_mod error: %s", strerror(errno)); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/network/epoller.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_NETWORK_EPOLLER_H_ 2 | #define _YOHUB_NETWORK_EPOLLER_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace yohub { 10 | 11 | class Channel; 12 | class EPoller : boost::noncopyable { 13 | public: 14 | typedef std::map ChannelMap; 15 | typedef std::vector ChannelList; 16 | typedef std::vector EventList; 17 | 18 | EPoller(); 19 | ~EPoller(); 20 | 21 | void Poll(int timeout_ms, ChannelList* active_channels); 22 | void Close(); 23 | void AttachChannel(Channel* channel); 24 | void DetachChannel(Channel* channel); 25 | void DisableChannel(Channel* channel); 26 | 27 | private: 28 | int epoll_fd_; 29 | ChannelMap channels_; 30 | EventList events_; 31 | }; 32 | 33 | } // namespace yohub 34 | 35 | #endif // _YOHUB_NETWORK_EPOLLER_H_ 36 | -------------------------------------------------------------------------------- /src/network/event_pool.cc: -------------------------------------------------------------------------------- 1 | #include "network/event_pool.h" 2 | #include "network/socket.h" 3 | #include 4 | #include 5 | 6 | using namespace yohub; 7 | 8 | namespace { 9 | const int kPollTimeMs = 10000; 10 | } 11 | 12 | EventPool::EventPool(int pollers, int backends) 13 | : num_pollers_(pollers), 14 | num_backends_(backends) 15 | { 16 | poller_handler_.Start(num_pollers_); 17 | backend_handler_.Start(num_backends_); 18 | 19 | for (int i = 0; i < num_pollers_; i++) { 20 | int wakeup_fd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); 21 | wakeup_socks_.push_back(new Socket(wakeup_fd)); 22 | wakeup_chans_.push_back(new Channel(this, wakeup_fd)); 23 | 24 | pollers_.push_back(new EPoller); 25 | pollers_.back().AttachChannel(&(wakeup_chans_.back())); 26 | } 27 | } 28 | 29 | EventPool::~EventPool() { 30 | if (AtomicGetValue(running_) == 1) { 31 | Stop(); 32 | } 33 | poller_handler_.Stop(); 34 | backend_handler_.Stop(); 35 | } 36 | 37 | void EventPool::Run() { 38 | AtomicSetValue(running_, 1); 39 | 40 | for (int i = 0; i < num_pollers_; i++) { 41 | poller_handler_.Schedule( 42 | boost::bind(&EventPool::PollWrapper, this, i), i); 43 | } 44 | } 45 | 46 | void EventPool::Stop() { 47 | if (AtomicSetValue(running_, 0) == 1) { 48 | WakeUp(); 49 | } 50 | } 51 | 52 | void EventPool::WakeUp() { 53 | for (size_t i = 0; i < wakeup_socks_.size(); i++) { 54 | uint64_t val = 1; 55 | int result = ::write(wakeup_socks_[i].fd(), &val, sizeof(val)); 56 | if (result != sizeof(val)) { 57 | LOG_WARN("WakeUp error: %s", strerror(errno)); 58 | } 59 | } 60 | } 61 | 62 | void EventPool::PostJob(const Job& job, int which) { 63 | backend_handler_.Schedule(job, which); 64 | } 65 | 66 | void EventPool::PostJob(const Job& job, const Channel& channel) { 67 | backend_handler_.Schedule(job, channel.id() % num_backends_); 68 | } 69 | 70 | void EventPool::PollWrapper(int which) { 71 | ChannelList active_channels; 72 | EPoller& poller = pollers_[which]; 73 | 74 | while (AtomicGetValue(running_) == 1) { 75 | active_channels.clear(); 76 | poller.Poll(kPollTimeMs, &active_channels); 77 | 78 | for (ChannelList::iterator iter = active_channels.begin(); 79 | iter != active_channels.end(); iter++) { 80 | PostJob(boost::bind(&Channel::EventHandler, *iter), **iter); 81 | } 82 | } 83 | } 84 | 85 | void EventPool::AttachChannel(Channel* channel) { 86 | int which = channel->id() % num_pollers_; 87 | pollers_[which].AttachChannel(channel); 88 | } 89 | 90 | void EventPool::DetachChannel(Channel* channel) { 91 | int which = channel->id() % num_pollers_; 92 | pollers_[which].DetachChannel(channel); 93 | } 94 | 95 | void EventPool::DisableChannel(Channel* channel) { 96 | int which = channel->id() % num_pollers_; 97 | pollers_[which].DisableChannel(channel); 98 | } 99 | -------------------------------------------------------------------------------- /src/network/event_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_NETWORK_EVENTPOOL_H_ 2 | #define _YOHUB_NETWORK_EVENTPOOL_H_ 3 | 4 | #include "network/epoller.h" 5 | #include "network/channel.h" 6 | #include "network/socket.h" 7 | #include "share/threadpool.h" 8 | #include 9 | #include 10 | 11 | namespace yohub { 12 | 13 | class EventPool : boost::noncopyable { 14 | public: 15 | typedef ThreadPool::Job Job; 16 | typedef EPoller::ChannelList ChannelList; 17 | 18 | EventPool(int pollers, int backends); 19 | ~EventPool(); 20 | 21 | void Run(); 22 | void Stop(); 23 | 24 | void WakeUp(); 25 | 26 | void AttachChannel(Channel* channel); 27 | void DetachChannel(Channel* channel); 28 | void DisableChannel(Channel* channel); 29 | 30 | // choose the first thread by default 31 | void PostJob(const Job& job, int which = 0); 32 | void PostJob(const Job& job, const Channel& channel); 33 | 34 | void PollWrapper(int which); 35 | 36 | private: 37 | volatile int running_; 38 | const int num_pollers_; 39 | const int num_backends_; 40 | 41 | boost::ptr_vector pollers_; 42 | boost::ptr_vector wakeup_socks_; 43 | boost::ptr_vector wakeup_chans_; 44 | ThreadPool poller_handler_; 45 | ThreadPool backend_handler_; 46 | }; 47 | 48 | } // namespace yohub 49 | 50 | #endif // _YOHUB_NETWORK_EVENTPOOL_H_ 51 | -------------------------------------------------------------------------------- /src/network/inet_address.cc: -------------------------------------------------------------------------------- 1 | #include "network/inet_address.h" 2 | #include "share/log.h" 3 | 4 | using namespace yohub; 5 | 6 | InetAddress::InetAddress(const struct sockaddr_in& inet_addr) 7 | : inet_addr_(inet_addr) 8 | { 9 | } 10 | 11 | InetAddress::InetAddress(Port port) { 12 | memset(&inet_addr_, 0, sizeof(inet_addr_)); 13 | inet_addr_.sin_family = AF_INET; 14 | inet_addr_.sin_addr.s_addr = INADDR_ANY; 15 | inet_addr_.sin_port = htons(port); 16 | } 17 | 18 | InetAddress::InetAddress(const std::string& ip, Port port) { 19 | memset(&inet_addr_, 0, sizeof(inet_addr_)); 20 | inet_addr_.sin_family = AF_INET; 21 | inet_addr_.sin_port = htons(port); 22 | ::inet_pton(AF_INET, ip.c_str(), &inet_addr_.sin_addr); 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/network/inet_address.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_NETWORK_INET_ADDRESS_H_ 2 | #define _YOHUB_NETWORK_INET_ADDRESS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace yohub { 9 | 10 | class InetAddress { 11 | public: 12 | typedef uint16_t Port; 13 | 14 | InetAddress(Port port); 15 | InetAddress(const std::string& ip, Port port); 16 | InetAddress(const sockaddr_in& inet_addr); 17 | 18 | void SetSockAddr(const sockaddr_in& inet_addr) { 19 | inet_addr_ = inet_addr; 20 | } 21 | 22 | uint16_t port() const { 23 | return ntohs(inet_addr_.sin_port); 24 | } 25 | 26 | std::string ip() const { 27 | char ip[32]; 28 | ::inet_ntop(AF_INET, &inet_addr_.sin_addr, ip, sizeof(ip)); 29 | return ip; 30 | } 31 | 32 | const struct sockaddr_in& sockaddr_in() const { 33 | return inet_addr_; 34 | } 35 | 36 | private: 37 | struct sockaddr_in inet_addr_; 38 | }; 39 | 40 | } // namespace yohub 41 | 42 | #endif // _YOHUB_NETWORK_INET_ADDRESS_H_ 43 | -------------------------------------------------------------------------------- /src/network/socket.cc: -------------------------------------------------------------------------------- 1 | #include "network/socket.h" 2 | #include "network/inet_address.h" 3 | #include "share/log.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace yohub; 10 | 11 | Socket::Socket(int fd) 12 | : socket_fd_(fd) 13 | { 14 | } 15 | 16 | Socket::~Socket() { 17 | ::close(socket_fd_); 18 | } 19 | 20 | void Socket::Bind(const InetAddress& addr) { 21 | int result = ::bind(socket_fd_, 22 | reinterpret_cast(&addr.sockaddr_in()), 23 | static_cast(sizeof(sockaddr_in))); 24 | if (result < 0) { 25 | LOG_FATAL("bind failed, error: %s", strerror(errno)); 26 | } 27 | } 28 | 29 | void Socket::Listen() { 30 | int result = ::listen(socket_fd_, SOMAXCONN); 31 | if (result < 0) { 32 | LOG_FATAL("listen failed, error: %s", strerror(errno)); 33 | } 34 | } 35 | 36 | int Socket::Accept(InetAddress* peeraddr) { 37 | retry: 38 | struct sockaddr_in sa; 39 | socklen_t len = sizeof(sa); 40 | 41 | memset(&sa, 0, sizeof(sa)); 42 | int newfd = ::accept4(socket_fd_, reinterpret_cast(&sa), &len, 43 | SOCK_NONBLOCK | SOCK_CLOEXEC); 44 | 45 | if (newfd < 0) { 46 | if (errno == EAGAIN || errno == ECONNABORTED) 47 | goto retry; 48 | LOG_WARN("accept failed, error: %s", strerror(errno)); 49 | } else { 50 | peeraddr->SetSockAddr(sa); 51 | } 52 | return newfd; 53 | } 54 | 55 | void Socket::SetTcpNoDelay(bool opt) { 56 | int value = opt ? 1 : 0; 57 | ::setsockopt(socket_fd_, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)); 58 | } 59 | 60 | void Socket::SetReuseAddr(bool opt) { 61 | int value = opt ? 1 : 0; 62 | ::setsockopt(socket_fd_, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); 63 | } 64 | 65 | void Socket::SetReusePort(bool opt) { 66 | #ifdef SO_REUSEPORT 67 | int value = opt ? 1 : 0; 68 | ::setsockopt(socket_fd_, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value)); 69 | #endif 70 | } 71 | 72 | void Socket::SetKeepAlive(bool opt) { 73 | int value = opt ? 1 : 0; 74 | ::setsockopt(socket_fd_, SOL_SOCKET, SO_KEEPALIVE, &value, sizeof(value)); 75 | } 76 | 77 | int Socket::CreateNonblockingSocket() { 78 | int newfd = ::socket(AF_INET, SOCK_NONBLOCK | SOCK_CLOEXEC | SOCK_STREAM, IPPROTO_TCP); 79 | if (newfd < 0) { 80 | LOG_FATAL("socket failed, error: %s", strerror(errno)); 81 | } 82 | return newfd; 83 | } 84 | 85 | int Socket::Connect(int fd, const struct sockaddr_in& sa) { 86 | return ::connect(fd, reinterpret_cast(&sa), sizeof(sa)); 87 | } 88 | 89 | struct sockaddr_in Socket::GetSocketName(int sockfd) { 90 | struct sockaddr_in sa; 91 | socklen_t len = sizeof(sa); 92 | 93 | memset(&sa, 0, sizeof(sa)); 94 | if (::getsockname(sockfd, reinterpret_cast(&sa), &len) < 0) { 95 | LOG_WARN("getsockname failed, error: %s", strerror(errno)); 96 | } 97 | return sa; 98 | } 99 | 100 | struct sockaddr_in Socket::GetLocalSockAddr(int sockfd) { 101 | return GetSocketName(sockfd); 102 | } 103 | 104 | struct sockaddr_in Socket::GetPeerSockAddr(int sockfd) { 105 | struct sockaddr_in sa; 106 | socklen_t len = sizeof(sa); 107 | 108 | memset(&sa, 0, sizeof(sa)); 109 | if (::getpeername(sockfd, reinterpret_cast(&sa), &len) < 0) { 110 | LOG_WARN("getpeername failed, error: %s", strerror(errno)); 111 | } 112 | return sa; 113 | } 114 | -------------------------------------------------------------------------------- /src/network/socket.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_NETWORK_SOCKET_H_ 2 | #define _YOHUB_NETWORK_SOCKET_H_ 3 | 4 | #include 5 | #include 6 | 7 | namespace yohub { 8 | 9 | class InetAddress; 10 | 11 | class Socket : boost::noncopyable { 12 | public: 13 | Socket(int fd); 14 | ~Socket(); 15 | 16 | void Bind(const InetAddress& address); 17 | void Listen(); 18 | int Accept(InetAddress* peer_address); 19 | 20 | void SetTcpNoDelay(bool opt); 21 | void SetReuseAddr(bool opt); 22 | void SetReusePort(bool opt); 23 | void SetKeepAlive(bool opt); 24 | 25 | int fd() const { return socket_fd_; } 26 | 27 | static int CreateNonblockingSocket(); 28 | static int Connect(int fd, const struct sockaddr_in& addr); 29 | static struct sockaddr_in GetSocketName(int sockfd); 30 | static struct sockaddr_in GetLocalSockAddr(int sockfd); 31 | static struct sockaddr_in GetPeerSockAddr(int sockfd); 32 | 33 | private: 34 | const int socket_fd_; 35 | }; 36 | 37 | } // namespace yohub 38 | 39 | #endif // _YOHUB_NETWORK_SOCKET_H_ 40 | -------------------------------------------------------------------------------- /src/share/atomic.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_SHARE_ATOMIC_H_ 2 | #define _YOHUB_SHARE_ATOMIC_H_ 3 | 4 | #include 5 | 6 | namespace yohub { 7 | 8 | // return old value 9 | static inline int atomic_add(volatile void* count, int add) { 10 | __asm__ __volatile__( 11 | "lock xadd %0, (%1);" 12 | : "=a"(add) 13 | : "r"(count), "a"(add) 14 | : "memory"); 15 | return add; 16 | } 17 | 18 | // return old value 19 | static inline int atomic_swap(volatile void* lockword, int value) { 20 | __asm__ __volatile__( 21 | "lock xchg %0, (%1);" 22 | : "=a"(value) 23 | : "r"(lockword), "a"(value) 24 | : "memory"); 25 | return value; 26 | } 27 | 28 | // return old value 29 | static inline int atomic_comp_swap(volatile void* lockword, 30 | int exchange, 31 | int comperand) { 32 | __asm__ __volatile__( 33 | "lock cmpxchg %1, (%2);" 34 | : "=a"(comperand) 35 | : "d"(exchange), "r"(lockword), "a"(comperand)); 36 | return comperand; 37 | } 38 | 39 | #define AtomicInc(x) (atomic_add(&(x), 1) + 1) 40 | #define AtomicDec(x) (atomic_add(&(x), -1) - 1) 41 | #define AtomicGetValue(x) (atomic_comp_swap(&(x), 0, 0)) 42 | #define AtomicSetValue(x, v) (atomic_swap(&(x), (v))) 43 | 44 | } // namespace yohub 45 | 46 | #endif // _YOHUB_SHARE_ATOMIC_H_ 47 | -------------------------------------------------------------------------------- /src/share/clock.cc: -------------------------------------------------------------------------------- 1 | #include "share/clock.h" 2 | #include 3 | #include 4 | 5 | using namespace yohub; 6 | 7 | Clock::Clock(int64_t now_ms) 8 | : now_ms_(now_ms) 9 | { 10 | } 11 | 12 | int64_t Clock::NowMicros() { 13 | struct timeval tv; 14 | gettimeofday(&tv, NULL); 15 | int64_t sec = tv.tv_sec; 16 | return sec * kMsPerSecond + tv.tv_usec; 17 | } 18 | -------------------------------------------------------------------------------- /src/share/clock.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_SHARE_CLOCK_H_ 2 | #define _YOHUB_SHARE_CLOCK_H_ 3 | 4 | #include 5 | 6 | namespace yohub { 7 | 8 | class Clock { 9 | public: 10 | explicit Clock(int64_t now_ms); 11 | 12 | static int64_t NowMicros(); 13 | 14 | int64_t now_ms() { return now_ms_; } 15 | 16 | private: 17 | int64_t now_ms_; 18 | 19 | static const int kMsPerSecond = 1e6; 20 | }; 21 | 22 | } // namespace yohub 23 | 24 | #endif // _YOHUB_SHARE_CLOCK_H_ 25 | -------------------------------------------------------------------------------- /src/share/condvar.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_SHARE_CONDVAR_H_ 2 | #define _YOHUB_SHARE_CONDVAR_H_ 3 | 4 | #include 5 | #include "share/mutex.h" 6 | 7 | namespace yohub { 8 | 9 | class CondVar : boost::noncopyable { 10 | public: 11 | explicit CondVar(Mutex* mutex) 12 | : mutex_(mutex) { 13 | utils::PthreadCall("init cv", pthread_cond_init(&cv_, NULL)); 14 | } 15 | 16 | ~CondVar() { 17 | utils::PthreadCall("destroy cv", pthread_cond_destroy(&cv_)); 18 | } 19 | 20 | void Wait() { 21 | utils::PthreadCall("wait", pthread_cond_wait(&cv_, &mutex_->mtx_)); 22 | } 23 | 24 | bool TimedWait(int seconds) { 25 | struct timespec abstime; 26 | clock_gettime(CLOCK_REALTIME, &abstime); 27 | abstime.tv_sec += seconds; 28 | return ETIMEDOUT == pthread_cond_timedwait(&cv_, &mutex_->mtx_, &abstime); 29 | } 30 | 31 | void Signal() { 32 | utils::PthreadCall("signal", pthread_cond_signal(&cv_)); 33 | } 34 | 35 | void SignalAll() { 36 | utils::PthreadCall("broadcast", pthread_cond_broadcast(&cv_)); 37 | } 38 | 39 | private: 40 | pthread_cond_t cv_; 41 | Mutex* mutex_; 42 | }; 43 | 44 | } // namespace yohub 45 | 46 | #endif // _YOHUB_SHARE_CONDVAR_H_ 47 | -------------------------------------------------------------------------------- /src/share/log.cc: -------------------------------------------------------------------------------- 1 | #include "share/log.h" 2 | 3 | namespace yohub { 4 | namespace log { 5 | 6 | LogLevel g_loglevel = TRACE; 7 | 8 | } // namespace log 9 | } // namespace yohub 10 | 11 | using namespace yohub::log; 12 | 13 | // void SetLogLevel(LogLevel level) { 14 | // g_loglevel = level; 15 | // } 16 | // 17 | // LogLevel GetLogLevel() { 18 | // return g_loglevel; 19 | // } 20 | -------------------------------------------------------------------------------- /src/share/log.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_SHARE_LOG_H_ 2 | #define _YOHUB_SHARE_LOG_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace yohub { 9 | 10 | namespace log { 11 | 12 | enum LogLevel { 13 | NOTICE, TRACE, DEBUG, WARN, FATAL 14 | }; 15 | 16 | extern LogLevel g_loglevel; 17 | 18 | inline LogLevel GetLogLevel() { return g_loglevel; } 19 | inline void SetLogLevel(LogLevel level) { g_loglevel = level; } 20 | 21 | } // namespace log 22 | 23 | 24 | #define LOG_NOTICE(fmt, ...) if (yohub::log::GetLogLevel() <= yohub::log::NOTICE) \ 25 | fprintf(stderr, "[NOTICE][%s:%d]" fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) 26 | #define LOG_TRACE(fmt, ...) if (yohub::log::GetLogLevel() <= yohub::log::TRACE) \ 27 | fprintf(stderr, "[TRACE][%s:%d]" fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) 28 | #define LOG_DEBUG(fmt, ...) if (yohub::log::GetLogLevel() <= yohub::log::DEBUG) \ 29 | fprintf(stderr, "[DEBUG][%s:%d]" fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) 30 | #define LOG_WARN(fmt, ...) if (yohub::log::GetLogLevel() <= yohub::log::WARN) \ 31 | fprintf(stderr, "[WARN][%s:%d]" fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) 32 | #define LOG_FATAL(fmt, ...) if (yohub::log::GetLogLevel() <= yohub::log::FATAL) \ 33 | fprintf(stderr, "[FATAL][%s:%d]" fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__);abort(); 34 | 35 | } // namespace yohub 36 | 37 | #endif // _YOHUB_SHARE_LOG_H_ 38 | -------------------------------------------------------------------------------- /src/share/mutex.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_SHARE_MUTEX_H_ 2 | #define _YOHUB_SHARE_MUTEX_H_ 3 | 4 | #include 5 | #include 6 | #include "share/utils.h" 7 | 8 | namespace yohub { 9 | 10 | class CondVar; 11 | 12 | class Mutex : boost::noncopyable { 13 | public: 14 | Mutex() { 15 | utils::PthreadCall("init mutex", pthread_mutex_init(&mtx_, NULL)); 16 | } 17 | 18 | ~Mutex() { 19 | utils::PthreadCall("destroy mutex", pthread_mutex_destroy(&mtx_)); 20 | } 21 | 22 | void Lock() { 23 | utils::PthreadCall("mutex lock", pthread_mutex_lock(&mtx_)); 24 | } 25 | 26 | void Unlock() { 27 | utils::PthreadCall("mutex unlock", pthread_mutex_unlock(&mtx_)); 28 | } 29 | 30 | void AssertHeld() { } 31 | 32 | private: 33 | friend class CondVar; 34 | pthread_mutex_t mtx_; 35 | }; 36 | 37 | class MutexLock : boost::noncopyable { 38 | public: 39 | explicit MutexLock(Mutex* mu) 40 | : mutex_(mu) { 41 | this->mutex_->Lock(); 42 | } 43 | 44 | ~MutexLock() { 45 | this->mutex_->Unlock(); 46 | } 47 | 48 | private: 49 | Mutex *const mutex_; 50 | }; 51 | 52 | } // namespace yohub 53 | 54 | #endif // _YOHUB_SHARE_MUTEX_H_ 55 | -------------------------------------------------------------------------------- /src/share/queue.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_SHARE_QUEUE_H_ 2 | #define _YOHUB_SHARE_QUEUE_H_ 3 | 4 | #include "share/mutex.h" 5 | #include "share/condvar.h" 6 | #include 7 | 8 | namespace yohub { 9 | 10 | template 11 | class Queue { 12 | public: 13 | typedef std::vector Container; 14 | 15 | Queue() 16 | : mu_(), 17 | cv_(&mu_), 18 | valid_(true) 19 | { } 20 | 21 | bool Push(const T& x) { 22 | MutexLock l(&mu_); 23 | if (!valid_) { 24 | return false; 25 | } 26 | if (elems_.empty()) { 27 | cv_.Signal(); 28 | } 29 | elems_.push_back(x); 30 | return true; 31 | } 32 | 33 | bool FetchAll(Container* c, int wait_seconds = -1) { 34 | MutexLock l(&mu_); 35 | while (elems_.empty()) { 36 | if (wait_seconds == -1) { 37 | cv_.Wait(); 38 | } else { 39 | if (cv_.TimedWait(wait_seconds)) 40 | return false; 41 | } 42 | } 43 | c->swap(elems_); 44 | return true; 45 | } 46 | 47 | size_t size() { 48 | MutexLock l(&mu_); 49 | return elems_.size(); 50 | } 51 | 52 | void SetInvalid() { 53 | MutexLock l(&mu_); 54 | valid_ = false; 55 | } 56 | 57 | bool valid() { 58 | MutexLock l(&mu_); 59 | return valid_; 60 | } 61 | 62 | private: 63 | Mutex mu_; 64 | CondVar cv_; 65 | Container elems_; 66 | bool valid_; 67 | }; 68 | 69 | } // namespace yohub 70 | 71 | #endif // _YOHUB_JOBQUEUE_H_ 72 | -------------------------------------------------------------------------------- /src/share/slice.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_SHARE_SLICE_H_ 2 | #define _YOHUB_SHARE_SLICE_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace yohub { 9 | 10 | class Slice { 11 | public: 12 | Slice() : data_(""), size_(0) { } 13 | Slice(const char* d, size_t n) : data_(d), size_(n) { } 14 | Slice(const std::string& s) : data_(s.data()), size_(s.size()) { } 15 | 16 | const char* data() const { return data_; } 17 | size_t size() const { return size_; } 18 | void clear() { data_= ""; size_ = 0; } 19 | 20 | std::string ToString() const { return std::string(data_, size_); } 21 | 22 | private: 23 | const char* data_; 24 | size_t size_; 25 | }; 26 | 27 | } // namespace yohub 28 | 29 | #endif // _YOHUB_SHARE_SLICE_H_ 30 | -------------------------------------------------------------------------------- /src/share/thread.cc: -------------------------------------------------------------------------------- 1 | #include "share/thread.h" 2 | #include "share/utils.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace yohub; 9 | 10 | namespace { 11 | __thread int t_cached_tid = 0; 12 | } 13 | 14 | static void CacheTid() { 15 | if (t_cached_tid == 0) { 16 | t_cached_tid = static_cast(::syscall(SYS_gettid)); 17 | } 18 | } 19 | 20 | Thread::Thread(const BGWorker& worker) 21 | : started_(false), 22 | joined_(false), 23 | pid_(0), 24 | worker_(worker) 25 | { 26 | } 27 | 28 | Thread::~Thread() { 29 | if (started_ && !joined_) { 30 | utils::PthreadCall("detach thread", pthread_detach(pid_)); 31 | } 32 | } 33 | 34 | namespace { 35 | struct StartThreadState { 36 | Thread::BGWorker worker; 37 | }; 38 | } 39 | 40 | static void* BGWorkerWrapper(void* arg) { 41 | StartThreadState* state = reinterpret_cast(arg); 42 | state->worker(); 43 | delete state; 44 | return NULL; 45 | } 46 | 47 | void Thread::Start() { 48 | StartThreadState* state = new StartThreadState; 49 | state->worker = worker_; 50 | 51 | assert(!started_); 52 | started_ = true; 53 | utils::PthreadCall("start thread", 54 | pthread_create(&pid_, NULL, &BGWorkerWrapper, state)); 55 | } 56 | 57 | void Thread::Join() { 58 | assert(started_); 59 | assert(!joined_); 60 | joined_ = true; 61 | utils::PthreadCall("join thread", pthread_join(pid_, NULL)); 62 | } 63 | 64 | int Thread::SelfId() { 65 | CacheTid(); 66 | return t_cached_tid; 67 | } 68 | -------------------------------------------------------------------------------- /src/share/thread.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_SHARE_THREAD_H_ 2 | #define _YOHUB_SHARE_THREAD_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace yohub { 9 | 10 | class Thread : boost::noncopyable { 11 | public: 12 | typedef boost::function BGWorker; 13 | 14 | explicit Thread(const BGWorker& worker); 15 | ~Thread(); 16 | 17 | void Start(); 18 | void Join(); 19 | 20 | static int SelfId(); 21 | 22 | private: 23 | bool started_; 24 | bool joined_; 25 | pthread_t pid_; 26 | BGWorker worker_; 27 | }; 28 | 29 | } // namespace yohub 30 | 31 | #endif // _YOHUB_SHARE_THREAD_H_ 32 | -------------------------------------------------------------------------------- /src/share/threadpool.cc: -------------------------------------------------------------------------------- 1 | #include "share/threadpool.h" 2 | #include "share/log.h" 3 | #include 4 | #include 5 | 6 | using namespace yohub; 7 | 8 | ThreadPool::ThreadPool() 9 | : running_(0), 10 | workers_(0), 11 | job_count_(0) 12 | { 13 | } 14 | 15 | ThreadPool::~ThreadPool() { 16 | if (AtomicGetValue(running_)) { 17 | Stop(); 18 | } 19 | } 20 | 21 | void ThreadPool::Start(int workers) { 22 | assert(!running_); 23 | assert(threads_.empty()); 24 | 25 | AtomicSetValue(running_, 1); 26 | AtomicSetValue(workers_, workers); 27 | 28 | threads_.reserve(workers); 29 | for (int i = 0; i < workers; i++) { 30 | queues_.push_back(new JobQueue); 31 | threads_.push_back(new Thread( 32 | boost::bind(&ThreadPool::WorkHandler, this, i))); 33 | threads_.back().Start(); 34 | } 35 | } 36 | 37 | void ThreadPool::Stop() { 38 | if (AtomicSetValue(running_, 0) == 1) { 39 | for (size_t i = 0; i < threads_.size(); i++) { 40 | bool ok = queues_[i].Push( 41 | boost::bind(&ThreadPool::TerminationJob, this)); 42 | if (ok) { 43 | AtomicInc(job_count_); 44 | } 45 | } 46 | for (size_t i = 0; i < threads_.size(); i++) { 47 | threads_[i].Join(); 48 | } 49 | assert(AtomicGetValue(job_count_) == 0); 50 | } 51 | } 52 | 53 | void ThreadPool::WorkHandler(int which) { 54 | JobQueue::Container jobs; 55 | while (AtomicGetValue(running_) == 1 56 | || queues_[which].size()) { 57 | jobs.clear(); 58 | queues_[which].FetchAll(&jobs); 59 | 60 | for (size_t i = 0; i < jobs.size(); i++) { 61 | jobs[i](); 62 | AtomicDec(job_count_); 63 | } 64 | } 65 | queues_[which].SetInvalid(); 66 | } 67 | 68 | bool ThreadPool::Schedule(const Job& job, int which) { 69 | if (AtomicGetValue(running_) == 0) { 70 | return false; 71 | } 72 | int n = AtomicInc(job_count_); 73 | if (which == -1) { 74 | which = n % workers_; 75 | } 76 | if (queues_[which].Push(job)) { 77 | return true; 78 | } else { 79 | AtomicDec(job_count_); 80 | return false; 81 | } 82 | } 83 | 84 | void ThreadPool::TerminationJob() { 85 | LOG_TRACE("TerminationJob scheduled..."); 86 | } 87 | -------------------------------------------------------------------------------- /src/share/threadpool.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_SHARE_THREADPOOL_H_ 2 | #define _YOHUB_SHARE_THREADPOOL_H_ 3 | 4 | #include "share/atomic.h" 5 | #include "share/thread.h" 6 | #include "share/mutex.h" 7 | #include "share/queue.h" 8 | #include 9 | #include 10 | #include 11 | 12 | namespace yohub { 13 | 14 | class ThreadPool : boost::noncopyable { 15 | public: 16 | typedef boost::function Job; 17 | typedef Queue JobQueue; 18 | 19 | ThreadPool(); 20 | ~ThreadPool(); 21 | 22 | void Start(int workers); 23 | void Stop(); 24 | 25 | bool Schedule(const Job& job, int which = -1); 26 | 27 | int workers() { 28 | return AtomicGetValue(workers_); 29 | } 30 | 31 | private: 32 | void WorkHandler(int which); 33 | void TerminationJob(); 34 | 35 | volatile int running_; 36 | volatile int workers_; 37 | volatile int job_count_; 38 | 39 | boost::ptr_vector queues_; 40 | boost::ptr_vector threads_; 41 | }; 42 | 43 | } // namespace yohub 44 | 45 | #endif // _YOHUB_SHARE_THREADPOOL_H_ 46 | -------------------------------------------------------------------------------- /src/share/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _YOHUB_SHARE_UTILS_H_ 2 | #define _YOHUB_SHARE_UTILS_H_ 3 | 4 | #include 5 | #include "share/log.h" 6 | 7 | namespace yohub { 8 | namespace utils { 9 | 10 | inline void PthreadCall(const char* label, int result) { 11 | if (result != 0) { 12 | LOG_FATAL("pthread %s: %s\n", label, strerror(result)); 13 | } 14 | } 15 | 16 | } // namespace utils 17 | } // namespace yohub 18 | 19 | #endif // _YOHUB_SHARE_UTILS_H_ 20 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(share_atomic_test share_atomic_test.cc) 2 | target_link_libraries(share_atomic_test yohub) 3 | 4 | add_executable(share_queue_test share_queue_test.cc) 5 | target_link_libraries(share_queue_test yohub) 6 | 7 | add_executable(share_threadpool_test share_threadpool_test.cc) 8 | target_link_libraries(share_threadpool_test yohub) 9 | 10 | add_executable(net_buffer_test net_buffer_test.cc) 11 | target_link_libraries(net_buffer_test yohub) 12 | 13 | add_executable(net_inet_address_test net_inet_address_test.cc) 14 | target_link_libraries(net_inet_address_test yohub) 15 | -------------------------------------------------------------------------------- /test/net_buffer_test.cc: -------------------------------------------------------------------------------- 1 | #include "network/buffer.h" 2 | #include 3 | #include 4 | 5 | using namespace yohub; 6 | 7 | int main() { 8 | Buffer buffer; 9 | buffer.Append("12345", 5); 10 | buffer.Append("12345", 5); 11 | 12 | std::string s = buffer.TakeAsString(); 13 | printf("result=%s, expected=%s\n", 14 | s.c_str(), "1234512345"); 15 | printf("buffer.size=%zd, expected=%d\n", 16 | buffer.WritableBytes(), 10); 17 | } 18 | -------------------------------------------------------------------------------- /test/net_inet_address_test.cc: -------------------------------------------------------------------------------- 1 | #include "network/inet_address.h" 2 | #include 3 | 4 | using namespace yohub; 5 | 6 | int main() { 7 | InetAddress addr0(1234); 8 | assert(addr0.ip() == std::string("0.0.0.0")); 9 | assert(addr0.port() == 1234); 10 | 11 | InetAddress addr1("1.2.3.4", 8888); 12 | assert(addr1.ip() == std::string("1.2.3.4")); 13 | assert(addr1.port() == 8888); 14 | 15 | InetAddress addr2("255.254.253.252", 65535); 16 | assert(addr2.ip() == std::string("255.254.253.252")); 17 | assert(addr2.port() == 65535); 18 | } 19 | -------------------------------------------------------------------------------- /test/share_atomic_test.cc: -------------------------------------------------------------------------------- 1 | #include "share/atomic.h" 2 | #include "share/thread.h" 3 | #include "share/log.h" 4 | 5 | using namespace yohub; 6 | 7 | volatile bool flag; 8 | volatile int count; 9 | 10 | void atomic_test() { 11 | while (flag) { 12 | AtomicInc(count); 13 | AtomicDec(count); 14 | } 15 | } 16 | 17 | int main() { 18 | count = 0; 19 | flag = true; 20 | 21 | Thread t1(atomic_test); 22 | Thread t2(atomic_test); 23 | Thread t3(atomic_test); 24 | 25 | t1.Start(); 26 | t2.Start(); 27 | t3.Start(); 28 | 29 | ::sleep(2); 30 | flag = false; 31 | 32 | t1.Join(); 33 | t2.Join(); 34 | t3.Join(); 35 | 36 | LOG_TRACE("count=%d, expected 0", count); 37 | } 38 | -------------------------------------------------------------------------------- /test/share_queue_test.cc: -------------------------------------------------------------------------------- 1 | #include "share/thread.h" 2 | #include "share/queue.h" 3 | #include "share/atomic.h" 4 | #include 5 | #include 6 | 7 | using namespace yohub; 8 | 9 | Queue g_queue; 10 | volatile int g_flag; 11 | volatile int g_count; 12 | 13 | void FetchProc() { 14 | int sum = 0; 15 | int times = 0; 16 | while (g_flag) { 17 | std::vector take; 18 | g_queue.FetchAll(&take, 1); 19 | times++; 20 | sum += take.size(); 21 | } 22 | printf("sum = %d, times = %d\n", sum, times); 23 | } 24 | 25 | void PutProc() { 26 | while (g_flag) { 27 | g_queue.Push(1); 28 | AtomicInc(g_count); 29 | } 30 | } 31 | 32 | int main() { 33 | g_flag = 1; 34 | g_count = 0; 35 | 36 | Thread thr1(FetchProc); 37 | Thread thr2(PutProc); 38 | Thread thr3(PutProc); 39 | 40 | thr1.Start(); 41 | thr2.Start(); 42 | thr3.Start(); 43 | 44 | sleep(2); 45 | AtomicSetValue(g_flag, 0); 46 | 47 | thr1.Join(); 48 | thr2.Join(); 49 | thr3.Join(); 50 | 51 | printf("put num: %d\n", g_count); 52 | } 53 | -------------------------------------------------------------------------------- /test/share_threadpool_test.cc: -------------------------------------------------------------------------------- 1 | #include "share/log.h" 2 | #include "share/atomic.h" 3 | #include "share/threadpool.h" 4 | 5 | #include 6 | #include 7 | 8 | using namespace yohub; 9 | 10 | int stop = 0; 11 | int num_gets = 0; 12 | int num_posts = 0; 13 | ThreadPool thread_pool; 14 | 15 | void SignalStop(int) { 16 | AtomicSetValue(stop, 1); 17 | thread_pool.Stop(); 18 | LOG_TRACE("Actually: %d, expected: %d", num_gets, num_posts); 19 | } 20 | 21 | void Get() { 22 | AtomicInc(num_gets); 23 | } 24 | 25 | void Post() { 26 | while (AtomicGetValue(stop) == 0) { 27 | if (thread_pool.Schedule(boost::bind(&Get))) { 28 | AtomicInc(num_posts); 29 | } 30 | } 31 | } 32 | 33 | int main() { 34 | ::signal(SIGINT, SignalStop); 35 | 36 | thread_pool.Start(4); 37 | 38 | Thread thr1(boost::bind(&Post)); 39 | Thread thr2(boost::bind(&Post)); 40 | 41 | thr1.Start(); 42 | thr2.Start(); 43 | 44 | thr1.Join(); 45 | thr2.Join(); 46 | 47 | LOG_TRACE("Process exit successfully."); 48 | } 49 | --------------------------------------------------------------------------------