您的位置:首页 > 移动开发

node-haystack Episode-4: Wrapper of libuv

2016-09-10 12:50 423 查看

Preface

Before starting our project: node-haystack, we need some handful utilities. Using the C style libuv is boring. Then here comes the wrappers.

Environment

Hardware

TypeBrandCapacityFrequency
MBMSI-Z77HD
MemoryN/A32GB1600Mhz
CPUIntel® Core™ i7-47904Core Hyper-thread3.60GHzx8
HD(os)LSI MR9260-8i120GBx2
HD(data)Seagate8T(Ext enclosure, raid 0, USB3.0)

Software

TypeNameVer
OSCentOS7.0
CompilerGcc6.1
JavaScriptnode.js4.5.0
IDEatom1.8.0
C++ libraryboost1.6.1

Code style

The C++ code follows this google style

If you have difficulty with this site, here is the local resource.

Comments

Doxygen style comments will be applied.

Define & typedef

Here are some predefined type, macros:

namespace bst = boost;
namespace bfs = boost::filesystem;
namespace js = v8;

using u8    =   unsigned char;
using byte  =   unsigned char;
using u16   =   unsigned short;
using i16   =   signed short;
using u32   =   unsigned int;
using i32   =   signed int;
using u64   =   unsigned long long;
using i64   =   signed long long;
using u128  =   unsigned __int128;
using i128  =   signed __int128;

#define DECL_CB(name, ...) \
using callback_ ## name = bst::function<void(__VA_ARGS__)>; \
using cb_ ## name ## _t = callback_ ## name;

template<typename T>
using vec_t = std::vector<T>;

template<typename T>
using list_t = std::list<T>;

template<typename T>
using shared_t = std::shared_ptr<T>;

template<typename K, typename T>
using map_t = std::unordered_map<K, T>;

using uuid_t = buid::uuid;

using shared_mtx_t = bst::shared_mutex;
using slock_t = bst::upgrade_lock<bst::shared_mutex>;
using ulock_t = bst::upgrade_to_unique_lock<bst::shared_mutex>;


Errors

A script is used for generate errors automatically from a template, which is a plaint text and is easy to change.

The script:

#!/bin/bash

input="error.def"
output="error.hpp"

date=$(date +%d/%b/%Y)
header="
/*!\n
\t\\\file\terror.hpp\n
\t\\\brief\tDefine error and according messages.\n
\t\\\note\tDo NOT modify this file manually, it's automatically\n
\t\t\tgenerated by script. Instead, you should modify the error.def\n
\t\t\tand re-run gen-err.sh to apply the change.\n
\t\\\author\tHailing.Zhou\n
\t\\\date\t$date\n
*/\n
//////////////////////////////////////////////////////////\n
// CAUTION:\n
// DO NOT modify this file manually.\n
// This file is automatically generated by scripts.\n
//////////////////////////////////////////////////////////\n
#ifndef ERROR_HPP\n
#define ERROR_HPP\n
\n
namespace hhcloud {\n
\t#define _ERR(name)    HC_ERROR_## name\n
\t#define _ERR_STR(name) ErrorToString(_ERR(name))\n"

footer="\n
} // ns hhcloud\n
\n
#endif // ERROR_HPP\n"

func_beg="\n\tstatic const char* ErrorToString(int err) {\n
\t\tswitch(err) {"

func_end="\t\t\tdefault: return \"Unknown error\";\n
\t\t} // End of switch\n
\t} // End of ErrorToString\n"

gen_content() {
declare -a def_array
declare -a case_array
count=0
err_code=-8000
err_name=

while IFS='' read -r line || [[ -n "$line" ]]; do
line=$(echo $line)
[[ ${#line} -eq 0 ]] && continue

if echo $line | grep -qE "^\/\/.*"; then
def_array[$count]="\n\t$line"
case_array[$count]="\n\t\t\t$line"

count=$((count + 1))
else
if echo $line | grep -qiE "^code=\d*"; then
err_code=${line#*=}
else
err_name=${line%:*}
err_msg=${line#*:}

# If error message not defined, use error name as the message
if [ "$err_msg" == "" ]; then
err_msg=$err_name
fi

def_array[$count]="\tconst int _ERR($err_name) = $err_code;"
case_array[$count]="\t\t\tcase _ERR($err_name): return \"$err_msg\";"

err_code=$((err_code - 1))
count=$((count + 1))
fi
fi
done < $input

n=0
while [[ $n -lt $count ]]; do
echo -e ${def_array[$n]} >> $output
n=$((n + 1))
done

# output function
echo -e $func_beg >> $output

n=0
while [[ $n -lt $count ]]; do
echo -e ${case_array[$n]} >> $output
n=$((n + 1))
done

# end of function
echo -e $func_end >> $output
}

gen_err() {
echo "" > $output
echo -e $header >> $output

gen_content

echo -e $footer >> $output
}

echo "Generate $output"
gen_err


A sample of error template:

Code=0
NOERROR:

// Common error, begins from -8000
FAIL:
INVALID:
INTERNAL:
NOT_EXIST:
NOT_FOUND:
NOT_IMPL:
ALLOC_MEM:

// File operation, starts from -8100
Code=-8100
NOT_OPEN:
FAIL_OPEN:

// Haystack Volume, starts from -8200
Code=-8200
VOL_READ_BLOCK:
VOL_CACHE_FULL:
VOL_INVALID_FILE:
VOL_INVALID_VOL:
VOL_INVALID_BLOCK_HEADER:
VOL_INVALID_BLOCK_FOOTER:
VOL_EXCEED_BLOCKS:
VOL_EXCEED_SIZE:
VOL_NONIDENTICAL:
VOL_FAILED_TO_WRITE:
VOL_FAILED_TO_UNLINK:

// Command library, starts from -8300
Code=-8300
CMD_LIB_LOADED:
CMD_LIB_LOAD:
CMD_LIB_UNLOAD:
CMD_LIB_NO_FUNC:
CMD_LIB_INVALID_CMD:

// Command, starts from -8400
Code=-8400
CMD_NO_SRC:
CMD_INVALID_SRC:

// Text Render, starts from -8500
Code=-8500
CMD_TXT_EMPTY:
CMD_TXT_NO_FONT:
CMD_TXT_INIT_FONT:
CMD_TXT_READ_FONT:
CMD_TXT_INIT_RENDER:
CMD_TXT_RENDER_TXT:

// Add more errors here

// End of errors


The generated error.hpp looks like:

/*!
\file   error.hpp
\brief  Define error and according messages.
\note   Do NOT modify this file manually, it's automatically
generated by script. Instead, you should modify the error.def
and re-run gen-err.sh to apply the change.
\author igame
\date   07/Sep/2016
*/
//////////////////////////////////////////////////////////
// CAUTION:
// DO NOT modify this file manually.
// This file is automatically generated by scripts.
//////////////////////////////////////////////////////////
#ifndef ERROR_HPP
#define ERROR_HPP

namespace hhcloud {
#define _ERR(name) HC_ERROR_## name
#define _ERR_STR(name) ErrorToString(_ERR(name))

const int _ERR(NOERROR) = 0;

// Common error, begins from -8000
const int _ERR(FAIL) = -1;
const int _ERR(INVALID) = -2;
const int _ERR(INTERNAL) = -3;
const int _ERR(NOT_EXIST) = -4;
const int _ERR(NOT_FOUND) = -5;
const int _ERR(NOT_IMPL) = -6;
const int _ERR(ALLOC_MEM) = -7;

// File operation, starts from -8100
const int _ERR(NOT_OPEN) = -8100;
const int _ERR(FAIL_OPEN) = -8101;

// Haystack Volume, starts from -8200
const int _ERR(VOL_READ_BLOCK) = -8200;
const int _ERR(VOL_CACHE_FULL) = -8201;
const int _ERR(VOL_INVALID_FILE) = -8202;
const int _ERR(VOL_INVALID_VOL) = -8203;
const int _ERR(VOL_INVALID_BLOCK_HEADER) = -8204;
const int _ERR(VOL_INVALID_BLOCK_FOOTER) = -8205;
const int _ERR(VOL_EXCEED_BLOCKS) = -8206;
const int _ERR(VOL_EXCEED_SIZE) = -8207;
const int _ERR(VOL_NONIDENTICAL) = -8208;
const int _ERR(VOL_FAILED_TO_WRITE) = -8209;
const int _ERR(VOL_FAILED_TO_UNLINK) = -8210;

// Command library, starts from -8300
const int _ERR(CMD_LIB_LOADED) = -8300;
const int _ERR(CMD_LIB_LOAD) = -8301;
const int _ERR(CMD_LIB_UNLOAD) = -8302;
const int _ERR(CMD_LIB_NO_FUNC) = -8303;
const int _ERR(CMD_LIB_INVALID_CMD) = -8304;

// Command, starts from -8400
const int _ERR(CMD_NO_SRC) = -8400;
const int _ERR(CMD_INVALID_SRC) = -8401;

// Text Render, starts from -8500
const int _ERR(CMD_TXT_EMPTY) = -8500;
const int _ERR(CMD_TXT_NO_FONT) = -8501;
const int _ERR(CMD_TXT_INIT_FONT) = -8502;
const int _ERR(CMD_TXT_READ_FONT) = -8503;
const int _ERR(CMD_TXT_INIT_RENDER) = -8504;
const int _ERR(CMD_TXT_RENDER_TXT) = -8505;

// Add more errors here

// End of errors

static const char* ErrorToString(int err) {
switch(err) {
case _ERR(NOERROR): return "NOERROR";

// Common error, begins from -8000
case _ERR(FAIL): return "FAIL";
case _ERR(INVALID): return "INVALID";
case _ERR(INTERNAL): return "INTERNAL";
case _ERR(NOT_EXIST): return "NOT_EXIST";
case _ERR(NOT_FOUND): return "NOT_FOUND";
case _ERR(NOT_IMPL): return "NOT_IMPL";
case _ERR(ALLOC_MEM): return "ALLOC_MEM";

// File operation, starts from -8100
case _ERR(NOT_OPEN): return "NOT_OPEN";
case _ERR(FAIL_OPEN): return "FAIL_OPEN";

// Haystack Volume, starts from -8200
case _ERR(VOL_READ_BLOCK): return "VOL_READ_BLOCK";
case _ERR(VOL_CACHE_FULL): return "VOL_CACHE_FULL";
case _ERR(VOL_INVALID_FILE): return "VOL_INVALID_FILE";
case _ERR(VOL_INVALID_VOL): return "VOL_INVALID_VOL";
case _ERR(VOL_INVALID_BLOCK_HEADER): return "VOL_INVALID_BLOCK_HEADER";
case _ERR(VOL_INVALID_BLOCK_FOOTER): return "VOL_INVALID_BLOCK_FOOTER";
case _ERR(VOL_EXCEED_BLOCKS): return "VOL_EXCEED_BLOCKS";
case _ERR(VOL_EXCEED_SIZE): return "VOL_EXCEED_SIZE";
case _ERR(VOL_NONIDENTICAL): return "VOL_NONIDENTICAL";
case _ERR(VOL_FAILED_TO_WRITE): return "VOL_FAILED_TO_WRITE";
case _ERR(VOL_FAILED_TO_UNLINK): return "VOL_FAILED_TO_UNLINK";

// Command library, starts from -8300
case _ERR(CMD_LIB_LOADED): return "CMD_LIB_LOADED";
case _ERR(CMD_LIB_LOAD): return "CMD_LIB_LOAD";
case _ERR(CMD_LIB_UNLOAD): return "CMD_LIB_UNLOAD";
case _ERR(CMD_LIB_NO_FUNC): return "CMD_LIB_NO_FUNC";
case _ERR(CMD_LIB_INVALID_CMD): return "CMD_LIB_INVALID_CMD";

// Command, starts from -8400
case _ERR(CMD_NO_SRC): return "CMD_NO_SRC";
case _ERR(CMD_INVALID_SRC): return "CMD_INVALID_SRC";

// Text Render, starts from -8500
case _ERR(CMD_TXT_EMPTY): return "CMD_TXT_EMPTY";
case _ERR(CMD_TXT_NO_FONT): return "CMD_TXT_NO_FONT";
case _ERR(CMD_TXT_INIT_FONT): return "CMD_TXT_INIT_FONT";
case _ERR(CMD_TXT_READ_FONT): return "CMD_TXT_READ_FONT";
case _ERR(CMD_TXT_INIT_RENDER): return "CMD_TXT_INIT_RENDER";
case _ERR(CMD_TXT_RENDER_TXT): return "CMD_TXT_RENDER_TXT";

// Add more errors here

// End of errors
default: return "Unknown error";
} // End of switch
} // End of ErrorToString
} // ns hhcloud

#endif // ERROR_HPP


Wrapper of Async File Access

There a dozen of file relative functions in libuv, but we only concentrate on functions about stating and read/write. Let code talk:

/*!
\brief Asyncronous file read/write class, which hides the details of libuv and expose it as C++ style.
*/
class AsyncFile {
public:
/** A copy of uv_time_t */
typedef struct {
i64 sec;
i64 nsec;
} time_type;

/** File attributes */
typedef struct {
u64 dev;
u64 mode;
u64 nlink;
u64 uid;
u64 gid;
u64 rdev;
u64 ino;
u64 size;
u64 blksize;
u64 blocks;
u64 flags;
u64 gen;
time_type atim;
time_type mtim;
time_type ctim;
time_type birthtim;
} file_stat_t;

DECL_CB(stat, int /*!< Error code: 0 succeeded; otherwise failed. */, const file_stat_t& /*!< Set of file attributes if succeded. */, void* /*!< User data */)
DECL_CB(open, int /*!< Error code. */, void* /*!< User data */)
DECL_CB(read, int /*!< Error code. */, shared_t<vec_t<char>>& /*!< Fetched data if succeeded. */, void* /*!< User data */)
DECL_CB(write, int, u64 /*!< Number of bytes writed. */, void* /*!< User data */)
DECL_CB(close, int, void*  /*!< User data */)

private:
/** callback information which will be used to bring necessary into libuv callback */
template< class T >
struct callback_info_t{
typedef T   callback_type;
AsyncFile*  sender;
shared_t<vec_t<char>>   data;
uv_buf_t    buf;
void*   user_data;
callback_type cb;
}; // callback_info_t;

using ci_read_t = callback_info_t<cb_read_t>;
using ci_write_t = callback_info_t<cb_write_t>;

public:
AsyncFile() {}
AsyncFile(const AsyncFile&) = delete;

~AsyncFile() {
Close();
}
/*!
\brief open a file for asynchronous operation.
\return None
*/
void Open(const std::string& path /*!< File path. */, const cb_open_t& cb /*!< Callback function. */, void* user_data = nullptr /*!< Extra user data which will be passed to callback */) {
uv_fs_t* req = this->CreateReq<cb_open_t>(cb, user_data);

uv_fs_open(uv_default_loop(), req, path.c_str(), O_CREAT | O_RDWR, 0, AsyncFile::OnOpen);
}

/*!
\brief close opened file.
\return None.
*/
void Close(const cb_close_t& cb = [&](int, void*) { return; }/*!< Callback function */, void* user_data = nullptr /*!< Extra user data which will be passed to callback */) {
if (m_fd > 0) {
uv_fs_t* req = this->CreateReq<cb_close_t>(cb, user_data);

uv_fs_close(uv_default_loop(), req, m_fd, AsyncFile::OnClose);
} else {
cb(_ERR(NOT_OPEN), user_data);
}
}

/*!
\brief stat a file.
\return None.
*/
void Stat(const std::string& path /*!< File path. */, const cb_stat_t& cb /*!< Callback function */, void* user_data = nullptr  /*!< Extra user data which will be passed to callback */) {
uv_fs_t* req = this->CreateReq<cb_stat_t>(cb, user_data);

uv_fs_stat(uv_default_loop(), req, path.c_str(), AsyncFile::OnStat);
}

/*!
\brief read an amount of data from opened file begining at specified offset.
\return None.
*/
void Read(u64 offset, size_t size, const cb_read_t& cb, void* user_data = nullptr  /*!< Extra user data which will be passed to callback */) {
assert(m_fd != 0);

auto req = this->CreateReq<cb_read_t>(cb, user_data, size);
auto ci = reinterpret_cast<ci_read_t *>(req->data);

uv_fs_read(uv_default_loop(), req, m_fd, &ci->buf, 1, offset, AsyncFile::OnRead);
}

/*!
\brief write data into file from specified offset.
*/
void Write(u64 offset, shared_t<vec_t<char>>& array, const cb_write_t& cb, void* user_data = nullptr  /*!< Extra user data which will be passed to callback */) {
assert(m_fd != 0);

auto req = this->CreateReq<cb_write_t>(cb, user_data, array);
auto ci = reinterpret_cast<ci_write_t *>(req->data);

uv_fs_write(uv_default_loop(), req, m_fd, &ci->buf, 1, offset, AsyncFile::OnWrite);
}

private:
/*!
\brief Create a uv_fs_t object.
\return Pointer to uv_fs_t object.
*/
template< class T>
inline uv_fs_t* CreateReq(const T& cb, void* user_data) {
uv_fs_t* req = (uv_fs_t *)malloc(sizeof(uv_fs_t));
std::memset(req, 0x0, sizeof(uv_fs_t));

callback_info_t<T>* ci = new callback_info_t<T>;

ci->sender = this;
ci->cb = cb;
ci->user_data = user_data;

req->data = ci;

return req;
}

template<class T>
inline uv_fs_t* CreateReq(const T& cb, void* user_data, size_t size) {
auto req = CreateReq<T>(cb, user_data);
auto ci = reinterpret_cast<callback_info_t<T> *>(req->data);

ci->data = mk_shared<vec_t<char>>();
ci->data->resize(size);

ci->buf.base = &(*ci->data)[0];
ci->buf.len = ci->data->size();

return req;
}

template<class T>
inline uv_fs_t* CreateReq(const T& cb, void* user_data, shared_t<vec_t<char>>& svec) {
auto req = CreateReq<T>(cb, user_data);
auto ci = reinterpret_cast<callback_info_t<T> *>(req->data);

ci->data = svec;
ci->buf.base = &(*ci->data)[0];
ci->buf.len = ci->data->size();

return req;
}

/*!
\brief Release created uv_fs_t object.
*/
template< class T >
inline void DelReq(uv_fs_t* req) {
auto ci = reinterpret_cast<callback_info_t<T> *>(req->data);

delete ci;
uv_fs_req_cleanup(req);
}

private:
/*!
\brief Callback for uv_fs_stat.
*/
static void OnStat(uv_fs_t* req) {
auto ci = reinterpret_cast<callback_info_t<cb_stat_t> *>(req->data);

if (req->result < 0) {
int err = req->result;
ci->cb(err, {}, ci->user_data);
} else {
auto ft = reinterpret_cast<file_stat_t *>(&req->statbuf);

ci->cb(0, *ft, ci->user_data);
}

ci->sender->DelReq<cb_stat_t>(req);
}

/*!
\brief Callback for uv_fs_open.
*/
static void OnOpen(uv_fs_t* req) {
auto ci = reinterpret_cast<callback_info_t<cb_open_t> *>(req->data);

if (req->result < 0) {
ci->cb(req->result, ci->user_data);
} else {
ci->sender->m_fd = req->result;
ci->cb(0, ci->user_data);
}

ci->sender->DelReq<cb_open_t>(req);
}

/*!
\brief Callback for uv_fs_close
*/
static void OnClose(uv_fs_t* req) {
auto ci = reinterpret_cast<callback_info_t<cb_close_t> *>(req->data);

if (req->result < 0) {
ci->cb(req->result, ci->user_data);
} else {
ci->cb(0, ci->user_data);
}

ci->sender->DelReq<cb_close_t>(req);
// ci->sender->m_fd = 0;
}

/*!
\brief Callback for uv_fs_read
*/
static void OnRead(uv_fs_t* req) {
auto ci = reinterpret_cast<callback_info_t<cb_read_t> *>(req->data);

if (req->result < 0) {
auto temp = mk_shared<vec_t<char>>();

ci->cb(req->result, temp, ci->user_data);
} else {
if (req->result == 0) {
auto temp = mk_shared<vec_t<char>>();

ci->cb(0, temp, ci->user_data);
} else {
if (req->result != ci->data->size()) {
auto temp = mk_shared<vec_t<char>>();

ci->cb(_ERR(FAIL), temp, ci->user_data);
} else {
ci->cb(0, ci->data, ci->user_data);
}
}
}

ci->sender->DelReq<cb_read_t>(req);
}

/*!
\brief Callback for uv_fs_write.
*/
static void OnWrite(uv_fs_t* req) {
auto ci = reinterpret_cast<callback_info_t<cb_write_t> *>(req->data);

if (req->result < 0) {
ci->cb(req->result, 0, ci->user_data);
} else {
ci->cb(0, req->result, ci->user_data);
}

ci->sender->DelReq<cb_write_t>(req);
}
private:
uv_file m_fd = 0; /*!< uv file handle. */
}; // class AsyncFile


Wrapper of Async Work Queue

libuv uses work queue to perform asynchronous operation. More details can be found in libuv’s official documents.

The declaration of AsyncWork looks like:

/*!
\brief C++ wrapper for libuv asynchronous queue work.
\note The template parameter T is the type of user data which will be used for user's extra data when posting work item into queue.
*/
template<class T>
class AsyncWorker {
public:
DECL_CB(work, T* /*!< User data. */)
DECL_CB(after_work, T* /*!< User data. */, bool /*!< Cancelation flag */)

private:
typedef struct {
AsyncWorker* sender;
cb_work_t cb_work;
cb_after_work_t cb_after_work;
T* user_data;
} callback_info_t;

using ci_t = callback_info_t;

public:
/*!
\brief Post work request to queue.
\return None.
*/
inline void post(T* user_data /*!< User data. */, const cb_work_t& cb_work /*!< Callback when work is been processing. */, const cb_after_work_t& cb_afterWork /*!< Callback when work has been done. */) {
uv_work_t* req = createReq(user_data, cb_work, cb_afterWork);

uv_queue_work(uv_default_loop(), req, AsyncWorker::onWork, AsyncWorker::onAfterWork);
}

private:
/*!
\brief Create work request.
\return uv_work_t* libuv work request type.
*/
inline uv_work_t* createReq(T* user_data /*!< User data. */, const cb_work_t& cb_work /*!< Callback when work is been processing. */, const cb_after_work_t& cb_afterWork /*!< Callback when work is done. */) {
uv_work_t* req = (uv_work_t *)malloc(sizeof(uv_work_t));
std::memset(req, 0x0, sizeof(uv_work_t));

auto ci = new ci_t{ this, cb_work, cb_afterWork, user_data };

req->data = ci;

return req;
}

/*!
\brief Delete work request.
\return None.
*/
inline void delReq(uv_work_t* req /*!< libuv work struct. */) {
auto ci = reinterpret_cast<ci_t *>(req->data);

delete ci;
free(req);
}

/*!
\brief Event handle for libuv work processing.
\return None.
*/
static void onWork(uv_work_t* req /*!< Libuv work struct. */) {
auto ci = reinterpret_cast<ci_t *>(req->data);

ci->cb_work(ci->user_data);
}

/*!
\brief Event handle for libuv work done.
\return None.
*/
static void onAfterWork(uv_work_t* req /*!< libuv work struct. */, int status /*!< status of work. \note Now only UV_ECANCELED is cared about. */) {
auto ci = reinterpret_cast<ci_t *>(req->data);

ci->cb_after_work(ci->user_data, status == UV_ECANCELED);

ci->sender->delReq(req);
}
}; // class AsyncWorker


Wrapper of Timer

Make the libuv timer featurn more easily to use.

/*!
\brief C++ wrapper for libuv timer
*/
class AsyncTimer {
public:
/** Timer event callback. */
DECL_CB(timer, void)

const u64 INTERVAL_MIN = 1; /*!< Minimum interval for timer: 1 Millisecond. */
const u64 DEFAULT_INTERVAL = 1000;  /*!< Default interval: 1 second. */

private:
//** Callback information. */
typedef struct {
AsyncTimer* sender;
cb_timer_t cb;
} callback_info_t;

using ci_t = callback_info_t;

public:
AsyncTimer() {
Init();
}

AsyncTimer(const AsyncTimer&) = delete;

~AsyncTimer() {
Stop();
assert(m_handle != nullptr);
free(m_handle);
}

/*!
\brief Check if the timer already started.
\return bool true/false.
*/
inline bool IsStarted() {
return m_started;
}

/*!
\brief Set interval, unit: millisecond.
\note  interval must equal or be greater than INTERVAL_MIN.
\return None.
*/
inline void SetInterval(u64 msec) {
uv_timer_set_repeat(m_handle, msec < INTERVAL_MIN ? INTERVAL_MIN : msec);
}

/*!
\brief Get interval.
\return u64 interval value.
*/
inline u64 GetInterval() {
return uv_timer_get_repeat(m_handle);
}

/*!
\brief Start timer. When the specified interval arrives, specified callback(defined by cb_timer_t) will be called.
\return None.
*/
inline void Start(const cb_timer_t& cb) {
if (IsStarted()) return;

m_handle->data = new ci_t { this, cb };
uv_timer_start(m_handle, AsyncTimer::OnTimer, 0, GetInterval());

m_started = true;
}

/*!
\brief Stop timer.
\return None.
*/
inline void Stop() {
if (IsStarted()) {
m_started = false;

auto ci = reinterpret_cast<ci_t *>(m_handle->data);
delete ci;

m_handle->data = nullptr;
uv_timer_stop(m_handle);

TRACE("Timer stopped");
}
}

private:
/*!
\brief Initialize timer handler.
\return None.
*/
inline void Init() {
m_handle = (uv_timer_t *)malloc(sizeof(uv_timer_t));
uv_timer_init(uv_default_loop(), m_handle);
m_handle->data = nullptr;
m_started = false;
}

/*!
\brief Timer handling event for libuv.
\return None.
*/
static void OnTimer(uv_timer_t* handle) {
auto ci = reinterpret_cast<ci_t *>(handle->data);

if (ci->sender->IsStarted()) {
ci->cb();
}
}

private:
uv_timer_t* m_handle = nullptr; /*!< Timer handle */
bst::atomic_bool m_started; /*!< Flag of if timer started/stopped */
}; // class AsyncTimer
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  libuv C++