1073 lines
29 KiB
Lua
1073 lines
29 KiB
Lua
|
|
local ffi = require 'ffi'
|
||
|
|
local cast = ffi.cast
|
||
|
|
local C = ffi.C
|
||
|
|
local UV = nil
|
||
|
|
|
||
|
|
print("OS:", ffi.os)
|
||
|
|
if ffi.os == 'Windows' then
|
||
|
|
UV = ffi.load('libuv')
|
||
|
|
ffi.cdef [[
|
||
|
|
typedef uint32_t ULONG;
|
||
|
|
typedef struct uv_buf_t {
|
||
|
|
ULONG len;
|
||
|
|
char* base;
|
||
|
|
} uv_buf_t;
|
||
|
|
]]
|
||
|
|
end
|
||
|
|
if ffi.os == "Linux" then
|
||
|
|
UV = ffi.load('./bin/linux/x64/libuv.so')
|
||
|
|
ffi.cdef [[
|
||
|
|
typedef struct uv_buf_t {
|
||
|
|
char* base;
|
||
|
|
size_t len;
|
||
|
|
} uv_buf_t;
|
||
|
|
]]
|
||
|
|
end
|
||
|
|
|
||
|
|
ffi.cdef [[
|
||
|
|
uv_buf_t uv_buf_init(const char* base, unsigned int len);
|
||
|
|
|
||
|
|
typedef enum {
|
||
|
|
UV_EOF = -4095
|
||
|
|
} uv_errno_t;
|
||
|
|
|
||
|
|
typedef enum {
|
||
|
|
UV_UNKNOWN_HANDLE = 0,
|
||
|
|
UV_ASYNC,
|
||
|
|
UV_CHECK,
|
||
|
|
UV_FS_EVENT,
|
||
|
|
UV_FS_POLL,
|
||
|
|
UV_HANDLE,
|
||
|
|
UV_IDLE,
|
||
|
|
UV_NAMED_PIPE,
|
||
|
|
UV_POLL,
|
||
|
|
UV_PREPARE,
|
||
|
|
UV_PROCESS,
|
||
|
|
UV_STREAM,
|
||
|
|
UV_TCP,
|
||
|
|
UV_TIMER,
|
||
|
|
UV_TTY,
|
||
|
|
UV_UDP,
|
||
|
|
UV_SIGNAL,
|
||
|
|
UV_FILE,
|
||
|
|
UV_HANDLE_TYPE_MAX
|
||
|
|
} uv_handle_type;
|
||
|
|
|
||
|
|
typedef enum {
|
||
|
|
UV_UNKNOWN_REQ = 0,
|
||
|
|
UV_REQ,
|
||
|
|
UV_CONNECT,
|
||
|
|
UV_WRITE,
|
||
|
|
UV_SHUTDOWN,
|
||
|
|
UV_UDP_SEND,
|
||
|
|
UV_FS,
|
||
|
|
UV_WORK,
|
||
|
|
UV_GETADDRINFO,
|
||
|
|
UV_GETNAMEINFO,
|
||
|
|
UV_REQ_TYPE_MAX,
|
||
|
|
} uv_req_type;
|
||
|
|
|
||
|
|
size_t uv_loop_size(void);
|
||
|
|
size_t uv_req_size(uv_req_type type);
|
||
|
|
size_t uv_handle_size(uv_handle_type type);
|
||
|
|
]]
|
||
|
|
|
||
|
|
ffi.cdef(
|
||
|
|
string.format(
|
||
|
|
[[
|
||
|
|
struct uv_loop_s {uint8_t _[%d];};
|
||
|
|
struct uv_connect_s {uint8_t _[%d];};
|
||
|
|
struct uv_write_s {uint8_t _[%d];};
|
||
|
|
struct uv_shutdown_s {uint8_t _[%d];};
|
||
|
|
struct uv_getaddrinfo_s {uint8_t _[%d];};
|
||
|
|
struct uv_tcp_s {uint8_t _[%d];};
|
||
|
|
struct uv_tty_s {uint8_t _[%d];};
|
||
|
|
struct uv_pipe_s {uint8_t _[%d];};
|
||
|
|
struct uv_timer_s {uint8_t _[%d];};
|
||
|
|
]],
|
||
|
|
tonumber(UV.uv_loop_size()),
|
||
|
|
tonumber(UV.uv_req_size(UV.UV_CONNECT)),
|
||
|
|
tonumber(UV.uv_req_size(UV.UV_WRITE)),
|
||
|
|
tonumber(UV.uv_req_size(UV.UV_SHUTDOWN)),
|
||
|
|
tonumber(UV.uv_req_size(UV.UV_GETADDRINFO)),
|
||
|
|
tonumber(UV.uv_handle_size(UV.UV_TCP)),
|
||
|
|
tonumber(UV.uv_handle_size(UV.UV_TTY)),
|
||
|
|
tonumber(UV.uv_handle_size(UV.UV_NAMED_PIPE)),
|
||
|
|
tonumber(UV.uv_handle_size(UV.UV_TIMER))
|
||
|
|
)
|
||
|
|
)
|
||
|
|
|
||
|
|
ffi.cdef [[
|
||
|
|
typedef struct uv_loop_s uv_loop_t;
|
||
|
|
typedef struct uv_req_s uv_req_t;
|
||
|
|
typedef struct uv_write_s uv_write_t;
|
||
|
|
typedef struct uv_connect_s uv_connect_t;
|
||
|
|
typedef struct uv_shutdown_s uv_shutdown_t;
|
||
|
|
typedef struct uv_getaddrinfo_s uv_getaddrinfo_t;
|
||
|
|
typedef struct uv_handle_s uv_handle_t;
|
||
|
|
typedef struct uv_stream_s uv_stream_t;
|
||
|
|
typedef struct uv_tcp_s uv_tcp_t;
|
||
|
|
typedef struct uv_tty_s uv_tty_t;
|
||
|
|
typedef struct uv_pipe_s uv_pipe_t;
|
||
|
|
typedef struct uv_timer_s uv_timer_t;
|
||
|
|
|
||
|
|
typedef enum uv_run_mode_e {
|
||
|
|
UV_RUN_DEFAULT = 0,
|
||
|
|
UV_RUN_ONCE,
|
||
|
|
UV_RUN_NOWAIT
|
||
|
|
} uv_run_mode;
|
||
|
|
|
||
|
|
int uv_ip4_addr(const char* ip, int port, struct sockaddr_in* addr);
|
||
|
|
int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr);
|
||
|
|
int uv_ip4_name(const struct sockaddr_in* src, char* dst, size_t size);
|
||
|
|
int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size);
|
||
|
|
int uv_inet_ntop(int af, const void* src, char* dst, size_t size);
|
||
|
|
int uv_inet_pton(int af, const char* src, void* dst);
|
||
|
|
|
||
|
|
const char* uv_err_name(int err);
|
||
|
|
const char* uv_strerror(int err);
|
||
|
|
]]
|
||
|
|
|
||
|
|
local function makeCallback(type)
|
||
|
|
local thread, isMain = coroutine.running()
|
||
|
|
if not thread or isMain then return end
|
||
|
|
local cb
|
||
|
|
cb =
|
||
|
|
cast(
|
||
|
|
type,
|
||
|
|
function(...)
|
||
|
|
-- print('oncall', ...)
|
||
|
|
cb:free()
|
||
|
|
assert(coroutine.resume(thread, ...))
|
||
|
|
end
|
||
|
|
)
|
||
|
|
return cb
|
||
|
|
end
|
||
|
|
|
||
|
|
local function uvGetError(status)
|
||
|
|
return ffi.string(UV.uv_err_name(status)) .. ': ' .. ffi.string(UV.uv_strerror(status))
|
||
|
|
end
|
||
|
|
|
||
|
|
local function uvCheck(status)
|
||
|
|
if status < 0 then
|
||
|
|
error(uvGetError(status))
|
||
|
|
else
|
||
|
|
return status
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local Loop = {}
|
||
|
|
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
-- File
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
local File = {}
|
||
|
|
|
||
|
|
ffi.cdef [[
|
||
|
|
|
||
|
|
typedef uint32_t DWORD;
|
||
|
|
|
||
|
|
typedef enum {
|
||
|
|
UV_FS_UNKNOWN = -1,
|
||
|
|
UV_FS_CUSTOM,
|
||
|
|
UV_FS_OPEN,
|
||
|
|
UV_FS_CLOSE,
|
||
|
|
UV_FS_READ,
|
||
|
|
UV_FS_WRITE,
|
||
|
|
UV_FS_SENDFILE,
|
||
|
|
UV_FS_STAT,
|
||
|
|
UV_FS_LSTAT,
|
||
|
|
UV_FS_FSTAT,
|
||
|
|
UV_FS_FTRUNCATE,
|
||
|
|
UV_FS_UTIME,
|
||
|
|
UV_FS_FUTIME,
|
||
|
|
UV_FS_ACCESS,
|
||
|
|
UV_FS_CHMOD,
|
||
|
|
UV_FS_FCHMOD,
|
||
|
|
UV_FS_FSYNC,
|
||
|
|
UV_FS_FDATASYNC,
|
||
|
|
UV_FS_UNLINK,
|
||
|
|
UV_FS_RMDIR,
|
||
|
|
UV_FS_MKDIR,
|
||
|
|
UV_FS_MKDTEMP,
|
||
|
|
UV_FS_RENAME,
|
||
|
|
UV_FS_SCANDIR,
|
||
|
|
UV_FS_LINK,
|
||
|
|
UV_FS_SYMLINK,
|
||
|
|
UV_FS_READLINK,
|
||
|
|
UV_FS_CHOWN,
|
||
|
|
UV_FS_FCHOWN,
|
||
|
|
UV_FS_REALPATH,
|
||
|
|
UV_FS_COPYFILE,
|
||
|
|
UV_FS_LCHOWN,
|
||
|
|
UV_FS_OPENDIR,
|
||
|
|
UV_FS_READDIR,
|
||
|
|
UV_FS_CLOSEDIR,
|
||
|
|
UV_FS_MKSTEMP
|
||
|
|
} uv_fs_type;
|
||
|
|
|
||
|
|
typedef struct {
|
||
|
|
long tv_sec;
|
||
|
|
long tv_nsec;
|
||
|
|
} uv_timespec_t;
|
||
|
|
|
||
|
|
struct uv__work {
|
||
|
|
void (*work)(struct uv__work *w);
|
||
|
|
void (*done)(struct uv__work *w, int status);
|
||
|
|
struct uv_loop_s* loop;
|
||
|
|
void* wq[2];
|
||
|
|
};
|
||
|
|
|
||
|
|
typedef struct {
|
||
|
|
uint64_t st_dev;
|
||
|
|
uint64_t st_mode;
|
||
|
|
uint64_t st_nlink;
|
||
|
|
uint64_t st_uid;
|
||
|
|
uint64_t st_gid;
|
||
|
|
uint64_t st_rdev;
|
||
|
|
uint64_t st_ino;
|
||
|
|
uint64_t st_size;
|
||
|
|
uint64_t st_blksize;
|
||
|
|
uint64_t st_blocks;
|
||
|
|
uint64_t st_flags;
|
||
|
|
uint64_t st_gen;
|
||
|
|
uv_timespec_t st_atim;
|
||
|
|
uv_timespec_t st_mtim;
|
||
|
|
uv_timespec_t st_ctim;
|
||
|
|
uv_timespec_t st_birthtim;
|
||
|
|
} uv_stat_t;
|
||
|
|
|
||
|
|
typedef enum {
|
||
|
|
UV_DIRENT_UNKNOWN,
|
||
|
|
UV_DIRENT_FILE,
|
||
|
|
UV_DIRENT_DIR,
|
||
|
|
UV_DIRENT_LINK,
|
||
|
|
UV_DIRENT_FIFO,
|
||
|
|
UV_DIRENT_SOCKET,
|
||
|
|
UV_DIRENT_CHAR,
|
||
|
|
UV_DIRENT_BLOCK
|
||
|
|
} uv_dirent_type_t;
|
||
|
|
|
||
|
|
typedef struct uv_dirent_s {
|
||
|
|
const char* name;
|
||
|
|
uv_dirent_type_t type;
|
||
|
|
} uv_dirent_t;
|
||
|
|
|
||
|
|
typedef struct uv_statfs_s {
|
||
|
|
uint64_t f_type;
|
||
|
|
uint64_t f_bsize;
|
||
|
|
uint64_t f_blocks;
|
||
|
|
uint64_t f_bfree;
|
||
|
|
uint64_t f_bavail;
|
||
|
|
uint64_t f_files;
|
||
|
|
uint64_t f_ffree;
|
||
|
|
uint64_t f_spare[4];
|
||
|
|
} uv_statfs_t;
|
||
|
|
|
||
|
|
typedef struct uv_dir_s {
|
||
|
|
uv_dirent_t* dirents;
|
||
|
|
size_t nentries;
|
||
|
|
} uv_dir_t;
|
||
|
|
|
||
|
|
typedef struct uv_fs_s uv_fs_t;
|
||
|
|
typedef void (*uv_fs_cb)(uv_fs_t* req);
|
||
|
|
|
||
|
|
/* uv_fs_t is a subclass of uv_req_t. */
|
||
|
|
typedef struct uv_fs_s {
|
||
|
|
void* data;
|
||
|
|
uv_req_type type;
|
||
|
|
void* reserved[6];
|
||
|
|
void* data;
|
||
|
|
uv_req_type type;
|
||
|
|
void* reserved[6];
|
||
|
|
uv_loop_t* loop;
|
||
|
|
uv_fs_cb cb;
|
||
|
|
size_t result;
|
||
|
|
void* ptr;
|
||
|
|
const char* path;
|
||
|
|
uv_stat_t statbuf; /* Stores the result of uv_fs_stat() and uv_fs_fstat(). */
|
||
|
|
struct uv__work work_req;
|
||
|
|
int flags;
|
||
|
|
DWORD sys_errno_;
|
||
|
|
union {
|
||
|
|
uint16_t* pathw;
|
||
|
|
int fd;
|
||
|
|
} file;
|
||
|
|
union {
|
||
|
|
struct {
|
||
|
|
int mode;
|
||
|
|
uint16_t* new_pathw;
|
||
|
|
int file_flags;
|
||
|
|
int fd_out;
|
||
|
|
unsigned int nbufs;
|
||
|
|
uv_buf_t* bufs;
|
||
|
|
int64_t offset;
|
||
|
|
uv_buf_t bufsml[4];
|
||
|
|
} info;
|
||
|
|
struct {
|
||
|
|
double atime;
|
||
|
|
double mtime;
|
||
|
|
} time;
|
||
|
|
} fs;
|
||
|
|
};
|
||
|
|
|
||
|
|
int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb);
|
||
|
|
const char* uv_fs_get_path(const uv_fs_t* req);
|
||
|
|
|
||
|
|
int uv_exepath(char* buffer, size_t* size);
|
||
|
|
int uv_cwd(char* buffer, size_t* size);
|
||
|
|
]]
|
||
|
|
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
-- Thread
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
local Thread = {}
|
||
|
|
|
||
|
|
|
||
|
|
ffi.cdef [[
|
||
|
|
|
||
|
|
typedef uint32_t HANDLE;
|
||
|
|
|
||
|
|
typedef HANDLE uv_thread_t;
|
||
|
|
typedef void (*uv_thread_cb)(void* arg);
|
||
|
|
|
||
|
|
int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg);
|
||
|
|
|
||
|
|
typedef enum {
|
||
|
|
UV_THREAD_NO_FLAGS = 0x00,
|
||
|
|
UV_THREAD_HAS_STACK_SIZE = 0x01
|
||
|
|
} uv_thread_create_flags;
|
||
|
|
|
||
|
|
struct uv_thread_options_s {
|
||
|
|
unsigned int flags;
|
||
|
|
size_t stack_size;
|
||
|
|
/* More fields may be added at any time. */
|
||
|
|
};
|
||
|
|
|
||
|
|
typedef struct uv_thread_options_s uv_thread_options_t;
|
||
|
|
|
||
|
|
int uv_thread_create_ex(uv_thread_t* tid, const uv_thread_options_t* params,
|
||
|
|
uv_thread_cb entry, void* arg);
|
||
|
|
uv_thread_t uv_thread_self(void);
|
||
|
|
int uv_thread_join(uv_thread_t *tid);
|
||
|
|
int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2);
|
||
|
|
]]
|
||
|
|
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
-- Req
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
ffi.cdef [[
|
||
|
|
int uv_cancel(uv_req_t* req);
|
||
|
|
uv_req_type uv_req_get_type(const uv_req_t* req);
|
||
|
|
const char* uv_req_type_name(uv_req_type type);
|
||
|
|
]]
|
||
|
|
|
||
|
|
local Req = {}
|
||
|
|
|
||
|
|
function Req:cancel()
|
||
|
|
return uvCheck(UV.uv_cancel(cast('uv_req_t*', self)))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Req:getType()
|
||
|
|
local id = UV.uv_req_get_type(cast('uv_req_t*', self))
|
||
|
|
return ffi.string(UV.uv_req_type_name(id))
|
||
|
|
end
|
||
|
|
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
-- Connect
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
local Connect = setmetatable({}, {__index = Req})
|
||
|
|
Connect.type = ffi.typeof 'uv_connect_t'
|
||
|
|
ffi.metatype(Connect.type, {__index = Connect})
|
||
|
|
function Connect.new()
|
||
|
|
return Connect.type()
|
||
|
|
end
|
||
|
|
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
-- Write
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
local Write = setmetatable({}, {__index = Req})
|
||
|
|
Write.type = ffi.typeof 'uv_write_t'
|
||
|
|
ffi.metatype(Write.type, {__index = Write})
|
||
|
|
function Write.new()
|
||
|
|
return Write.type()
|
||
|
|
end
|
||
|
|
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
-- Shutdown
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
local Shutdown = setmetatable({}, {__index = Req})
|
||
|
|
Shutdown.type = ffi.typeof 'uv_shutdown_t'
|
||
|
|
ffi.metatype(Shutdown.type, {__index = Shutdown})
|
||
|
|
function Shutdown.new()
|
||
|
|
return Shutdown.type()
|
||
|
|
end
|
||
|
|
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
-- Getaddrinfo
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
ffi.cdef [[
|
||
|
|
enum {
|
||
|
|
AF_UNSPEC = 0,
|
||
|
|
AF_INET = 2,
|
||
|
|
AF_INET6 = 10
|
||
|
|
};
|
||
|
|
enum {
|
||
|
|
SOCK_STREAM = 1,
|
||
|
|
SOCK_DGRAM = 2
|
||
|
|
};
|
||
|
|
|
||
|
|
typedef int32_t socklen_t;
|
||
|
|
|
||
|
|
struct addrinfo {
|
||
|
|
int ai_flags;
|
||
|
|
int ai_family;
|
||
|
|
int ai_socktype;
|
||
|
|
int ai_protocol;
|
||
|
|
socklen_t ai_addrlen;
|
||
|
|
struct sockaddr *ai_addr;
|
||
|
|
char *ai_canonname;
|
||
|
|
struct addrinfo *ai_next;
|
||
|
|
};
|
||
|
|
|
||
|
|
typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* req, int status, struct addrinfo* res);
|
||
|
|
|
||
|
|
int uv_getaddrinfo(
|
||
|
|
uv_loop_t* loop, uv_getaddrinfo_t* req, uv_getaddrinfo_cb getaddrinfo_cb,
|
||
|
|
const char* node, const char* service, const struct addrinfo* hints
|
||
|
|
);
|
||
|
|
void uv_freeaddrinfo(struct addrinfo* ai);
|
||
|
|
]]
|
||
|
|
|
||
|
|
local Getaddrinfo = setmetatable({}, {__index = Req})
|
||
|
|
Getaddrinfo.type = ffi.typeof 'uv_getaddrinfo_t'
|
||
|
|
ffi.metatype(Getaddrinfo.type, {__index = Getaddrinfo})
|
||
|
|
function Getaddrinfo.new()
|
||
|
|
return Getaddrinfo.type()
|
||
|
|
end
|
||
|
|
|
||
|
|
local families = {
|
||
|
|
inet = UV.AF_INET,
|
||
|
|
inet6 = UV.AF_INET6,
|
||
|
|
[UV.AF_INET] = 'inet',
|
||
|
|
[UV.AF_INET6] = 'inet6'
|
||
|
|
}
|
||
|
|
|
||
|
|
local socktypes = {
|
||
|
|
stream = UV.SOCK_STREAM,
|
||
|
|
dgram = UV.SOCK_DGRAM,
|
||
|
|
[UV.SOCK_STREAM] = 'stream',
|
||
|
|
[UV.SOCK_DGRAM] = 'dgram'
|
||
|
|
}
|
||
|
|
|
||
|
|
function Loop:getaddrinfo(node, service, hints)
|
||
|
|
local req = Getaddrinfo.new()
|
||
|
|
p(req)
|
||
|
|
local opts = ffi.new('struct addrinfo')
|
||
|
|
if type(service) == 'number' then
|
||
|
|
service = tostring(service)
|
||
|
|
end
|
||
|
|
if hints then
|
||
|
|
if hints.family then
|
||
|
|
opts.ai_family = assert(families[hints.family], 'Unknown family name')
|
||
|
|
end
|
||
|
|
if hints.socktype then
|
||
|
|
opts.ai_socktype = assert(socktypes[hints.socktype], 'Unknown socktype name')
|
||
|
|
end
|
||
|
|
end
|
||
|
|
p(node, service, hints)
|
||
|
|
uvCheck(UV.uv_getaddrinfo(self, req, makeCallback 'uv_getaddrinfo_cb', node, service, opts))
|
||
|
|
local _, status, res = coroutine.yield()
|
||
|
|
uvCheck(status)
|
||
|
|
local results = {}
|
||
|
|
local i = 1
|
||
|
|
while res ~= nil do
|
||
|
|
local family = families[res.ai_family]
|
||
|
|
local socktype = socktypes[res.ai_socktype]
|
||
|
|
local entry = {
|
||
|
|
family = family,
|
||
|
|
socktype = socktype
|
||
|
|
}
|
||
|
|
results[i] = entry
|
||
|
|
i = i + 1
|
||
|
|
if family == 'inet' then
|
||
|
|
local buf = ffi.new 'char[16]'
|
||
|
|
local addr = cast('const struct sockaddr_in*', res.ai_addr)
|
||
|
|
uvCheck(UV.uv_ip4_name(addr, buf, 16))
|
||
|
|
entry.addr = ffi.string(buf)
|
||
|
|
entry.port = C.ntohs(addr.sin_port)
|
||
|
|
elseif family == 'inet6' then
|
||
|
|
local buf = ffi.new 'char[46]'
|
||
|
|
local addr = cast('const struct sockaddr_in6*', res.ai_addr)
|
||
|
|
uvCheck(UV.uv_ip6_name(addr, buf, 46))
|
||
|
|
entry.addr = ffi.string(buf)
|
||
|
|
entry.port = C.ntohs(addr.sin6_port)
|
||
|
|
end
|
||
|
|
res = res.ai_next
|
||
|
|
end
|
||
|
|
return results
|
||
|
|
end
|
||
|
|
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
-- Handle
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
ffi.cdef [[
|
||
|
|
typedef void (*uv_walk_cb)(uv_handle_t* handle, void* arg);
|
||
|
|
typedef void (*uv_close_cb)(uv_handle_t* handle);
|
||
|
|
typedef void (*uv_alloc_cb)(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf);
|
||
|
|
|
||
|
|
struct sockaddr {
|
||
|
|
unsigned short sa_family;
|
||
|
|
char sa_data[14];
|
||
|
|
};
|
||
|
|
|
||
|
|
struct in_addr {
|
||
|
|
unsigned long s_addr;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct sockaddr_in {
|
||
|
|
short sin_family;
|
||
|
|
unsigned short sin_port;
|
||
|
|
struct in_addr sin_addr;
|
||
|
|
char sin_zero[8];
|
||
|
|
};
|
||
|
|
|
||
|
|
struct in6_addr {
|
||
|
|
unsigned char s6_addr[16];
|
||
|
|
};
|
||
|
|
|
||
|
|
struct sockaddr_in6 {
|
||
|
|
uint16_t sin6_family;
|
||
|
|
uint16_t sin6_port;
|
||
|
|
uint32_t sin6_flowinfo;
|
||
|
|
struct in6_addr sin6_addr;
|
||
|
|
uint32_t sin6_scope_id;
|
||
|
|
};
|
||
|
|
|
||
|
|
typedef uint16_t sa_family_t;
|
||
|
|
|
||
|
|
struct sockaddr_storage {
|
||
|
|
sa_family_t ss_family;
|
||
|
|
char __ss_pad1[6];
|
||
|
|
int64_t __ss_align;
|
||
|
|
char __ss_pad2[112];
|
||
|
|
};
|
||
|
|
|
||
|
|
uint32_t htonl(uint32_t hostlong);
|
||
|
|
uint16_t htons(uint16_t hostshort);
|
||
|
|
uint32_t ntohl(uint32_t netlong);
|
||
|
|
uint16_t ntohs(uint16_t netshort);
|
||
|
|
|
||
|
|
void *malloc(size_t size);
|
||
|
|
void free(void *ptr);
|
||
|
|
|
||
|
|
typedef int uv_file;
|
||
|
|
|
||
|
|
uv_handle_type uv_guess_handle(uv_file file);
|
||
|
|
|
||
|
|
int uv_is_active(const uv_handle_t* handle);
|
||
|
|
int uv_is_closing(const uv_handle_t* handle);
|
||
|
|
void uv_close(uv_handle_t* handle, uv_close_cb close_cb);
|
||
|
|
void uv_ref(uv_handle_t* handle);
|
||
|
|
void uv_unref(uv_handle_t* handle);
|
||
|
|
int uv_has_ref(const uv_handle_t* handle);
|
||
|
|
int uv_send_buffer_size(uv_handle_t* handle, int* value);
|
||
|
|
int uv_recv_buffer_size(uv_handle_t* handle, int* value);
|
||
|
|
uv_loop_t* uv_handle_get_loop(const uv_handle_t* handle);
|
||
|
|
void* uv_handle_get_data(const uv_handle_t* handle);
|
||
|
|
void* uv_handle_set_data(uv_handle_t* handle, void* data);
|
||
|
|
uv_handle_type uv_handle_get_type(const uv_handle_t* handle);
|
||
|
|
const char* uv_handle_type_name(uv_handle_type type);
|
||
|
|
]]
|
||
|
|
|
||
|
|
local function onAlloc(handle, suggestedSize, buf)
|
||
|
|
local cached = cast('uv_buf_t*', UV.uv_handle_get_data(handle))
|
||
|
|
if cached ~= nil then
|
||
|
|
buf.base = cached.base
|
||
|
|
buf.len = cached.len
|
||
|
|
else
|
||
|
|
local base = C.malloc(suggestedSize)
|
||
|
|
buf.base = base
|
||
|
|
buf.len = suggestedSize
|
||
|
|
-- Store the data in handle->data as a uv_buf_t*
|
||
|
|
local data = cast('uv_buf_t*', C.malloc(ffi.sizeof 'uv_buf_t'))
|
||
|
|
data.base = base
|
||
|
|
data.len = suggestedSize
|
||
|
|
UV.uv_handle_set_data(handle, data)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local allocCb = cast('uv_alloc_cb', onAlloc)
|
||
|
|
|
||
|
|
function Loop.guessHandle(fd)
|
||
|
|
return ffi.string(UV.uv_handle_type_name(UV.uv_guess_handle(fd)))
|
||
|
|
end
|
||
|
|
|
||
|
|
local Handle = {}
|
||
|
|
|
||
|
|
function Handle:isActive()
|
||
|
|
return UV.uv_is_active(cast('uv_handle_t*', self)) ~= 0
|
||
|
|
end
|
||
|
|
|
||
|
|
function Handle:isClosing()
|
||
|
|
return UV.uv_is_closing(cast('uv_handle_t*', self)) ~= 0
|
||
|
|
end
|
||
|
|
|
||
|
|
function Handle:close()
|
||
|
|
local handle = cast('uv_handle_t*', self)
|
||
|
|
local _, main = coroutine.running()
|
||
|
|
if main then
|
||
|
|
UV.uv_close(handle, nil)
|
||
|
|
else
|
||
|
|
UV.uv_close(handle, makeCallback 'uv_close_cb')
|
||
|
|
coroutine.yield()
|
||
|
|
end
|
||
|
|
local cached = cast('uv_buf_t*', UV.uv_handle_get_data(handle))
|
||
|
|
if cached ~= nil then
|
||
|
|
C.free(cached.base)
|
||
|
|
C.free(cached)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function Handle:ref()
|
||
|
|
return UV.uv_ref(cast('uv_handle_t*', self))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Handle:unref()
|
||
|
|
return UV.uv_unref(cast('uv_handle_t*', self))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Handle:hasRef()
|
||
|
|
return UV.uv_has_ref(cast('uv_handle_t*', self)) ~= 0
|
||
|
|
end
|
||
|
|
|
||
|
|
function Handle:setSendBufferSize(value)
|
||
|
|
uvCheck(UV.uv_send_buffer_size(cast('uv_handle_t*', self), value))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Handle:getSendBufferSize()
|
||
|
|
local out = ffi.new('int[1]')
|
||
|
|
uvCheck(UV.uv_send_buffer_size(cast('uv_handle_t*', self), out))
|
||
|
|
return out[0]
|
||
|
|
end
|
||
|
|
|
||
|
|
function Handle:setRecvBufferSize(value)
|
||
|
|
uvCheck(UV.uv_recv_buffer_size(cast('uv_handle_t*', self), value))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Handle:getRecvBufferSize()
|
||
|
|
local out = ffi.new('int[1]')
|
||
|
|
uvCheck(UV.uv_recv_buffer_size(cast('uv_handle_t*', self), out))
|
||
|
|
return out[0]
|
||
|
|
end
|
||
|
|
|
||
|
|
function Handle:getLoop()
|
||
|
|
return UV.uv_handle_get_loop(cast('uv_handle_t*', self))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Handle:getType()
|
||
|
|
local id = UV.uv_handle_get_type(cast('uv_handle_t*', self))
|
||
|
|
return ffi.string(UV.uv_handle_type_name(id))
|
||
|
|
end
|
||
|
|
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
-- Stream
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
ffi.cdef [[
|
||
|
|
|
||
|
|
typedef struct sws_handle_req_t{
|
||
|
|
|
||
|
|
struct uv_tcp_s handle;
|
||
|
|
int id;
|
||
|
|
struct sws_parse_req_t parse_req;
|
||
|
|
} sws_handle_req_t;
|
||
|
|
|
||
|
|
typedef void (*uv_read_cb)(uv_stream_t* stream, int64_t nread, const uv_buf_t* buf);
|
||
|
|
typedef void (*uv_write_cb)(uv_write_t* req, int status);
|
||
|
|
typedef void (*uv_connect_cb)(uv_connect_t* req, int status);
|
||
|
|
typedef void (*uv_shutdown_cb)(uv_shutdown_t* req, int status);
|
||
|
|
typedef void (*uv_connection_cb)(uv_stream_t* server, int status);
|
||
|
|
|
||
|
|
int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb);
|
||
|
|
int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb);
|
||
|
|
int uv_accept(uv_stream_t* server, uv_stream_t* client);
|
||
|
|
int uv_read_start(uv_stream_t* stream, uv_alloc_cb alloc_cb, uv_read_cb read_cb);
|
||
|
|
int uv_read_stop(uv_stream_t*);
|
||
|
|
int uv_write(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb);
|
||
|
|
int uv_write2(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs,
|
||
|
|
uv_stream_t* send_handle, uv_write_cb cb);
|
||
|
|
int uv_try_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs);
|
||
|
|
int uv_is_readable(const uv_stream_t* handle);
|
||
|
|
int uv_is_writable(const uv_stream_t* handle);
|
||
|
|
int uv_stream_set_blocking(uv_stream_t* handle, int blocking);
|
||
|
|
size_t uv_stream_get_write_queue_size(const uv_stream_t* stream);
|
||
|
|
]]
|
||
|
|
|
||
|
|
local Stream = setmetatable({}, {__index = Handle})
|
||
|
|
|
||
|
|
function Stream:shutdown()
|
||
|
|
local req = Shutdown.new()
|
||
|
|
uvCheck(UV.uv_shutdown(req, cast('uv_stream_t*', self), makeCallback 'uv_shutdown_cb'))
|
||
|
|
local _, status = coroutine.yield()
|
||
|
|
uvCheck(status)
|
||
|
|
end
|
||
|
|
|
||
|
|
function Stream:listen(backlog, onConnection)
|
||
|
|
local cb = cast('uv_connection_cb', onConnection)
|
||
|
|
uvCheck(UV.uv_listen(cast('uv_stream_t*', self), backlog, cb))
|
||
|
|
return cb
|
||
|
|
end
|
||
|
|
|
||
|
|
function Stream:accept(client)
|
||
|
|
uvCheck(UV.uv_accept(cast('uv_stream_t*', self), cast('uv_stream_t*', client)))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Stream:readStart(onRead)
|
||
|
|
local function onEvent(_, status, buf)
|
||
|
|
print('onRead', _, status, buf)
|
||
|
|
if status == 0 then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
if status == UV.UV_EOF then
|
||
|
|
onRead(nil)
|
||
|
|
elseif status < 0 then
|
||
|
|
return onRead(uvGetError(status))
|
||
|
|
else
|
||
|
|
onRead(nil, ffi.string(buf.base, status))
|
||
|
|
end
|
||
|
|
end
|
||
|
|
local cb = cast('uv_read_cb', onEvent)
|
||
|
|
uvCheck(UV.uv_read_start(cast('uv_stream_t*', self), allocCb, cb))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Stream:readStop()
|
||
|
|
uvCheck(UV.uv_read_stop(cast('uv_stream_t*', self)))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Stream:write(data)
|
||
|
|
local req = Write.new()
|
||
|
|
local bufs = ffi.new('uv_buf_t[1]')
|
||
|
|
bufs[0].base = cast('char*', data)
|
||
|
|
bufs[0].len = #data
|
||
|
|
local cb = makeCallback 'uv_write_cb'
|
||
|
|
uvCheck(UV.uv_write(req, cast('uv_stream_t*', self), bufs, 1, cb))
|
||
|
|
if cb then
|
||
|
|
local _, status = coroutine.yield()
|
||
|
|
uvCheck(status)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function Stream:write2(handle)
|
||
|
|
local req = Write.new()
|
||
|
|
local cb = makeCallback 'uv_write_cb'
|
||
|
|
uvCheck(UV.uv_write(req, cast('uv_stream_t*', self), nil, 0, cast('uv_stream_t*', handle), cb))
|
||
|
|
local _, status = coroutine.yield()
|
||
|
|
uvCheck(status)
|
||
|
|
end
|
||
|
|
|
||
|
|
function Stream:tryWrite(data)
|
||
|
|
local bufs = ffi.new('uv_buf_t[1]')
|
||
|
|
bufs[0].base = cast('char*', data)
|
||
|
|
bufs[0].len = #data
|
||
|
|
return uvCheck(UV.uv_try_write(cast('uv_stream_t*', self), bufs, 1))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Stream:isReadable()
|
||
|
|
return UV.uv_is_readable(cast('uv_stream_t*', self)) ~= 0
|
||
|
|
end
|
||
|
|
|
||
|
|
function Stream:isWritable()
|
||
|
|
return UV.uv_is_writable(cast('uv_stream_t*', self)) ~= 0
|
||
|
|
end
|
||
|
|
|
||
|
|
function Stream:setBlocking(blocking)
|
||
|
|
uvCheck(UV.uv_set_blocking(cast('uv_stream_t*', self), blocking))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Stream:getWriteQueueSize()
|
||
|
|
return tonumber(UV.uv_stream_get_write_queue_size(cast('uv_stream_t*', self)))
|
||
|
|
end
|
||
|
|
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
-- Tcp
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
ffi.cdef [[
|
||
|
|
int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* handle);
|
||
|
|
int uv_tcp_bind(uv_tcp_t* handle, const struct sockaddr* addr, unsigned int flags);
|
||
|
|
int uv_tcp_connect(uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr_in* addr, uv_connect_cb cb);
|
||
|
|
]]
|
||
|
|
|
||
|
|
local Tcp = setmetatable({}, {__index = Stream})
|
||
|
|
Tcp.type = ffi.typeof 'uv_tcp_t'
|
||
|
|
|
||
|
|
function Loop:newTcp()
|
||
|
|
local tcp = Tcp.type()
|
||
|
|
uvCheck(UV.uv_tcp_init(self, tcp))
|
||
|
|
return tcp
|
||
|
|
end
|
||
|
|
|
||
|
|
function Tcp:getsockname()
|
||
|
|
-- TODO: Improve output
|
||
|
|
return UV.uv_tcp_getsockname(self)
|
||
|
|
end
|
||
|
|
|
||
|
|
function Tcp:getpeername()
|
||
|
|
-- TODO: Improve output
|
||
|
|
return UV.uv_tcp_getpeername(self)
|
||
|
|
end
|
||
|
|
|
||
|
|
function Tcp:connect(host, port)
|
||
|
|
local req = Connect.new()
|
||
|
|
local addr = ffi.new("struct sockaddr_in [1]")
|
||
|
|
UV.uv_ip4_addr(host, port, cast("struct sockaddr_in *", addr))
|
||
|
|
uvCheck(UV.uv_tcp_connect(req, self, addr, makeCallback 'uv_connect_cb'))
|
||
|
|
local _, status = coroutine.yield()
|
||
|
|
uvCheck(status)
|
||
|
|
end
|
||
|
|
|
||
|
|
ffi.metatype(Tcp.type, {__index = Tcp})
|
||
|
|
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
-- Tty
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
ffi.cdef [[
|
||
|
|
typedef enum {
|
||
|
|
/* Initial/normal terminal mode */
|
||
|
|
UV_TTY_MODE_NORMAL,
|
||
|
|
/* Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled) */
|
||
|
|
UV_TTY_MODE_RAW,
|
||
|
|
/* Binary-safe I/O mode for IPC (Unix-only) */
|
||
|
|
UV_TTY_MODE_IO
|
||
|
|
} uv_tty_mode_t;
|
||
|
|
|
||
|
|
int uv_tty_init(uv_loop_t* loop, uv_tty_t* handle, uv_file fd, int unused);
|
||
|
|
int uv_tty_set_mode(uv_tty_t* handle, uv_tty_mode_t mode);
|
||
|
|
int uv_tty_reset_mode(void);
|
||
|
|
int uv_tty_get_winsize(uv_tty_t* handle, int* width, int* height);
|
||
|
|
]]
|
||
|
|
|
||
|
|
local Tty = setmetatable({}, {__index = Stream})
|
||
|
|
Tty.type = ffi.typeof 'uv_tty_t'
|
||
|
|
|
||
|
|
function Loop:newTty(fd)
|
||
|
|
local tty = Tty.type()
|
||
|
|
uvCheck(UV.uv_tty_init(self, tty, fd, 0))
|
||
|
|
return tty
|
||
|
|
end
|
||
|
|
|
||
|
|
function Tty:setMode(mode)
|
||
|
|
uvCheck(
|
||
|
|
UV.uv_tty_set_mode(self, assert(UV['UV_TTY_MODE_' .. string.upper(mode)], 'Unknown tty mode'))
|
||
|
|
)
|
||
|
|
end
|
||
|
|
|
||
|
|
function Tty:resetMode()
|
||
|
|
uvCheck(UV.uv_tty_reset_mode())
|
||
|
|
end
|
||
|
|
|
||
|
|
function Tty:getWinsize()
|
||
|
|
local width = ffi.new('int[1]')
|
||
|
|
local height = ffi.new('int[1]')
|
||
|
|
uvCheck(UV.uv_tty_get_winsize(self, width, height))
|
||
|
|
return tonumber(width[0]), tonumber(height[0])
|
||
|
|
end
|
||
|
|
|
||
|
|
ffi.metatype(Tty.type, {__index = Tty})
|
||
|
|
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
-- Pipe
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
ffi.cdef [[
|
||
|
|
int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc);
|
||
|
|
int uv_pipe_open(uv_pipe_t* handle, uv_file file);
|
||
|
|
int uv_pipe_bind(uv_pipe_t* handle, const char* name);
|
||
|
|
void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, const char* name, uv_connect_cb cb);
|
||
|
|
int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size);
|
||
|
|
int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size);
|
||
|
|
void uv_pipe_pending_instances(uv_pipe_t* handle, int count);
|
||
|
|
int uv_pipe_pending_count(uv_pipe_t* handle);
|
||
|
|
uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle);
|
||
|
|
int uv_pipe_chmod(uv_pipe_t* handle, int flags);
|
||
|
|
]]
|
||
|
|
|
||
|
|
local Pipe = setmetatable({}, {__index = Stream})
|
||
|
|
Pipe.type = ffi.typeof 'uv_pipe_t'
|
||
|
|
|
||
|
|
function Loop:newPipe(ipc)
|
||
|
|
local pipe = Pipe.type()
|
||
|
|
uvCheck(UV.uv_pipe_init(self, pipe, ipc and 1 or 0))
|
||
|
|
return pipe
|
||
|
|
end
|
||
|
|
|
||
|
|
function Pipe:open(fd)
|
||
|
|
uvCheck(UV.uv_pipe_open(self, fd))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Pipe:bind(path)
|
||
|
|
uvCheck(UV.uv_pipe_bind(self, path))
|
||
|
|
end
|
||
|
|
|
||
|
|
-- TODO: add more pipe functions
|
||
|
|
|
||
|
|
ffi.metatype(Pipe.type, {__index = Pipe})
|
||
|
|
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
-- Timer
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
ffi.cdef [[
|
||
|
|
typedef void (*uv_timer_cb)(uv_timer_t* handle);
|
||
|
|
|
||
|
|
int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle);
|
||
|
|
int uv_timer_start(uv_timer_t* handle, uv_timer_cb cb, uint64_t timeout, uint64_t repeat);
|
||
|
|
int uv_timer_stop(uv_timer_t* handle);
|
||
|
|
int uv_timer_again(uv_timer_t* handle);
|
||
|
|
void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat);
|
||
|
|
uint64_t uv_timer_get_repeat(const uv_timer_t* handle);
|
||
|
|
]]
|
||
|
|
|
||
|
|
local Timer = setmetatable({}, {__index = Handle})
|
||
|
|
Timer.Type = ffi.typeof 'uv_timer_t'
|
||
|
|
|
||
|
|
function Loop:newTimer()
|
||
|
|
local timer = Timer.Type()
|
||
|
|
uvCheck(UV.uv_timer_init(self, timer))
|
||
|
|
return timer
|
||
|
|
end
|
||
|
|
|
||
|
|
function Timer:sleep(timeout)
|
||
|
|
uvCheck(UV.uv_timer_start(self, makeCallback 'uv_timer_cb', timeout, 0))
|
||
|
|
coroutine.yield()
|
||
|
|
uvCheck(UV.uv_timer_stop(self))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Timer:start(callback, timeout, rep)
|
||
|
|
local cb = cast('uv_timer_cb', timeout)
|
||
|
|
uvCheck(UV.uv_timer_start(self, callback, cb, rep))
|
||
|
|
return cb
|
||
|
|
end
|
||
|
|
|
||
|
|
function Timer:stop()
|
||
|
|
uvCheck(UV.uv_timer_stop(self))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Timer:again()
|
||
|
|
uvCheck(UV.uv_timer_again(self))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Timer:setRepeat(rep)
|
||
|
|
uvCheck(UV.uv_timer_set_repeat(self, rep))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Timer:getRepeat()
|
||
|
|
return UV.uv_timer_get_repeat(self)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function onGc(handle)
|
||
|
|
if not handle:isClosing() then
|
||
|
|
-- We can't safely close handles here because onClose happens after the
|
||
|
|
-- struct is freed by lua.
|
||
|
|
-- Instead abort the process so the programmer can fix it early.
|
||
|
|
handle = cast('uv_' .. Handle.getType(handle) .. '_t*', handle)
|
||
|
|
error('Unclosed ' .. tostring(handle) .. ' got garbage collected')
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
ffi.metatype(Timer.Type, {__index = Timer, __gc = onGc})
|
||
|
|
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
-- Loop
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
ffi.cdef [[
|
||
|
|
uv_loop_t* uv_default_loop();
|
||
|
|
int uv_loop_init(uv_loop_t* loop);
|
||
|
|
int uv_loop_close(uv_loop_t* loop);
|
||
|
|
int uv_loop_alive(const uv_loop_t* loop);
|
||
|
|
void uv_stop(uv_loop_t* loop);
|
||
|
|
uint64_t uv_now(const uv_loop_t* loop);
|
||
|
|
void uv_update_time(uv_loop_t* loop);
|
||
|
|
void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg);
|
||
|
|
int uv_run(uv_loop_t* loop, uv_run_mode mode);
|
||
|
|
]]
|
||
|
|
|
||
|
|
local LoopType = ffi.typeof 'uv_loop_t'
|
||
|
|
|
||
|
|
function Loop.new()
|
||
|
|
local loop = LoopType()
|
||
|
|
uvCheck(UV.uv_loop_init(loop))
|
||
|
|
return loop
|
||
|
|
end
|
||
|
|
|
||
|
|
function Loop:close()
|
||
|
|
uvCheck(UV.uv_loop_close(self))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Loop:alive()
|
||
|
|
return UV.uv_loop_alive(self) ~= 0
|
||
|
|
end
|
||
|
|
|
||
|
|
function Loop:stop()
|
||
|
|
return UV.uv_loop_stop(self)
|
||
|
|
end
|
||
|
|
|
||
|
|
function Loop:now()
|
||
|
|
return UV.uv_loop_now(self)
|
||
|
|
end
|
||
|
|
|
||
|
|
function Loop:updateTime()
|
||
|
|
return UV.uv_update_time(self)
|
||
|
|
end
|
||
|
|
|
||
|
|
function Loop:walk(callback)
|
||
|
|
local function onHandle(handle)
|
||
|
|
callback(cast('uv_' .. Handle.getType(handle) .. '_t*', handle))
|
||
|
|
end
|
||
|
|
local cb = cast('uv_walk_cb', onHandle)
|
||
|
|
UV.uv_walk(self, cb, nil)
|
||
|
|
cb:free()
|
||
|
|
end
|
||
|
|
|
||
|
|
function Loop:run(mode)
|
||
|
|
mode = assert(UV['UV_RUN_' .. string.upper(mode)], 'Unknown run mode')
|
||
|
|
return tonumber(uvCheck(UV.uv_run(self, mode)))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Loop:bind(server, host, port)
|
||
|
|
local addr = ffi.new("struct sockaddr_in [1]")
|
||
|
|
local res = UV.uv_ip4_addr(host, port, cast("struct sockaddr_in *", addr))
|
||
|
|
uvCheck(UV.uv_tcp_bind(server, cast("struct sockaddr *", addr), 0 ))
|
||
|
|
end
|
||
|
|
|
||
|
|
function Loop:listen(tcp, backlog, onConnect)
|
||
|
|
tcp:listen( backlog, onConnect )
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
ffi.metatype(LoopType, {__index = Loop})
|
||
|
|
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
Loop.Req = Req
|
||
|
|
Loop.Connect = Connect
|
||
|
|
Loop.Write = Write
|
||
|
|
Loop.Shutdown = Shutdown
|
||
|
|
Loop.Handle = Handle
|
||
|
|
Loop.Stream = Stream
|
||
|
|
Loop.Tcp = Tcp
|
||
|
|
Loop.Timer = Timer
|
||
|
|
Loop.uv = UV
|
||
|
|
|
||
|
|
return UV.uv_default_loop()
|