initial commit

This commit is contained in:
2025-08-05 15:53:44 +08:00
commit 09dc02ae52
553 changed files with 137665 additions and 0 deletions

15
third_party/libhv/evpp/Buffer.h vendored Executable file
View File

@@ -0,0 +1,15 @@
#ifndef HV_BUFFER_HPP_
#define HV_BUFFER_HPP_
#include <memory>
#include "hbuf.h"
namespace hv {
typedef HBuf Buffer;
typedef std::shared_ptr<Buffer> BufferPtr;
}
#endif // HV_BUFFER_HPP_

377
third_party/libhv/evpp/Channel.h vendored Executable file
View File

@@ -0,0 +1,377 @@
#ifndef HV_CHANNEL_HPP_
#define HV_CHANNEL_HPP_
#include <string>
#include <functional>
#include <memory>
#include <atomic>
#include "hloop.h"
#include "hsocket.h"
#include "Buffer.h"
namespace hv {
class Channel {
public:
Channel(hio_t* io = NULL) {
io_ = io;
fd_ = -1;
id_ = 0;
ctx_ = NULL;
status = CLOSED;
if (io) {
fd_ = hio_fd(io);
id_ = hio_id(io);
ctx_ = hio_context(io);
hio_set_context(io, this);
if (hio_is_opened(io)) {
status = OPENED;
}
if (hio_getcb_read(io) == NULL) {
hio_setcb_read(io_, on_read);
}
if (hio_getcb_write(io) == NULL) {
hio_setcb_write(io_, on_write);
}
if (hio_getcb_close(io) == NULL) {
hio_setcb_close(io_, on_close);
}
}
}
virtual ~Channel() {
if (isOpened()) {
close();
// NOTE: Detach after destructor to avoid triggering onclose
if (io_ && id_ == hio_id(io_)) {
hio_set_context(io_, NULL);
}
}
}
hio_t* io() { return io_; }
int fd() { return fd_; }
uint32_t id() { return id_; }
int error() { return hio_error(io_); }
// context
void* context() {
return ctx_;
}
void setContext(void* ctx) {
ctx_ = ctx;
}
template<class T>
T* newContext() {
ctx_ = new T;
return (T*)ctx_;
}
template<class T>
T* getContext() {
return (T*)ctx_;
}
template<class T>
void deleteContext() {
if (ctx_) {
delete (T*)ctx_;
ctx_ = NULL;
}
}
// contextPtr
std::shared_ptr<void> contextPtr() {
return contextPtr_;
}
void setContextPtr(const std::shared_ptr<void>& ctx) {
contextPtr_ = ctx;
}
void setContextPtr(std::shared_ptr<void>&& ctx) {
contextPtr_ = std::move(ctx);
}
template<class T>
std::shared_ptr<T> newContextPtr() {
contextPtr_ = std::make_shared<T>();
return std::static_pointer_cast<T>(contextPtr_);
}
template<class T>
std::shared_ptr<T> getContextPtr() {
return std::static_pointer_cast<T>(contextPtr_);
}
void deleteContextPtr() {
contextPtr_.reset();
}
bool isOpened() {
if (io_ == NULL || status >= DISCONNECTED) return false;
return id_ == hio_id(io_) && hio_is_opened(io_);
}
bool isClosed() {
return !isOpened();
}
int startRead() {
if (!isOpened()) return -1;
return hio_read_start(io_);
}
int stopRead() {
if (!isOpened()) return -1;
return hio_read_stop(io_);
}
int readOnce() {
if (!isOpened()) return -1;
return hio_read_once(io_);
}
int readString() {
if (!isOpened()) return -1;
return hio_readstring(io_);
}
int readLine() {
if (!isOpened()) return -1;
return hio_readline(io_);
}
int readBytes(int len) {
if (!isOpened() || len <= 0) return -1;
return hio_readbytes(io_, len);
}
// write thread-safe
int write(const void* data, int size) {
if (!isOpened()) return -1;
return hio_write(io_, data, size);
}
int write(Buffer* buf) {
return write(buf->data(), buf->size());
}
int write(const std::string& str) {
return write(str.data(), str.size());
}
// iobuf setting
void setReadBuf(void* buf, size_t len) {
if (io_ == NULL) return;
hio_set_readbuf(io_, buf, len);
}
void setMaxReadBufsize(uint32_t size) {
if (io_ == NULL) return;
hio_set_max_read_bufsize(io_, size);
}
void setMaxWriteBufsize(uint32_t size) {
if (io_ == NULL) return;
hio_set_max_write_bufsize(io_, size);
}
size_t writeBufsize() {
if (io_ == NULL) return 0;
return hio_write_bufsize(io_);
}
bool isWriteComplete() {
return writeBufsize() == 0;
}
// close thread-safe
int close(bool async = false) {
if (isClosed()) return -1;
status = CLOSED;
return async ? hio_close_async(io_) : hio_close(io_);
}
public:
hio_t* io_;
int fd_;
uint32_t id_;
void* ctx_;
enum Status {
OPENED,
CONNECTING,
CONNECTED,
DISCONNECTED,
CLOSED,
};
std::atomic<Status> status;
std::function<void(Buffer*)> onread;
// NOTE: Use Channel::isWriteComplete in onwrite callback to determine whether all data has been written.
std::function<void(Buffer*)> onwrite;
std::function<void()> onclose;
std::shared_ptr<void> contextPtr_;
private:
static void on_read(hio_t* io, void* data, int readbytes) {
Channel* channel = (Channel*)hio_context(io);
if (channel && channel->onread) {
Buffer buf(data, readbytes);
channel->onread(&buf);
}
}
static void on_write(hio_t* io, const void* data, int writebytes) {
Channel* channel = (Channel*)hio_context(io);
if (channel && channel->onwrite) {
Buffer buf((void*)data, writebytes);
channel->onwrite(&buf);
}
}
static void on_close(hio_t* io) {
Channel* channel = (Channel*)hio_context(io);
if (channel) {
channel->status = CLOSED;
if (channel->onclose) {
channel->onclose();
}
}
}
};
class SocketChannel : public Channel {
public:
std::function<void()> onconnect; // only for TcpClient
std::function<void()> heartbeat;
SocketChannel(hio_t* io) : Channel(io) {
}
virtual ~SocketChannel() {}
// SSL/TLS
int enableSSL() {
if (io_ == NULL) return -1;
return hio_enable_ssl(io_);
}
bool isSSL() {
if (io_ == NULL) return false;
return hio_is_ssl(io_);
}
int setSSL(hssl_t ssl) {
if (io_ == NULL) return -1;
return hio_set_ssl(io_, ssl);
}
int setSslCtx(hssl_ctx_t ssl_ctx) {
if (io_ == NULL) return -1;
return hio_set_ssl_ctx(io_, ssl_ctx);
}
int newSslCtx(hssl_ctx_opt_t* opt) {
if (io_ == NULL) return -1;
return hio_new_ssl_ctx(io_, opt);
}
// for hssl_set_sni_hostname
int setHostname(const std::string& hostname) {
if (io_ == NULL) return -1;
return hio_set_hostname(io_, hostname.c_str());
}
// timeout
void setConnectTimeout(int timeout_ms) {
if (io_ == NULL) return;
hio_set_connect_timeout(io_, timeout_ms);
}
void setCloseTimeout(int timeout_ms) {
if (io_ == NULL) return;
hio_set_close_timeout(io_, timeout_ms);
}
void setReadTimeout(int timeout_ms) {
if (io_ == NULL) return;
hio_set_read_timeout(io_, timeout_ms);
}
void setWriteTimeout(int timeout_ms) {
if (io_ == NULL) return;
hio_set_write_timeout(io_, timeout_ms);
}
void setKeepaliveTimeout(int timeout_ms) {
if (io_ == NULL) return;
hio_set_keepalive_timeout(io_, timeout_ms);
}
// heartbeat
// NOTE: Beware of circular reference problems caused by passing SocketChannelPtr by value.
void setHeartbeat(int interval_ms, std::function<void()> fn) {
if (io_ == NULL) return;
heartbeat = std::move(fn);
hio_set_heartbeat(io_, interval_ms, send_heartbeat);
}
/*
* unpack
*
* NOTE: unpack_setting_t of multiple IOs of the same function also are same,
* so only the pointer of unpack_setting_t is stored in hio_t,
* the life time of unpack_setting_t shoud be guaranteed by caller.
*/
void setUnpack(unpack_setting_t* setting) {
if (io_ == NULL) return;
hio_set_unpack(io_, setting);
}
int startConnect(int port, const char* host = "127.0.0.1") {
sockaddr_u peeraddr;
memset(&peeraddr, 0, sizeof(peeraddr));
int ret = sockaddr_set_ipport(&peeraddr, host, port);
if (ret != 0) {
// hloge("unknown host %s", host);
return ret;
}
return startConnect(&peeraddr.sa);
}
int startConnect(struct sockaddr* peeraddr) {
if (io_ == NULL) return -1;
hio_set_peeraddr(io_, peeraddr, SOCKADDR_LEN(peeraddr));
return startConnect();
}
int startConnect() {
if (io_ == NULL) return -1;
status = CONNECTING;
hio_setcb_connect(io_, on_connect);
return hio_connect(io_);
}
bool isConnected() {
return status == CONNECTED && isOpened();
}
std::string localaddr() {
if (io_ == NULL) return "";
struct sockaddr* addr = hio_localaddr(io_);
char buf[SOCKADDR_STRLEN] = {0};
return SOCKADDR_STR(addr, buf);
}
std::string peeraddr() {
if (io_ == NULL) return "";
struct sockaddr* addr = hio_peeraddr(io_);
char buf[SOCKADDR_STRLEN] = {0};
return SOCKADDR_STR(addr, buf);
}
private:
static void on_connect(hio_t* io) {
SocketChannel* channel = (SocketChannel*)hio_context(io);
if (channel) {
channel->status = CONNECTED;
if (channel->onconnect) {
channel->onconnect();
}
}
}
static void send_heartbeat(hio_t* io) {
SocketChannel* channel = (SocketChannel*)hio_context(io);
if (channel && channel->heartbeat) {
channel->heartbeat();
}
}
};
typedef std::shared_ptr<Channel> ChannelPtr;
typedef std::shared_ptr<SocketChannel> SocketChannelPtr;
}
#endif // HV_CHANNEL_HPP_

47
third_party/libhv/evpp/Event.h vendored Executable file
View File

@@ -0,0 +1,47 @@
#ifndef HV_EVENT_HPP_
#define HV_EVENT_HPP_
#include <functional>
#include <memory>
#include "hloop.h"
namespace hv {
struct Event;
struct Timer;
typedef uint64_t TimerID;
#define INVALID_TIMER_ID ((hv::TimerID)-1)
typedef std::function<void(Event*)> EventCallback;
typedef std::function<void(TimerID)> TimerCallback;
struct Event {
hevent_t event;
EventCallback cb;
Event(EventCallback cb = NULL) {
memset(&event, 0, sizeof(hevent_t));
this->cb = std::move(cb);
}
};
struct Timer {
htimer_t* timer;
TimerCallback cb;
uint32_t repeat;
Timer(htimer_t* timer = NULL, TimerCallback cb = NULL, uint32_t repeat = INFINITE) {
this->timer = timer;
this->cb = std::move(cb);
this->repeat = repeat;
}
};
typedef std::shared_ptr<Event> EventPtr;
typedef std::shared_ptr<Timer> TimerPtr;
}
#endif // HV_EVENT_HPP_

277
third_party/libhv/evpp/EventLoop.h vendored Executable file
View File

@@ -0,0 +1,277 @@
#ifndef HV_EVENT_LOOP_HPP_
#define HV_EVENT_LOOP_HPP_
#include <functional>
#include <queue>
#include <map>
#include <mutex>
#include "hloop.h"
#include "hthread.h"
#include "Status.h"
#include "Event.h"
#include "ThreadLocalStorage.h"
namespace hv {
class EventLoop : public Status {
public:
typedef std::function<void()> Functor;
// New an EventLoop using an existing hloop_t object,
// so we can embed an EventLoop object into the old application based on hloop.
// NOTE: Be careful to deal with destroy of hloop_t.
EventLoop(hloop_t* loop = NULL) {
setStatus(kInitializing);
if (loop) {
loop_ = loop;
is_loop_owner = false;
} else {
loop_ = hloop_new(HLOOP_FLAG_AUTO_FREE);
is_loop_owner = true;
}
connectionNum = 0;
nextTimerID = 0;
setStatus(kInitialized);
}
~EventLoop() {
stop();
}
hloop_t* loop() {
return loop_;
}
// @brief Run loop forever
void run() {
if (loop_ == NULL) return;
if (status() == kRunning) return;
ThreadLocalStorage::set(ThreadLocalStorage::EVENT_LOOP, this);
setStatus(kRunning);
hloop_run(loop_);
setStatus(kStopped);
}
// stop thread-safe
void stop() {
if (loop_ == NULL) return;
if (status() < kRunning) {
if (is_loop_owner) {
hloop_free(&loop_);
}
loop_ = NULL;
return;
}
setStatus(kStopping);
hloop_stop(loop_);
loop_ = NULL;
}
void pause() {
if (loop_ == NULL) return;
if (isRunning()) {
hloop_pause(loop_);
setStatus(kPause);
}
}
void resume() {
if (loop_ == NULL) return;
if (isPause()) {
hloop_resume(loop_);
setStatus(kRunning);
}
}
// Timer interfaces: setTimer, killTimer, resetTimer
TimerID setTimer(int timeout_ms, TimerCallback cb, uint32_t repeat = INFINITE, TimerID timerID = INVALID_TIMER_ID) {
if (loop_ == NULL) return INVALID_TIMER_ID;
assertInLoopThread();
htimer_t* htimer = htimer_add(loop_, onTimer, timeout_ms, repeat);
assert(htimer != NULL);
if (timerID == INVALID_TIMER_ID) {
timerID = generateTimerID();
}
hevent_set_id(htimer, timerID);
hevent_set_userdata(htimer, this);
timers[timerID] = std::make_shared<Timer>(htimer, cb, repeat);
return timerID;
}
// setTimerInLoop thread-safe
TimerID setTimerInLoop(int timeout_ms, TimerCallback cb, uint32_t repeat = INFINITE, TimerID timerID = INVALID_TIMER_ID) {
if (timerID == INVALID_TIMER_ID) {
timerID = generateTimerID();
}
runInLoop(std::bind(&EventLoop::setTimer, this, timeout_ms, cb, repeat, timerID));
return timerID;
}
// alias javascript setTimeout, setInterval
// setTimeout thread-safe
TimerID setTimeout(int timeout_ms, TimerCallback cb) {
return setTimerInLoop(timeout_ms, cb, 1);
}
// setInterval thread-safe
TimerID setInterval(int interval_ms, TimerCallback cb) {
return setTimerInLoop(interval_ms, cb, INFINITE);
}
// killTimer thread-safe
void killTimer(TimerID timerID) {
runInLoop([timerID, this](){
auto iter = timers.find(timerID);
if (iter != timers.end()) {
htimer_del(iter->second->timer);
timers.erase(iter);
}
});
}
// resetTimer thread-safe
void resetTimer(TimerID timerID, int timeout_ms = 0) {
runInLoop([timerID, timeout_ms, this](){
auto iter = timers.find(timerID);
if (iter != timers.end()) {
htimer_reset(iter->second->timer, timeout_ms);
if (iter->second->repeat == 0) {
iter->second->repeat = 1;
}
}
});
}
long tid() {
if (loop_ == NULL) return hv_gettid();
return hloop_tid(loop_);
}
bool isInLoopThread() {
if (loop_ == NULL) return false;
return hv_gettid() == hloop_tid(loop_);
}
void assertInLoopThread() {
assert(isInLoopThread());
}
void runInLoop(Functor fn) {
if (isRunning() && isInLoopThread()) {
if (fn) fn();
} else {
queueInLoop(std::move(fn));
}
}
void queueInLoop(Functor fn) {
postEvent([fn](Event* ev) {
if (fn) fn();
});
}
void postEvent(EventCallback cb) {
if (loop_ == NULL) return;
EventPtr ev = std::make_shared<Event>(cb);
hevent_set_userdata(&ev->event, this);
ev->event.cb = onCustomEvent;
mutex_.lock();
customEvents.push(ev);
mutex_.unlock();
hloop_post_event(loop_, &ev->event);
}
private:
TimerID generateTimerID() {
return (((TimerID)tid() & 0xFFFFFFFF) << 32) | ++nextTimerID;
}
static void onTimer(htimer_t* htimer) {
EventLoop* loop = (EventLoop*)hevent_userdata(htimer);
TimerID timerID = hevent_id(htimer);
TimerPtr timer = NULL;
auto iter = loop->timers.find(timerID);
if (iter != loop->timers.end()) {
timer = iter->second;
if (timer->repeat != INFINITE) --timer->repeat;
}
if (timer) {
if (timer->cb) timer->cb(timerID);
if (timer->repeat == 0) {
// htimer_t alloc and free by hloop, but timers[timerID] managed by EventLoop.
loop->timers.erase(timerID);
}
}
}
static void onCustomEvent(hevent_t* hev) {
EventLoop* loop = (EventLoop*)hevent_userdata(hev);
loop->mutex_.lock();
EventPtr ev = loop->customEvents.front();
loop->customEvents.pop();
loop->mutex_.unlock();
if (ev && ev->cb) ev->cb(ev.get());
}
public:
std::atomic<uint32_t> connectionNum; // for LB_LeastConnections
private:
hloop_t* loop_;
bool is_loop_owner;
std::mutex mutex_;
std::queue<EventPtr> customEvents; // GUAREDE_BY(mutex_)
std::map<TimerID, TimerPtr> timers;
std::atomic<TimerID> nextTimerID;
};
typedef std::shared_ptr<EventLoop> EventLoopPtr;
// ThreadLocalStorage
static inline EventLoop* tlsEventLoop() {
return (EventLoop*)ThreadLocalStorage::get(ThreadLocalStorage::EVENT_LOOP);
}
#define currentThreadEventLoop ::hv::tlsEventLoop()
static inline TimerID setTimer(int timeout_ms, TimerCallback cb, uint32_t repeat = INFINITE) {
EventLoop* loop = tlsEventLoop();
assert(loop != NULL);
if (loop == NULL) return INVALID_TIMER_ID;
return loop->setTimer(timeout_ms, cb, repeat);
}
static inline void killTimer(TimerID timerID) {
EventLoop* loop = tlsEventLoop();
assert(loop != NULL);
if (loop == NULL) return;
loop->killTimer(timerID);
}
static inline void resetTimer(TimerID timerID, int timeout_ms) {
EventLoop* loop = tlsEventLoop();
assert(loop != NULL);
if (loop == NULL) return;
loop->resetTimer(timerID, timeout_ms);
}
static inline TimerID setTimeout(int timeout_ms, TimerCallback cb) {
return setTimer(timeout_ms, cb, 1);
}
static inline TimerID setInterval(int interval_ms, TimerCallback cb) {
return setTimer(interval_ms, cb, INFINITE);
}
}
#endif // HV_EVENT_LOOP_HPP_

116
third_party/libhv/evpp/EventLoopThread.h vendored Executable file
View File

@@ -0,0 +1,116 @@
#ifndef HV_EVENT_LOOP_THREAD_HPP_
#define HV_EVENT_LOOP_THREAD_HPP_
#include <thread>
#include "hlog.h"
#include "EventLoop.h"
namespace hv {
class EventLoopThread : public Status {
public:
// Return 0 means OK, other failed.
typedef std::function<int()> Functor;
EventLoopThread(EventLoopPtr loop = NULL) {
setStatus(kInitializing);
loop_ = loop ? loop : std::make_shared<EventLoop>();
setStatus(kInitialized);
}
~EventLoopThread() {
stop();
join();
}
const EventLoopPtr& loop() {
return loop_;
}
hloop_t* hloop() {
return loop_->loop();
}
bool isRunning() {
return loop_->isRunning();
}
// @param wait_thread_started: if ture this method will block until loop_thread started.
// @param pre: This functor will be executed when loop_thread started.
// @param post:This Functor will be executed when loop_thread stopped.
void start(bool wait_thread_started = true,
Functor pre = Functor(),
Functor post = Functor()) {
if (status() >= kStarting && status() < kStopped) return;
if (isRunning()) return;
setStatus(kStarting);
thread_ = std::make_shared<std::thread>(&EventLoopThread::loop_thread, this, pre, post);
if (wait_thread_started) {
while (loop_->status() < kRunning) {
hv_delay(1);
}
}
}
// @param wait_thread_started: if ture this method will block until loop_thread stopped.
// stop thread-safe
void stop(bool wait_thread_stopped = false) {
if (status() < kStarting || status() >= kStopping) return;
setStatus(kStopping);
long loop_tid = loop_->tid();
loop_->stop();
if (wait_thread_stopped) {
if (hv_gettid() == loop_tid) return;
join();
}
}
// @brief join loop_thread
// @note destructor will join loop_thread if you forget to call this method.
void join() {
if (thread_ && thread_->joinable()) {
thread_->join();
thread_ = NULL;
}
}
private:
void loop_thread(const Functor& pre, const Functor& post) {
hlogi("EventLoopThread started, tid=%ld", hv_gettid());
setStatus(kStarted);
if (pre) {
loop_->queueInLoop([this, pre]{
if (pre() != 0) {
loop_->stop();
}
});
}
loop_->run();
assert(loop_->isStopped());
if (post) {
post();
}
setStatus(kStopped);
hlogi("EventLoopThread stopped, tid=%ld", hv_gettid());
}
private:
EventLoopPtr loop_;
std::shared_ptr<std::thread> thread_;
};
typedef std::shared_ptr<EventLoopThread> EventLoopThreadPtr;
}
#endif // HV_EVENT_LOOP_THREAD_HPP_

138
third_party/libhv/evpp/EventLoopThreadPool.h vendored Executable file
View File

@@ -0,0 +1,138 @@
#ifndef HV_EVENT_LOOP_THREAD_POOL_HPP_
#define HV_EVENT_LOOP_THREAD_POOL_HPP_
#include "EventLoopThread.h"
#include "hbase.h"
namespace hv {
class EventLoopThreadPool : public Status {
public:
EventLoopThreadPool(int thread_num = std::thread::hardware_concurrency()) {
setStatus(kInitializing);
thread_num_ = thread_num;
next_loop_idx_ = 0;
setStatus(kInitialized);
}
~EventLoopThreadPool() {
stop();
join();
}
int threadNum() {
return thread_num_;
}
void setThreadNum(int num) {
thread_num_ = num;
}
EventLoopPtr nextLoop(load_balance_e lb = LB_RoundRobin) {
size_t numLoops = loop_threads_.size();
if (numLoops == 0) return NULL;
size_t idx = 0;
if (lb == LB_RoundRobin) {
if (++next_loop_idx_ >= numLoops) next_loop_idx_ = 0;
idx = next_loop_idx_ % numLoops;
} else if (lb == LB_Random) {
idx = hv_rand(0, numLoops - 1);
} else if (lb == LB_LeastConnections) {
for (size_t i = 1; i < numLoops; ++i) {
if (loop_threads_[i]->loop()->connectionNum < loop_threads_[idx]->loop()->connectionNum) {
idx = i;
}
}
} else {
// Not Implemented
}
return loop_threads_[idx]->loop();
}
EventLoopPtr loop(int idx = -1) {
if (idx >= 0 && idx < (int)loop_threads_.size()) {
return loop_threads_[idx]->loop();
}
return nextLoop();
}
hloop_t* hloop(int idx = -1) {
EventLoopPtr ptr = loop(idx);
return ptr ? ptr->loop() : NULL;
}
// @param wait_threads_started: if ture this method will block until all loop_threads started.
// @param pre: This functor will be executed when loop_thread started.
// @param post:This Functor will be executed when loop_thread stopped.
void start(bool wait_threads_started = false,
std::function<void(const EventLoopPtr&)> pre = NULL,
std::function<void(const EventLoopPtr&)> post = NULL) {
if (thread_num_ == 0) return;
if (status() >= kStarting && status() < kStopped) return;
setStatus(kStarting);
auto started_cnt = std::make_shared<std::atomic<int>>(0);
auto exited_cnt = std::make_shared<std::atomic<int>>(0);
loop_threads_.clear();
for (int i = 0; i < thread_num_; ++i) {
auto loop_thread = std::make_shared<EventLoopThread>();
const EventLoopPtr& loop = loop_thread->loop();
loop_thread->start(false,
[this, started_cnt, pre, &loop]() {
if (++(*started_cnt) == thread_num_) {
setStatus(kRunning);
}
if (pre) pre(loop);
return 0;
},
[this, exited_cnt, post, &loop]() {
if (post) post(loop);
if (++(*exited_cnt) == thread_num_) {
setStatus(kStopped);
}
return 0;
}
);
loop_threads_.push_back(loop_thread);
}
if (wait_threads_started) {
while (status() < kRunning) {
hv_delay(1);
}
}
}
// @param wait_threads_started: if ture this method will block until all loop_threads stopped.
// stop thread-safe
void stop(bool wait_threads_stopped = false) {
if (status() < kStarting || status() >= kStopping) return;
setStatus(kStopping);
for (auto& loop_thread : loop_threads_) {
loop_thread->stop(false);
}
if (wait_threads_stopped) {
join();
}
}
// @brief join all loop_threads
// @note destructor will join loop_threads if you forget to call this method.
void join() {
for (auto& loop_thread : loop_threads_) {
loop_thread->join();
}
}
private:
int thread_num_;
std::vector<EventLoopThreadPtr> loop_threads_;
std::atomic<unsigned int> next_loop_idx_;
};
}
#endif // HV_EVENT_LOOP_THREAD_POOL_HPP_

View File

@@ -0,0 +1,55 @@
/*
* EventLoopThreadPool_test.cpp
*
* @build: make evpp
*
*/
#include "hv.h"
#include "EventLoopThreadPool.h"
using namespace hv;
static void onTimer(TimerID timerID, int n) {
printf("tid=%ld timerID=%lu time=%lus n=%d\n", hv_gettid(), (unsigned long)timerID, (unsigned long)time(NULL), n);
}
int main(int argc, char* argv[]) {
HV_MEMCHECK;
hlog_set_level(LOG_LEVEL_DEBUG);
printf("main tid=%ld\n", hv_gettid());
EventLoopThreadPool loop_threads(4);
loop_threads.start(true);
int thread_num = loop_threads.threadNum();
for (int i = 0; i < thread_num; ++i) {
EventLoopPtr loop = loop_threads.nextLoop();
printf("worker[%d] tid=%ld\n", i, loop->tid());
loop->runInLoop([loop](){
// runEvery 1s
loop->setInterval(1000, std::bind(onTimer, std::placeholders::_1, 100));
});
loop->queueInLoop([](){
printf("queueInLoop tid=%ld\n", hv_gettid());
});
loop->runInLoop([](){
printf("runInLoop tid=%ld\n", hv_gettid());
});
}
// runAfter 10s
loop_threads.loop()->setTimeout(10000, [&loop_threads](TimerID timerID){
loop_threads.stop(false);
});
// wait loop_threads exit
loop_threads.join();
return 0;
}

View File

@@ -0,0 +1,48 @@
/*
* EventLoopThread_test.cpp
*
* @build: make evpp
*
*/
#include "hv.h"
#include "EventLoopThread.h"
using namespace hv;
static void onTimer(TimerID timerID, int n) {
printf("tid=%ld timerID=%lu time=%lus n=%d\n", hv_gettid(), (unsigned long)timerID, (unsigned long)time(NULL), n);
}
int main(int argc, char* argv[]) {
HV_MEMCHECK;
printf("main tid=%ld\n", hv_gettid());
EventLoopThread loop_thread;
const EventLoopPtr& loop = loop_thread.loop();
// runEvery 1s
loop->setInterval(1000, std::bind(onTimer, std::placeholders::_1, 100));
// runAfter 10s
loop->setTimeout(10000, [&loop](TimerID timerID){
loop->stop();
});
loop_thread.start();
loop->queueInLoop([](){
printf("queueInLoop tid=%ld\n", hv_gettid());
});
loop->runInLoop([](){
printf("runInLoop tid=%ld\n", hv_gettid());
});
// wait loop_thread exit
loop_thread.join();
return 0;
}

45
third_party/libhv/evpp/EventLoop_test.cpp vendored Executable file
View File

@@ -0,0 +1,45 @@
/*
* EventLoop_test.cpp
*
* @build: make evpp
*
*/
#include "hv.h"
#include "EventLoop.h"
using namespace hv;
static void onTimer(TimerID timerID, int n) {
printf("tid=%ld timerID=%lu time=%lus n=%d\n", hv_gettid(), (unsigned long)timerID, (unsigned long)time(NULL), n);
}
int main(int argc, char* argv[]) {
HV_MEMCHECK;
printf("main tid=%ld\n", hv_gettid());
auto loop = std::make_shared<EventLoop>();
// runEvery 1s
loop->setInterval(1000, std::bind(onTimer, std::placeholders::_1, 100));
// runAfter 10s
loop->setTimeout(10000, [&loop](TimerID timerID){
loop->stop();
});
loop->queueInLoop([](){
printf("queueInLoop tid=%ld\n", hv_gettid());
});
loop->runInLoop([](){
printf("runInLoop tid=%ld\n", hv_gettid());
});
// run until loop stopped
loop->run();
return 0;
}

24
third_party/libhv/evpp/README.md vendored Executable file
View File

@@ -0,0 +1,24 @@
The evpp module is designed to be header-only and does not participate in compilation.
hloop.h is encapsulated into c++ classes, referring to muduo and evpp.
You can modify and use evpp classes according to your own business.
evpp模块被设计成只包含头文件不参与编译。
hloop.h中的c接口被封装成了c++的类参考了muduo和evpp。
你能修改和使用这些类根据你自己的业务。
## 目录结构
```
.
├── Buffer.h 缓存类
├── Channel.h 通道类封装了hio_t
├── Event.h 事件类封装了hevent_t、htimer_t
├── EventLoop.h 事件循环类封装了hloop_t
├── EventLoopThread.h 事件循环线程类组合了EventLoop和thread
├── EventLoopThreadPool.h 事件循环线程池类组合了EventLoop和ThreadPool
├── TcpClient.h TCP客户端类
├── TcpServer.h TCP服务端类
├── UdpClient.h UDP客户端类
└── UdpServer.h UDP服务端类
```

56
third_party/libhv/evpp/Status.h vendored Executable file
View File

@@ -0,0 +1,56 @@
#ifndef HV_STATUS_HPP_
#define HV_STATUS_HPP_
#include <atomic>
namespace hv {
class Status {
public:
enum KStatus {
kNull = 0,
kInitializing = 1,
kInitialized = 2,
kStarting = 3,
kStarted = 4,
kRunning = 5,
kPause = 6,
kStopping = 7,
kStopped = 8,
kDestroyed = 9,
};
Status() {
status_ = kNull;
}
~Status() {
status_ = kDestroyed;
}
KStatus status() {
return status_;
}
void setStatus(KStatus status) {
status_ = status;
}
bool isRunning() {
return status_ == kRunning;
}
bool isPause() {
return status_ == kPause;
}
bool isStopped() {
return status_ == kStopped;
}
private:
std::atomic<KStatus> status_;
};
}
#endif // HV_STATUS_HPP_

308
third_party/libhv/evpp/TcpClient.h vendored Executable file
View File

@@ -0,0 +1,308 @@
#ifndef HV_TCP_CLIENT_HPP_
#define HV_TCP_CLIENT_HPP_
#include "hsocket.h"
#include "hssl.h"
#include "hlog.h"
#include "EventLoopThread.h"
#include "Channel.h"
namespace hv {
template<class TSocketChannel = SocketChannel>
class TcpClientEventLoopTmpl {
public:
typedef std::shared_ptr<TSocketChannel> TSocketChannelPtr;
TcpClientEventLoopTmpl(EventLoopPtr loop = NULL) {
loop_ = loop ? loop : std::make_shared<EventLoop>();
remote_port = 0;
connect_timeout = HIO_DEFAULT_CONNECT_TIMEOUT;
tls = false;
tls_setting = NULL;
reconn_setting = NULL;
unpack_setting = NULL;
}
virtual ~TcpClientEventLoopTmpl() {
HV_FREE(tls_setting);
HV_FREE(reconn_setting);
HV_FREE(unpack_setting);
}
const EventLoopPtr& loop() {
return loop_;
}
// delete thread-safe
void deleteInLoop() {
loop_->runInLoop([this](){
delete this;
});
}
// NOTE: By default, not bind local port. If necessary, you can call bind() after createsocket().
// @retval >=0 connfd, <0 error
int createsocket(int remote_port, const char* remote_host = "127.0.0.1") {
memset(&remote_addr, 0, sizeof(remote_addr));
int ret = sockaddr_set_ipport(&remote_addr, remote_host, remote_port);
if (ret != 0) {
return NABS(ret);
}
this->remote_host = remote_host;
this->remote_port = remote_port;
return createsocket(&remote_addr.sa);
}
int createsocket(struct sockaddr* remote_addr) {
int connfd = ::socket(remote_addr->sa_family, SOCK_STREAM, 0);
// SOCKADDR_PRINT(remote_addr);
if (connfd < 0) {
perror("socket");
return -2;
}
hio_t* io = hio_get(loop_->loop(), connfd);
assert(io != NULL);
hio_set_peeraddr(io, remote_addr, SOCKADDR_LEN(remote_addr));
channel = std::make_shared<TSocketChannel>(io);
return connfd;
}
int bind(int local_port, const char* local_host = "0.0.0.0") {
sockaddr_u local_addr;
memset(&local_addr, 0, sizeof(local_addr));
int ret = sockaddr_set_ipport(&local_addr, local_host, local_port);
if (ret != 0) {
return NABS(ret);
}
return bind(&local_addr.sa);
}
int bind(struct sockaddr* local_addr) {
if (channel == NULL || channel->isClosed()) {
return -1;
}
int ret = ::bind(channel->fd(), local_addr, SOCKADDR_LEN(local_addr));
if (ret != 0) {
perror("bind");
}
return ret;
}
// closesocket thread-safe
void closesocket() {
if (channel && channel->status != SocketChannel::CLOSED) {
loop_->runInLoop([this](){
if (channel) {
setReconnect(NULL);
channel->close();
}
});
}
}
int startConnect() {
if (channel == NULL || channel->isClosed()) {
int connfd = createsocket(&remote_addr.sa);
if (connfd < 0) {
hloge("createsocket %s:%d return %d!\n", remote_host.c_str(), remote_port, connfd);
return connfd;
}
}
if (channel == NULL || channel->status >= SocketChannel::CONNECTING) {
return -1;
}
if (connect_timeout) {
channel->setConnectTimeout(connect_timeout);
}
if (tls) {
channel->enableSSL();
if (tls_setting) {
int ret = channel->newSslCtx(tls_setting);
if (ret != 0) {
hloge("new SSL_CTX failed: %d", ret);
closesocket();
return ret;
}
}
if (!is_ipaddr(remote_host.c_str())) {
channel->setHostname(remote_host);
}
}
channel->onconnect = [this]() {
if (unpack_setting) {
channel->setUnpack(unpack_setting);
}
channel->startRead();
if (onConnection) {
onConnection(channel);
}
if (reconn_setting) {
reconn_setting_reset(reconn_setting);
}
};
channel->onread = [this](Buffer* buf) {
if (onMessage) {
onMessage(channel, buf);
}
};
channel->onwrite = [this](Buffer* buf) {
if (onWriteComplete) {
onWriteComplete(channel, buf);
}
};
channel->onclose = [this]() {
bool reconnect = reconn_setting != NULL;
if (onConnection) {
onConnection(channel);
}
if (reconnect) {
startReconnect();
}
};
return channel->startConnect();
}
int startReconnect() {
if (!reconn_setting) return -1;
if (!reconn_setting_can_retry(reconn_setting)) return -2;
uint32_t delay = reconn_setting_calc_delay(reconn_setting);
hlogi("reconnect... cnt=%d, delay=%d", reconn_setting->cur_retry_cnt, reconn_setting->cur_delay);
loop_->setTimeout(delay, [this](TimerID timerID){
startConnect();
});
return 0;
}
// start thread-safe
void start() {
loop_->runInLoop(std::bind(&TcpClientEventLoopTmpl::startConnect, this));
}
bool isConnected() {
if (channel == NULL) return false;
return channel->isConnected();
}
// send thread-safe
int send(const void* data, int size) {
if (!isConnected()) return -1;
return channel->write(data, size);
}
int send(Buffer* buf) {
return send(buf->data(), buf->size());
}
int send(const std::string& str) {
return send(str.data(), str.size());
}
int withTLS(hssl_ctx_opt_t* opt = NULL) {
tls = true;
if (opt) {
if (tls_setting == NULL) {
HV_ALLOC_SIZEOF(tls_setting);
}
opt->endpoint = HSSL_CLIENT;
*tls_setting = *opt;
}
return 0;
}
void setConnectTimeout(int ms) {
connect_timeout = ms;
}
void setReconnect(reconn_setting_t* setting) {
if (setting == NULL) {
HV_FREE(reconn_setting);
return;
}
if (reconn_setting == NULL) {
HV_ALLOC_SIZEOF(reconn_setting);
}
*reconn_setting = *setting;
}
bool isReconnect() {
return reconn_setting && reconn_setting->cur_retry_cnt > 0;
}
void setUnpack(unpack_setting_t* setting) {
if (setting == NULL) {
HV_FREE(unpack_setting);
return;
}
if (unpack_setting == NULL) {
HV_ALLOC_SIZEOF(unpack_setting);
}
*unpack_setting = *setting;
}
public:
TSocketChannelPtr channel;
std::string remote_host;
int remote_port;
sockaddr_u remote_addr;
int connect_timeout;
bool tls;
hssl_ctx_opt_t* tls_setting;
reconn_setting_t* reconn_setting;
unpack_setting_t* unpack_setting;
// Callback
std::function<void(const TSocketChannelPtr&)> onConnection;
std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage;
// NOTE: Use Channel::isWriteComplete in onWriteComplete callback to determine whether all data has been written.
std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete;
private:
EventLoopPtr loop_;
};
template<class TSocketChannel = SocketChannel>
class TcpClientTmpl : private EventLoopThread, public TcpClientEventLoopTmpl<TSocketChannel> {
public:
TcpClientTmpl(EventLoopPtr loop = NULL)
: EventLoopThread(loop)
, TcpClientEventLoopTmpl<TSocketChannel>(EventLoopThread::loop())
, is_loop_owner(loop == NULL)
{}
virtual ~TcpClientTmpl() {
stop(true);
}
const EventLoopPtr& loop() {
return EventLoopThread::loop();
}
// start thread-safe
void start(bool wait_threads_started = true) {
if (isRunning()) {
TcpClientEventLoopTmpl<TSocketChannel>::start();
} else {
EventLoopThread::start(wait_threads_started, [this]() {
TcpClientTmpl::startConnect();
return 0;
});
}
}
// stop thread-safe
void stop(bool wait_threads_stopped = true) {
TcpClientEventLoopTmpl<TSocketChannel>::closesocket();
if (is_loop_owner) {
EventLoopThread::stop(wait_threads_stopped);
}
}
private:
bool is_loop_owner;
};
typedef TcpClientTmpl<SocketChannel> TcpClient;
}
#endif // HV_TCP_CLIENT_HPP_

View File

@@ -0,0 +1,131 @@
/*
* TcpClientEventLoop_test.cpp
*
* @build make evpp
* @server bin/TcpServer_test 1234
* @client bin/TcpClientEventLoop_test 1234
*
*/
#include <iostream>
#include "TcpClient.h"
#include "htime.h"
#define TEST_RECONNECT 1
#define TEST_TLS 0
using namespace hv;
class MyTcpClient : public TcpClient {
public:
MyTcpClient(EventLoopPtr loop = NULL) : TcpClient(loop) {
onConnection = [this](const SocketChannelPtr& channel) {
std::string peeraddr = channel->peeraddr();
if (channel->isConnected()) {
printf("connected to %s! connfd=%d\n", peeraddr.c_str(), channel->fd());
// send(time) every 3s
setInterval(3000, [channel](TimerID timerID){
if (channel->isConnected()) {
if (channel->isWriteComplete()) {
char str[DATETIME_FMT_BUFLEN] = {0};
datetime_t dt = datetime_now();
datetime_fmt(&dt, str);
channel->write(str);
}
} else {
killTimer(timerID);
}
});
} else {
printf("disconnected to %s! connfd=%d\n", peeraddr.c_str(), channel->fd());
}
if (isReconnect()) {
printf("reconnect cnt=%d, delay=%d\n", reconn_setting->cur_retry_cnt, reconn_setting->cur_delay);
}
};
onMessage = [](const SocketChannelPtr& channel, Buffer* buf) {
printf("< %.*s\n", (int)buf->size(), (char*)buf->data());
};
}
int connect(int port) {
int connfd = createsocket(port);
if (connfd < 0) {
return connfd;
}
#if TEST_RECONNECT
// reconnect: 1,2,4,8,10,10,10...
reconn_setting_t reconn;
reconn_setting_init(&reconn);
reconn.min_delay = 1000;
reconn.max_delay = 10000;
reconn.delay_policy = 2;
setReconnect(&reconn);
#endif
#if TEST_TLS
withTLS();
#endif
printf("client connect to port %d, connfd=%d ...\n", port, connfd);
start();
return connfd;
}
};
typedef std::shared_ptr<MyTcpClient> MyTcpClientPtr;
int TestMultiClientsRunInOneEventLoop(int port, int nclients) {
auto loop_thread = std::make_shared<EventLoopThread>();
loop_thread->start();
std::map<int, MyTcpClient*> clients;
for (int i = 0; i < nclients; ++i) {
MyTcpClient* client = new MyTcpClient(loop_thread->loop());
client->connect(port);
clients[i] = client;
}
std::string str;
while (std::getline(std::cin, str)) {
if (str == "close") {
for (auto& pair : clients) {
MyTcpClient* client = pair.second;
client->closesocket();
}
} else if (str == "delete") {
for (auto& pair : clients) {
MyTcpClient* client = pair.second;
client->deleteInLoop();
}
break;
} else {
for (auto& pair : clients) {
MyTcpClient* client = pair.second;
client->send(str);
}
}
}
printf("Press Enter key to exit loop.\n");
while (getchar() != '\n');
loop_thread->stop();
loop_thread->join();
return 0;
}
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("Usage: %s port [nclients]\n", argv[0]);
return -10;
}
int port = atoi(argv[1]);
int nclients = 100;
if (argc > 2) {
nclients = atoi(argv[2]);
}
return TestMultiClientsRunInOneEventLoop(port, nclients);
}

97
third_party/libhv/evpp/TcpClient_test.cpp vendored Executable file
View File

@@ -0,0 +1,97 @@
/*
* TcpClient_test.cpp
*
* @build make evpp
* @server bin/TcpServer_test 1234
* @client bin/TcpClient_test 1234
*
*/
#include <iostream>
#include "TcpClient.h"
#include "htime.h"
#define TEST_RECONNECT 1
#define TEST_TLS 0
using namespace hv;
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("Usage: %s remote_port [remote_host]\n", argv[0]);
return -10;
}
int remote_port = atoi(argv[1]);
const char* remote_host = "127.0.0.1";
if (argc > 2) {
remote_host = argv[2];
}
TcpClient cli;
int connfd = cli.createsocket(remote_port, remote_host);
if (connfd < 0) {
return -20;
}
printf("client connect to port %d, connfd=%d ...\n", remote_port, connfd);
cli.onConnection = [&cli](const SocketChannelPtr& channel) {
std::string peeraddr = channel->peeraddr();
if (channel->isConnected()) {
printf("connected to %s! connfd=%d\n", peeraddr.c_str(), channel->fd());
// send(time) every 3s
setInterval(3000, [channel](TimerID timerID){
if (channel->isConnected()) {
if (channel->isWriteComplete()) {
char str[DATETIME_FMT_BUFLEN] = {0};
datetime_t dt = datetime_now();
datetime_fmt(&dt, str);
channel->write(str);
}
} else {
killTimer(timerID);
}
});
} else {
printf("disconnected to %s! connfd=%d\n", peeraddr.c_str(), channel->fd());
}
if (cli.isReconnect()) {
printf("reconnect cnt=%d, delay=%d\n", cli.reconn_setting->cur_retry_cnt, cli.reconn_setting->cur_delay);
}
};
cli.onMessage = [](const SocketChannelPtr& channel, Buffer* buf) {
printf("< %.*s\n", (int)buf->size(), (char*)buf->data());
};
#if TEST_RECONNECT
// reconnect: 1,2,4,8,10,10,10...
reconn_setting_t reconn;
reconn_setting_init(&reconn);
reconn.min_delay = 1000;
reconn.max_delay = 10000;
reconn.delay_policy = 2;
cli.setReconnect(&reconn);
#endif
#if TEST_TLS
cli.withTLS();
#endif
cli.start();
std::string str;
while (std::getline(std::cin, str)) {
if (str == "close") {
cli.closesocket();
} else if (str == "start") {
cli.start();
} else if (str == "stop") {
cli.stop();
break;
} else {
if (!cli.isConnected()) break;
cli.send(str);
}
}
return 0;
}

322
third_party/libhv/evpp/TcpServer.h vendored Executable file
View File

@@ -0,0 +1,322 @@
#ifndef HV_TCP_SERVER_HPP_
#define HV_TCP_SERVER_HPP_
#include "hsocket.h"
#include "hssl.h"
#include "hlog.h"
#include "EventLoopThreadPool.h"
#include "Channel.h"
namespace hv {
template<class TSocketChannel = SocketChannel>
class TcpServerEventLoopTmpl {
public:
typedef std::shared_ptr<TSocketChannel> TSocketChannelPtr;
TcpServerEventLoopTmpl(EventLoopPtr loop = NULL) {
acceptor_loop = loop ? loop : std::make_shared<EventLoop>();
port = 0;
listenfd = -1;
tls = false;
tls_setting = NULL;
unpack_setting = NULL;
max_connections = 0xFFFFFFFF;
load_balance = LB_RoundRobin;
}
virtual ~TcpServerEventLoopTmpl() {
HV_FREE(tls_setting);
HV_FREE(unpack_setting);
}
EventLoopPtr loop(int idx = -1) {
EventLoopPtr worker_loop = worker_threads.loop(idx);
if (worker_loop == NULL) {
worker_loop = acceptor_loop;
}
return worker_loop;
}
//@retval >=0 listenfd, <0 error
int createsocket(int port, const char* host = "0.0.0.0") {
listenfd = Listen(port, host);
if (listenfd < 0) return listenfd;
this->host = host;
this->port = port;
return listenfd;
}
// closesocket thread-safe
void closesocket() {
if (listenfd >= 0) {
hloop_t* loop = acceptor_loop->loop();
if (loop) {
hio_t* listenio = hio_get(loop, listenfd);
assert(listenio != NULL);
hio_close_async(listenio);
}
listenfd = -1;
}
}
void setMaxConnectionNum(uint32_t num) {
max_connections = num;
}
void setLoadBalance(load_balance_e lb) {
load_balance = lb;
}
// NOTE: totalThreadNum = 1 acceptor_thread + N worker_threads (N can be 0)
void setThreadNum(int num) {
worker_threads.setThreadNum(num);
}
int startAccept() {
if (listenfd < 0) {
listenfd = createsocket(port, host.c_str());
if (listenfd < 0) {
hloge("createsocket %s:%d return %d!\n", host.c_str(), port, listenfd);
return listenfd;
}
}
hloop_t* loop = acceptor_loop->loop();
if (loop == NULL) return -2;
hio_t* listenio = haccept(loop, listenfd, onAccept);
assert(listenio != NULL);
hevent_set_userdata(listenio, this);
if (tls) {
hio_enable_ssl(listenio);
if (tls_setting) {
int ret = hio_new_ssl_ctx(listenio, tls_setting);
if (ret != 0) {
hloge("new SSL_CTX failed: %d", ret);
closesocket();
return ret;
}
}
}
return 0;
}
int stopAccept() {
if (listenfd < 0) return -1;
hloop_t* loop = acceptor_loop->loop();
if (loop == NULL) return -2;
hio_t* listenio = hio_get(loop, listenfd);
assert(listenio != NULL);
return hio_del(listenio, HV_READ);
}
// start thread-safe
void start(bool wait_threads_started = true) {
if (worker_threads.threadNum() > 0) {
worker_threads.start(wait_threads_started);
}
acceptor_loop->runInLoop(std::bind(&TcpServerEventLoopTmpl::startAccept, this));
}
// stop thread-safe
void stop(bool wait_threads_stopped = true) {
closesocket();
if (worker_threads.threadNum() > 0) {
worker_threads.stop(wait_threads_stopped);
}
}
int withTLS(hssl_ctx_opt_t* opt = NULL) {
tls = true;
if (opt) {
if (tls_setting == NULL) {
HV_ALLOC_SIZEOF(tls_setting);
}
opt->endpoint = HSSL_SERVER;
*tls_setting = *opt;
}
return 0;
}
void setUnpack(unpack_setting_t* setting) {
if (setting == NULL) {
HV_FREE(unpack_setting);
return;
}
if (unpack_setting == NULL) {
HV_ALLOC_SIZEOF(unpack_setting);
}
*unpack_setting = *setting;
}
// channel
const TSocketChannelPtr& addChannel(hio_t* io) {
uint32_t id = hio_id(io);
auto channel = std::make_shared<TSocketChannel>(io);
std::lock_guard<std::mutex> locker(mutex_);
channels[id] = channel;
return channels[id];
}
TSocketChannelPtr getChannelById(uint32_t id) {
std::lock_guard<std::mutex> locker(mutex_);
auto iter = channels.find(id);
return iter != channels.end() ? iter->second : NULL;
}
void removeChannel(const TSocketChannelPtr& channel) {
uint32_t id = channel->id();
std::lock_guard<std::mutex> locker(mutex_);
channels.erase(id);
}
size_t connectionNum() {
std::lock_guard<std::mutex> locker(mutex_);
return channels.size();
}
int foreachChannel(std::function<void(const TSocketChannelPtr& channel)> fn) {
std::lock_guard<std::mutex> locker(mutex_);
for (auto& pair : channels) {
fn(pair.second);
}
return channels.size();
}
// broadcast thread-safe
int broadcast(const void* data, int size) {
return foreachChannel([data, size](const TSocketChannelPtr& channel) {
channel->write(data, size);
});
}
int broadcast(const std::string& str) {
return broadcast(str.data(), str.size());
}
private:
static void newConnEvent(hio_t* connio) {
TcpServerEventLoopTmpl* server = (TcpServerEventLoopTmpl*)hevent_userdata(connio);
if (server->connectionNum() >= server->max_connections) {
hlogw("over max_connections");
hio_close(connio);
return;
}
// NOTE: attach to worker loop
EventLoop* worker_loop = currentThreadEventLoop;
assert(worker_loop != NULL);
hio_attach(worker_loop->loop(), connio);
const TSocketChannelPtr& channel = server->addChannel(connio);
channel->status = SocketChannel::CONNECTED;
channel->onread = [server, &channel](Buffer* buf) {
if (server->onMessage) {
server->onMessage(channel, buf);
}
};
channel->onwrite = [server, &channel](Buffer* buf) {
if (server->onWriteComplete) {
server->onWriteComplete(channel, buf);
}
};
channel->onclose = [server, &channel]() {
EventLoop* worker_loop = currentThreadEventLoop;
assert(worker_loop != NULL);
--worker_loop->connectionNum;
channel->status = SocketChannel::CLOSED;
if (server->onConnection) {
server->onConnection(channel);
}
server->removeChannel(channel);
// NOTE: After removeChannel, channel may be destroyed,
// so in this lambda function, no code should be added below.
};
if (server->unpack_setting) {
channel->setUnpack(server->unpack_setting);
}
channel->startRead();
if (server->onConnection) {
server->onConnection(channel);
}
}
static void onAccept(hio_t* connio) {
TcpServerEventLoopTmpl* server = (TcpServerEventLoopTmpl*)hevent_userdata(connio);
// NOTE: detach from acceptor loop
hio_detach(connio);
EventLoopPtr worker_loop = server->worker_threads.nextLoop(server->load_balance);
if (worker_loop == NULL) {
worker_loop = server->acceptor_loop;
}
++worker_loop->connectionNum;
worker_loop->runInLoop(std::bind(&TcpServerEventLoopTmpl::newConnEvent, connio));
}
public:
std::string host;
int port;
int listenfd;
bool tls;
hssl_ctx_opt_t* tls_setting;
unpack_setting_t* unpack_setting;
// Callback
std::function<void(const TSocketChannelPtr&)> onConnection;
std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage;
// NOTE: Use Channel::isWriteComplete in onWriteComplete callback to determine whether all data has been written.
std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete;
uint32_t max_connections;
load_balance_e load_balance;
private:
// id => TSocketChannelPtr
std::map<uint32_t, TSocketChannelPtr> channels; // GUAREDE_BY(mutex_)
std::mutex mutex_;
EventLoopPtr acceptor_loop;
EventLoopThreadPool worker_threads;
};
template<class TSocketChannel = SocketChannel>
class TcpServerTmpl : private EventLoopThread, public TcpServerEventLoopTmpl<TSocketChannel> {
public:
TcpServerTmpl(EventLoopPtr loop = NULL)
: EventLoopThread(loop)
, TcpServerEventLoopTmpl<TSocketChannel>(EventLoopThread::loop())
, is_loop_owner(loop == NULL)
{}
virtual ~TcpServerTmpl() {
stop(true);
}
EventLoopPtr loop(int idx = -1) {
return TcpServerEventLoopTmpl<TSocketChannel>::loop(idx);
}
// start thread-safe
void start(bool wait_threads_started = true) {
TcpServerEventLoopTmpl<TSocketChannel>::start(wait_threads_started);
if (!isRunning()) {
EventLoopThread::start(wait_threads_started);
}
}
// stop thread-safe
void stop(bool wait_threads_stopped = true) {
if (is_loop_owner) {
EventLoopThread::stop(wait_threads_stopped);
}
TcpServerEventLoopTmpl<TSocketChannel>::stop(wait_threads_stopped);
}
private:
bool is_loop_owner;
};
typedef TcpServerTmpl<SocketChannel> TcpServer;
}
#endif // HV_TCP_SERVER_HPP_

75
third_party/libhv/evpp/TcpServer_test.cpp vendored Executable file
View File

@@ -0,0 +1,75 @@
/*
* TcpServer_test.cpp
*
* @build make evpp
* @server bin/TcpServer_test 1234
* @client bin/TcpClient_test 1234
*
*/
#include <iostream>
#include "TcpServer.h"
using namespace hv;
#define TEST_TLS 0
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("Usage: %s port\n", argv[0]);
return -10;
}
int port = atoi(argv[1]);
hlog_set_level(LOG_LEVEL_DEBUG);
TcpServer srv;
int listenfd = srv.createsocket(port);
if (listenfd < 0) {
return -20;
}
printf("server listen on port %d, listenfd=%d ...\n", port, listenfd);
srv.onConnection = [](const SocketChannelPtr& channel) {
std::string peeraddr = channel->peeraddr();
if (channel->isConnected()) {
printf("%s connected! connfd=%d id=%d tid=%ld\n", peeraddr.c_str(), channel->fd(), channel->id(), currentThreadEventLoop->tid());
} else {
printf("%s disconnected! connfd=%d id=%d tid=%ld\n", peeraddr.c_str(), channel->fd(), channel->id(), currentThreadEventLoop->tid());
}
};
srv.onMessage = [](const SocketChannelPtr& channel, Buffer* buf) {
// echo
printf("< %.*s\n", (int)buf->size(), (char*)buf->data());
channel->write(buf);
};
srv.setThreadNum(4);
srv.setLoadBalance(LB_LeastConnections);
#if TEST_TLS
hssl_ctx_opt_t ssl_opt;
memset(&ssl_opt, 0, sizeof(hssl_ctx_opt_t));
ssl_opt.crt_file = "cert/server.crt";
ssl_opt.key_file = "cert/server.key";
ssl_opt.verify_peer = 0;
srv.withTLS(&ssl_opt);
#endif
srv.start();
std::string str;
while (std::getline(std::cin, str)) {
if (str == "close") {
srv.closesocket();
} else if (str == "start") {
srv.start();
} else if (str == "stop") {
srv.stop();
break;
} else {
srv.broadcast(str.data(), str.size());
}
}
return 0;
}

47
third_party/libhv/evpp/TimerThread.h vendored Executable file
View File

@@ -0,0 +1,47 @@
#ifndef HV_TIMER_THREAD_HPP_
#define HV_TIMER_THREAD_HPP_
#include "EventLoopThread.h"
namespace hv {
class TimerThread : public EventLoopThread {
public:
std::atomic<TimerID> nextTimerID;
TimerThread() : EventLoopThread() {
nextTimerID = 0;
start();
}
virtual ~TimerThread() {
stop();
join();
}
public:
// setTimer, setTimeout, killTimer, resetTimer thread-safe
TimerID setTimer(int timeout_ms, TimerCallback cb, uint32_t repeat = INFINITE) {
TimerID timerID = ++nextTimerID;
loop()->setTimerInLoop(timeout_ms, cb, repeat, timerID);
return timerID;
}
// alias javascript setTimeout, setInterval
TimerID setTimeout(int timeout_ms, TimerCallback cb) {
return setTimer(timeout_ms, cb, 1);
}
TimerID setInterval(int interval_ms, TimerCallback cb) {
return setTimer(interval_ms, cb, INFINITE);
}
void killTimer(TimerID timerID) {
loop()->killTimer(timerID);
}
void resetTimer(TimerID timerID, int timeout_ms = 0) {
loop()->resetTimer(timerID, timeout_ms);
}
};
} // end namespace hv
#endif // HV_TIMER_THREAD_HPP_

55
third_party/libhv/evpp/TimerThread_test.cpp vendored Executable file
View File

@@ -0,0 +1,55 @@
/*
* TimerThread_test.cpp
*
* @build: make evpp
*
*/
#include "TimerThread.h"
#include "singleton.h"
namespace hv {
class GlobalTimerThread : public TimerThread {
SINGLETON_DECL(GlobalTimerThread)
protected:
GlobalTimerThread() : TimerThread() {}
~GlobalTimerThread() {}
public:
static TimerID setTimeout(int timeout_ms, TimerCallback cb) {
return GlobalTimerThread::instance()->setTimer(timeout_ms, cb, 1);
}
static void clearTimeout(TimerID timerID) {
GlobalTimerThread::instance()->killTimer(timerID);
}
static TimerID setInterval(int interval_ms, TimerCallback cb) {
return GlobalTimerThread::instance()->setTimer(interval_ms, cb, INFINITE);
}
static void clearInterval(TimerID timerID) {
GlobalTimerThread::instance()->killTimer(timerID);
}
};
SINGLETON_IMPL(GlobalTimerThread)
} // end namespace hv
int main(int argc, char* argv[]) {
hv::GlobalTimerThread::setTimeout(3000, [](hv::TimerID timerID) {
printf("setTimeout timerID=%lu time=%lus\n", (unsigned long)timerID, (unsigned long)time(NULL));
});
hv::GlobalTimerThread::setInterval(1000, [](hv::TimerID timerID) {
printf("setInterval timerID=%lu time=%lus\n", (unsigned long)timerID, (unsigned long)time(NULL));
});
// press Enter to stop
while (getchar() != '\n');
hv::GlobalTimerThread::exitInstance();
return 0;
}

200
third_party/libhv/evpp/UdpClient.h vendored Executable file
View File

@@ -0,0 +1,200 @@
#ifndef HV_UDP_CLIENT_HPP_
#define HV_UDP_CLIENT_HPP_
#include "hsocket.h"
#include "EventLoopThread.h"
#include "Channel.h"
namespace hv {
template<class TSocketChannel = SocketChannel>
class UdpClientEventLoopTmpl {
public:
typedef std::shared_ptr<TSocketChannel> TSocketChannelPtr;
UdpClientEventLoopTmpl(EventLoopPtr loop = NULL) {
loop_ = loop ? loop : std::make_shared<EventLoop>();
remote_port = 0;
#if WITH_KCP
kcp_setting = NULL;
#endif
}
virtual ~UdpClientEventLoopTmpl() {
#if WITH_KCP
HV_FREE(kcp_setting);
#endif
}
const EventLoopPtr& loop() {
return loop_;
}
// NOTE: By default, not bind local port. If necessary, you can call bind() after createsocket().
// @retval >=0 sockfd, <0 error
int createsocket(int remote_port, const char* remote_host = "127.0.0.1") {
hio_t* io = hloop_create_udp_client(loop_->loop(), remote_host, remote_port);
if (io == NULL) return -1;
this->remote_host = remote_host;
this->remote_port = remote_port;
channel = std::make_shared<TSocketChannel>(io);
int sockfd = channel->fd();
if (hv_strendswith(remote_host, ".255")) {
udp_broadcast(sockfd, 1);
}
return sockfd;
}
int bind(int local_port, const char* local_host = "0.0.0.0") {
if (channel == NULL || channel->isClosed()) {
return -1;
}
sockaddr_u local_addr;
memset(&local_addr, 0, sizeof(local_addr));
int ret = sockaddr_set_ipport(&local_addr, local_host, local_port);
if (ret != 0) {
return NABS(ret);
}
ret = ::bind(channel->fd(), &local_addr.sa, SOCKADDR_LEN(&local_addr));
if (ret != 0) {
perror("bind");
}
hio_set_localaddr(channel->io(), &local_addr.sa, SOCKADDR_LEN(&local_addr));
return ret;
}
// closesocket thread-safe
void closesocket() {
if (channel) {
channel->close(true);
}
}
int startRecv() {
if (channel == NULL || channel->isClosed()) {
int sockfd = createsocket(remote_port, remote_host.c_str());
if (sockfd < 0) {
hloge("createsocket %s:%d return %d!\n", remote_host.c_str(), remote_port, sockfd);
return sockfd;
}
}
if (channel == NULL || channel->isClosed()) {
return -1;
}
channel->onread = [this](Buffer* buf) {
if (onMessage) {
onMessage(channel, buf);
}
};
channel->onwrite = [this](Buffer* buf) {
if (onWriteComplete) {
onWriteComplete(channel, buf);
}
};
#if WITH_KCP
if (kcp_setting) {
hio_set_kcp(channel->io(), kcp_setting);
}
#endif
return channel->startRead();
}
int stopRecv() {
if (channel == NULL) return -1;
return channel->stopRead();
}
// start thread-safe
void start() {
loop_->runInLoop(std::bind(&UdpClientEventLoopTmpl::startRecv, this));
}
// sendto thread-safe
int sendto(const void* data, int size, struct sockaddr* peeraddr = NULL) {
if (channel == NULL) return -1;
std::lock_guard<std::mutex> locker(sendto_mutex);
if (peeraddr) hio_set_peeraddr(channel->io(), peeraddr, SOCKADDR_LEN(peeraddr));
return channel->write(data, size);
}
int sendto(Buffer* buf, struct sockaddr* peeraddr = NULL) {
return sendto(buf->data(), buf->size(), peeraddr);
}
int sendto(const std::string& str, struct sockaddr* peeraddr = NULL) {
return sendto(str.data(), str.size(), peeraddr);
}
#if WITH_KCP
void setKcp(kcp_setting_t* setting) {
if (setting == NULL) {
HV_FREE(kcp_setting);
return;
}
if (kcp_setting == NULL) {
HV_ALLOC_SIZEOF(kcp_setting);
}
*kcp_setting = *setting;
}
#endif
public:
TSocketChannelPtr channel;
std::string remote_host;
int remote_port;
#if WITH_KCP
kcp_setting_t* kcp_setting;
#endif
// Callback
std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage;
// NOTE: Use Channel::isWriteComplete in onWriteComplete callback to determine whether all data has been written.
std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete;
private:
std::mutex sendto_mutex;
EventLoopPtr loop_;
};
template<class TSocketChannel = SocketChannel>
class UdpClientTmpl : private EventLoopThread, public UdpClientEventLoopTmpl<TSocketChannel> {
public:
UdpClientTmpl(EventLoopPtr loop = NULL)
: EventLoopThread(loop)
, UdpClientEventLoopTmpl<TSocketChannel>(EventLoopThread::loop())
, is_loop_owner(loop == NULL)
{}
virtual ~UdpClientTmpl() {
stop(true);
}
const EventLoopPtr& loop() {
return EventLoopThread::loop();
}
// start thread-safe
void start(bool wait_threads_started = true) {
if (isRunning()) {
UdpClientEventLoopTmpl<TSocketChannel>::start();
} else {
EventLoopThread::start(wait_threads_started, std::bind(&UdpClientTmpl::startRecv, this));
}
}
// stop thread-safe
void stop(bool wait_threads_stopped = true) {
UdpClientEventLoopTmpl<TSocketChannel>::closesocket();
if (is_loop_owner) {
EventLoopThread::stop(wait_threads_stopped);
}
}
private:
bool is_loop_owner;
};
typedef UdpClientTmpl<SocketChannel> UdpClient;
}
#endif // HV_UDP_CLIENT_HPP_

62
third_party/libhv/evpp/UdpClient_test.cpp vendored Executable file
View File

@@ -0,0 +1,62 @@
/*
* UdpClient_test.cpp
*
* @build make evpp
* @server bin/UdpServer_test 1234
* @client bin/UdpClient_test 1234
*
*/
#include <iostream>
#include "UdpClient.h"
#include "htime.h"
using namespace hv;
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("Usage: %s remote_port [remote_host]\n", argv[0]);
return -10;
}
int remote_port = atoi(argv[1]);
const char* remote_host = "127.0.0.1";
if (argc > 2) {
remote_host = argv[2];
}
UdpClient cli;
int sockfd = cli.createsocket(remote_port, remote_host);
if (sockfd < 0) {
return -20;
}
printf("client sendto port %d, sockfd=%d ...\n", remote_port, sockfd);
cli.onMessage = [](const SocketChannelPtr& channel, Buffer* buf) {
printf("< %.*s\n", (int)buf->size(), (char*)buf->data());
};
cli.start();
// sendto(time) every 3s
cli.loop()->setInterval(3000, [&cli](TimerID timerID) {
char str[DATETIME_FMT_BUFLEN] = {0};
datetime_t dt = datetime_now();
datetime_fmt(&dt, str);
cli.sendto(str);
});
std::string str;
while (std::getline(std::cin, str)) {
if (str == "close") {
cli.closesocket();
} else if (str == "start") {
cli.start();
} else if (str == "stop") {
cli.stop();
break;
} else {
cli.sendto(str);
}
}
return 0;
}

174
third_party/libhv/evpp/UdpServer.h vendored Executable file
View File

@@ -0,0 +1,174 @@
#ifndef HV_UDP_SERVER_HPP_
#define HV_UDP_SERVER_HPP_
#include "hsocket.h"
#include "EventLoopThreadPool.h"
#include "Channel.h"
namespace hv {
template<class TSocketChannel = SocketChannel>
class UdpServerEventLoopTmpl {
public:
typedef std::shared_ptr<TSocketChannel> TSocketChannelPtr;
UdpServerEventLoopTmpl(EventLoopPtr loop = NULL) {
loop_ = loop ? loop : std::make_shared<EventLoop>();
port = 0;
#if WITH_KCP
kcp_setting = NULL;
#endif
}
virtual ~UdpServerEventLoopTmpl() {
#if WITH_KCP
HV_FREE(kcp_setting);
#endif
}
const EventLoopPtr& loop() {
return loop_;
}
//@retval >=0 bindfd, <0 error
int createsocket(int port, const char* host = "0.0.0.0") {
hio_t* io = hloop_create_udp_server(loop_->loop(), host, port);
if (io == NULL) return -1;
this->host = host;
this->port = port;
channel = std::make_shared<TSocketChannel>(io);
return channel->fd();
}
// closesocket thread-safe
void closesocket() {
if (channel) {
channel->close(true);
}
}
int startRecv() {
if (channel == NULL || channel->isClosed()) {
int bindfd = createsocket(port, host.c_str());
if (bindfd < 0) {
hloge("createsocket %s:%d return %d!\n", host.c_str(), port, bindfd);
return bindfd;
}
}
if (channel == NULL || channel->isClosed()) {
return -1;
}
channel->onread = [this](Buffer* buf) {
if (onMessage) {
onMessage(channel, buf);
}
};
channel->onwrite = [this](Buffer* buf) {
if (onWriteComplete) {
onWriteComplete(channel, buf);
}
};
#if WITH_KCP
if (kcp_setting) {
hio_set_kcp(channel->io(), kcp_setting);
}
#endif
return channel->startRead();
}
int stopRecv() {
if (channel == NULL) return -1;
return channel->stopRead();
}
// start thread-safe
void start() {
loop_->runInLoop(std::bind(&UdpServerEventLoopTmpl::startRecv, this));
}
// sendto thread-safe
int sendto(const void* data, int size, struct sockaddr* peeraddr = NULL) {
if (channel == NULL) return -1;
std::lock_guard<std::mutex> locker(sendto_mutex);
if (peeraddr) hio_set_peeraddr(channel->io(), peeraddr, SOCKADDR_LEN(peeraddr));
return channel->write(data, size);
}
int sendto(Buffer* buf, struct sockaddr* peeraddr = NULL) {
return sendto(buf->data(), buf->size(), peeraddr);
}
int sendto(const std::string& str, struct sockaddr* peeraddr = NULL) {
return sendto(str.data(), str.size(), peeraddr);
}
#if WITH_KCP
void setKcp(kcp_setting_t* setting) {
if (setting == NULL) {
HV_FREE(kcp_setting);
return;
}
if (kcp_setting == NULL) {
HV_ALLOC_SIZEOF(kcp_setting);
}
*kcp_setting = *setting;
}
#endif
public:
std::string host;
int port;
TSocketChannelPtr channel;
#if WITH_KCP
kcp_setting_t* kcp_setting;
#endif
// Callback
std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage;
// NOTE: Use Channel::isWriteComplete in onWriteComplete callback to determine whether all data has been written.
std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete;
private:
std::mutex sendto_mutex;
EventLoopPtr loop_;
};
template<class TSocketChannel = SocketChannel>
class UdpServerTmpl : private EventLoopThread, public UdpServerEventLoopTmpl<TSocketChannel> {
public:
UdpServerTmpl(EventLoopPtr loop = NULL)
: EventLoopThread(loop)
, UdpServerEventLoopTmpl<TSocketChannel>(EventLoopThread::loop())
, is_loop_owner(loop == NULL)
{}
virtual ~UdpServerTmpl() {
stop(true);
}
const EventLoopPtr& loop() {
return EventLoopThread::loop();
}
// start thread-safe
void start(bool wait_threads_started = true) {
if (isRunning()) {
UdpServerEventLoopTmpl<TSocketChannel>::start();
} else {
EventLoopThread::start(wait_threads_started, std::bind(&UdpServerTmpl::startRecv, this));
}
}
// stop thread-safe
void stop(bool wait_threads_stopped = true) {
UdpServerEventLoopTmpl<TSocketChannel>::closesocket();
if (is_loop_owner) {
EventLoopThread::stop(wait_threads_stopped);
}
}
private:
bool is_loop_owner;
};
typedef UdpServerTmpl<SocketChannel> UdpServer;
}
#endif // HV_UDP_SERVER_HPP_

51
third_party/libhv/evpp/UdpServer_test.cpp vendored Executable file
View File

@@ -0,0 +1,51 @@
/*
* UdpServer_test.cpp
*
* @build make evpp
* @server bin/UdpServer_test 1234
* @client bin/UdpClient_test 1234
*
*/
#include <iostream>
#include "UdpServer.h"
using namespace hv;
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("Usage: %s port\n", argv[0]);
return -10;
}
int port = atoi(argv[1]);
UdpServer srv;
int bindfd = srv.createsocket(port);
if (bindfd < 0) {
return -20;
}
printf("server bind on port %d, bindfd=%d ...\n", port, bindfd);
srv.onMessage = [](const SocketChannelPtr& channel, Buffer* buf) {
// echo
printf("< %.*s\n", (int)buf->size(), (char*)buf->data());
channel->write(buf);
};
srv.start();
std::string str;
while (std::getline(std::cin, str)) {
if (str == "close") {
srv.closesocket();
} else if (str == "start") {
srv.start();
} else if (str == "stop") {
srv.stop();
break;
} else {
srv.sendto(str);
}
}
return 0;
}

7
third_party/libhv/evpp/build_test.sh vendored Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/bash
SCRIPT_DIR=$(cd `dirname $0`; pwd)
ROOT_DIR=${SCRIPT_DIR}/..
cd ${ROOT_DIR}
make evpp