Selaa lähdekoodia

Z991239-342 #comment 添加 expat 工程的 .ignore 文件

gifur 5 vuotta sitten
vanhempi
sitoutus
4f51033d59

+ 10 - 5
libtoolkit/CMakeLists.txt

@@ -17,11 +17,15 @@ file(GLOB ${MODULE_PREFIX}_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cpp" "*.
 # list(REMOVE_ITEM ${MODULE_PREFIX}_SRCS 
  #   )
 if(NOT WIN32)
-list(REMOVE_ITEM ${MODULE_PREFIX}_SRCS 
-    dllmain.c
-    gettimeofday.c shm.h shm.c
-    wavfile.c wavfile.h
-    )
+    list(REMOVE_ITEM ${MODULE_PREFIX}_SRCS 
+            dllmain.c
+            gettimeofday.c shm.h shm.c
+            wavfile.c wavfile.h
+            bus-win.c bus_daemon-win.c ioqueue-win.c
+        )
+else()
+    list(REMOVE_ITEM ${MODULE_PREFIX}_SRCS 
+        bus-unix.c bus_daemon-unix.c ioqueuq-unix.c)
 endif()
 #foreach(item ${${MODULE_PREFIX}_SRCS})
 #    message(STATUS ${item})
@@ -49,6 +53,7 @@ if(MSVC)
     dbghelp 
     ws2_32
     Winmm
+    MsWSock # ioqueue
     winpr)
 
 else(MSVC)

+ 1158 - 0
libtoolkit/bus-unix.c

@@ -0,0 +1,1158 @@
+#include "precompile.h"
+#include "bus.h"
+#include "sockutil.h"
+#include "url.h"
+#include "memutil.h"
+#include "spinlock.h"
+#include "list.h"
+#include "bus_internal.h"
+
+#include <winpr/file.h>
+#include <winpr/pipe.h>
+#include <winpr/synch.h>
+#include <winpr/string.h>
+
+#define TAG TOOLKIT_TAG("bus_unix")
+
+#define BUS_RESULT_DATA		    1				// ==BUS_TYPE_PACKET, callback: callback.on_pkt,  no use
+#define BUS_RESULT_INFO	    	    2				// ==BUS_TYPE_INFO, callback: callback.on_inf
+#define BUS_RESULT_EVENT  	        3				// ==BUS_TYPE_EVENT, callback: callback.on_evt ,  no use
+#define BUS_RESULT_SYSTEM	    4				// ==BUS_TYPE_SYSTEM, callback: callback.on_sys
+#define BUS_RESULT_MSG		        5				// send package msg, callback: callback.on_msg
+#define BUS_RESULT_UNKNOWN	6
+
+typedef struct msg_t {
+	struct list_head entry;
+	int type;
+	int nparam;
+	int* params;
+	HANDLE evt;
+	int evt_result;
+}msg_t;
+
+struct bus_endpt_t {
+	int type;
+	int epid;
+	union {
+		HANDLE pipe_handle;
+		SOCKET sock_handle;
+	};
+	char* url;
+	bus_endpt_callback callback;
+	struct list_head msg_list;
+	spinlock_t msg_lock;
+	HANDLE msg_sem;
+	HANDLE tx_evt;
+	HANDLE rx_evt;
+	OVERLAPPED rx_overlapped;
+	int rx_pending;
+	int rx_pending_pkt_len;
+	iobuffer_queue_t* rx_buf_queue;
+	volatile int quit_flag;
+};
+
+static void free_msg(msg_t* msg)
+{
+	free(msg->params);
+	free(msg);
+}
+
+static int to_result(int pkt_type)
+{
+	switch (pkt_type) {
+	case BUS_TYPE_EVENT:
+		return BUS_RESULT_EVENT;
+	case BUS_TYPE_SYSTEM:
+		return BUS_RESULT_SYSTEM;
+	case BUS_TYPE_PACKET:
+		return BUS_RESULT_DATA;
+	case BUS_TYPE_INFO:
+		return BUS_RESULT_INFO;
+	default:
+		break;
+	}
+	return BUS_RESULT_UNKNOWN;
+}
+
+static HANDLE create_pipe_handle(const char* name)
+{
+	char tmp[MAX_PATH];
+	HANDLE pipe = INVALID_HANDLE_VALUE;
+
+	sprintf(tmp, "\\\\.\\pipe\\%s", name);
+
+	for (;;) {
+		pipe = CreateFileA(tmp,
+			GENERIC_READ | GENERIC_WRITE,
+			0,
+			NULL,
+			OPEN_EXISTING,
+			FILE_FLAG_OVERLAPPED,
+			NULL);
+		if (pipe == INVALID_HANDLE_VALUE) {
+			if (GetLastError() == ERROR_PIPE_BUSY) {
+				if (WaitNamedPipeA(name, 20000))
+					continue;
+			}
+		}
+		break;
+	}
+
+	return pipe;
+}
+
+static SOCKET create_socket_handle(const char* ip, int port)
+{
+	SOCKET fd;
+	struct sockaddr_in addr;
+
+	fd = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
+
+	if (fd == INVALID_SOCKET)
+		return fd;
+
+	{
+		BOOL f = TRUE;
+		u_long l = TRUE;
+		setsockopt(fd, SOL_SOCKET, SO_DONTLINGER, (char*)&f, sizeof(f));
+		//setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&f, sizeof(f));
+		_ioctlsocket(fd, FIONBIO, &l);
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(port);
+	addr.sin_addr.s_addr = inet_addr(ip);
+
+	if (connect(fd, (struct sockaddr*) & addr, sizeof(addr)) != 0) {
+		if (WSAGetLastError() == WSAEWOULDBLOCK) {
+			fd_set wr_set, ex_set;
+			FD_ZERO(&wr_set);
+			FD_ZERO(&ex_set);
+			FD_SET(fd, &wr_set);
+			FD_SET(fd, &ex_set);
+			if (select(fd, NULL, &wr_set, &ex_set, NULL) > 0 && FD_ISSET(fd, &wr_set))
+				return fd;
+		}
+	}
+
+	if (fd != INVALID_SOCKET)
+		closesocket(fd);
+
+	//return INVALID_SOCKET;//{bug}
+	return  fd;
+}
+
+
+static int tcp_send_buf(bus_endpt_t* endpt, const char* buf, int n)
+{
+	DWORD left = n;
+	DWORD offset = 0;
+
+	while (left > 0) {
+		BOOL ret;
+		WSABUF wsabuf;
+		DWORD dwBytesTransfer;
+		OVERLAPPED overlapped;
+		memset(&overlapped, 0, sizeof(overlapped));
+		overlapped.hEvent = endpt->tx_evt;
+		ResetEvent(endpt->tx_evt);
+		wsabuf.buf = (char*)buf + offset;
+		wsabuf.len = left;
+		ret = WSASend(endpt->sock_handle, &wsabuf, 1, &dwBytesTransfer, 0, &overlapped, NULL) == 0;
+		if (!ret && WSAGetLastError() == WSA_IO_PENDING) {
+			DWORD dwFlags = 0;
+			ret = WSAGetOverlappedResult(endpt->sock_handle, &overlapped, &dwBytesTransfer, TRUE, &dwFlags);
+		}
+		if (ret && dwBytesTransfer) {
+			offset += dwBytesTransfer;
+			left -= dwBytesTransfer;
+		}
+		else {
+			return -1;
+		}
+	}
+
+	return 0;
+
+}
+
+static int pipe_send_buf(bus_endpt_t* endpt, const char* buf, int n)
+{
+	DWORD left = n;
+	DWORD offset = 0;
+
+	while (left > 0) {
+		BOOL ret;
+		DWORD dwBytesTransfer;
+		OVERLAPPED overlapped;
+		memset(&overlapped, 0, sizeof(overlapped));
+		overlapped.hEvent = endpt->tx_evt;
+		ResetEvent(endpt->tx_evt);
+		ret = WriteFile(endpt->pipe_handle, buf + offset, left, &dwBytesTransfer, &overlapped);
+		if (!ret && GetLastError() == ERROR_IO_PENDING) {
+			ret = GetOverlappedResult(endpt->pipe_handle, &overlapped, &dwBytesTransfer, TRUE);
+		}
+		if (ret && dwBytesTransfer) {
+			offset += dwBytesTransfer;
+			left -= dwBytesTransfer;
+		}
+		else {
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int send_buf(bus_endpt_t* endpt, const char* buf, int n)
+{
+	if (endpt->type == TYPE_PIPE) {
+		return pipe_send_buf(endpt, buf, n);
+	}
+	else if (endpt->type == TYPE_TCP) {
+		return tcp_send_buf(endpt, buf, n);
+	}
+	else {
+		return -1;
+	}
+}
+
+static int send_pkt_raw(bus_endpt_t* endpt, iobuffer_t* pkt)
+{
+	int pkt_len = iobuffer_get_length(pkt);
+	int rc;
+
+	iobuffer_write_head(pkt, IOBUF_T_I4, &pkt_len, 0);
+	rc = send_buf(endpt, iobuffer_data(pkt, 0), iobuffer_get_length(pkt));
+
+	return rc;
+}
+
+static int pipe_recv_buf(bus_endpt_t* endpt, char* buf, DWORD n)
+{
+	DWORD left = n;
+	DWORD offset = 0;
+
+	while (left > 0) {
+		BOOL ret;
+		DWORD dwBytesTransfer;
+		OVERLAPPED overlapped;
+		memset(&overlapped, 0, sizeof(overlapped));
+		overlapped.hEvent = endpt->rx_evt;
+		ResetEvent(overlapped.hEvent);
+		ret = ReadFile(endpt->pipe_handle, buf + offset, left, &dwBytesTransfer, &overlapped);
+		if (!ret && GetLastError() == ERROR_IO_PENDING) {
+			ret = GetOverlappedResult(endpt->pipe_handle, &overlapped, &dwBytesTransfer, TRUE);
+		}
+		if (ret && dwBytesTransfer) {
+			offset += dwBytesTransfer;
+			left -= dwBytesTransfer;
+		}
+		else {
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int tcp_recv_buf(bus_endpt_t* endpt, char* buf, DWORD n)
+{
+	DWORD left = n;
+	DWORD offset = 0;
+
+	while (left > 0) {
+		BOOL ret;
+		DWORD dwFlags = 0;
+		WSABUF wsabuf;
+		DWORD dwBytesTransfer;
+		OVERLAPPED overlapped;
+		memset(&overlapped, 0, sizeof(overlapped));
+		overlapped.hEvent = endpt->rx_evt;
+		wsabuf.buf = buf + offset;
+		wsabuf.len = left;
+		ResetEvent(overlapped.hEvent);
+		ret = WSARecv(endpt->sock_handle, &wsabuf, 1, &dwBytesTransfer, &dwFlags, &overlapped, NULL) == 0;
+		if (!ret && WSAGetLastError() == WSA_IO_PENDING) {
+			ret = WSAGetOverlappedResult(endpt->sock_handle, &overlapped, &dwBytesTransfer, TRUE, &dwFlags);
+		}
+		if (ret && dwBytesTransfer) {
+			offset += dwBytesTransfer;
+			left -= dwBytesTransfer;
+		}
+		else {
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int recv_buf(bus_endpt_t* endpt, char* buf, DWORD n)
+{
+	if (endpt->type == TYPE_PIPE) {
+		return pipe_recv_buf(endpt, buf, n);
+	}
+	else if (endpt->type == TYPE_TCP) {
+		return tcp_recv_buf(endpt, buf, n);
+	}
+	else {
+		return -1;
+	}
+}
+
+static int recv_pkt_raw(bus_endpt_t* endpt, iobuffer_t** pkt)
+{
+	int pkt_len;
+	int rc = -1;
+
+	rc = recv_buf(endpt, (char*)&pkt_len, 4);
+	if (rc != 0)
+		return rc;
+	*pkt = iobuffer_create(-1, pkt_len);
+	iobuffer_push_count(*pkt, pkt_len);
+	if (pkt_len > 0) {
+		rc = recv_buf(endpt, iobuffer_data(*pkt, 0), pkt_len);
+	}
+	if (rc < 0) {
+		iobuffer_destroy(*pkt);
+		*pkt = NULL;
+	}
+
+	return rc;
+}
+
+static int start_read_pkt(bus_endpt_t* endpt, iobuffer_t** p_pkt)
+{
+	DWORD dwBytesTransferred;
+	BOOL ret;
+	int rc = 0;
+	iobuffer_t* pkt = NULL;
+
+	*p_pkt = NULL;
+
+	ResetEvent(endpt->rx_evt);
+	memset(&endpt->rx_overlapped, 0, sizeof(OVERLAPPED));
+	endpt->rx_overlapped.hEvent = endpt->rx_evt;
+
+	if (endpt->type == TYPE_PIPE) {
+		ret = ReadFile(endpt->pipe_handle,
+			&endpt->rx_pending_pkt_len,
+			4,
+			&dwBytesTransferred,
+			&endpt->rx_overlapped);
+	}
+	else if (endpt->type == TYPE_TCP) {
+		DWORD dwFlags = 0;
+		WSABUF wsabuf;
+		wsabuf.buf = (char*)&endpt->rx_pending_pkt_len;
+		wsabuf.len = 4;
+		ret = WSARecv(endpt->sock_handle,
+			&wsabuf,
+			1,
+			&dwBytesTransferred,
+			&dwFlags,
+			&endpt->rx_overlapped,
+			NULL) == 0;
+	}
+	else {
+		return -1;
+	}
+
+	if (ret) {
+		if (dwBytesTransferred == 0)
+			return -1;
+		if (dwBytesTransferred < 4) {
+			rc = recv_buf(endpt, (char*)&endpt->rx_pending_pkt_len + dwBytesTransferred, 4 - dwBytesTransferred);
+			if (rc < 0)
+				return rc;
+		}
+		pkt = iobuffer_create(0, endpt->rx_pending_pkt_len);
+		if (endpt->rx_pending_pkt_len > 0) {
+			rc = recv_buf(endpt, iobuffer_data(pkt, 0), endpt->rx_pending_pkt_len);
+			if (rc < 0) {
+				iobuffer_destroy(pkt);
+				return rc;
+			}
+
+			iobuffer_push_count(pkt, endpt->rx_pending_pkt_len);
+		}
+		*p_pkt = pkt;
+	}
+	else {
+		if (WSAGetLastError() == WSA_IO_PENDING) {
+			endpt->rx_pending = 1;
+		}
+		else {
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int read_left_pkt(bus_endpt_t* endpt, iobuffer_t** p_pkt)
+{
+	BOOL ret;
+	int rc;
+	DWORD dwBytesTransferred;
+	iobuffer_t* pkt = NULL;
+
+	if (endpt->type == TYPE_PIPE) {
+		ret = GetOverlappedResult(endpt->pipe_handle, &endpt->rx_overlapped, &dwBytesTransferred, TRUE);
+	}
+	else if (endpt->type == TYPE_TCP) {
+		DWORD dwFlags = 0;
+		ret = WSAGetOverlappedResult(endpt->sock_handle, &endpt->rx_overlapped, &dwBytesTransferred, TRUE, &dwFlags);
+	}
+
+	if (!ret || dwBytesTransferred == 0) {
+		DWORD dwError = GetLastError();
+		return -1;
+	}
+	if (dwBytesTransferred < 4) {
+		rc = recv_buf(endpt, (char*)&endpt->rx_pending_pkt_len + dwBytesTransferred, 4 - dwBytesTransferred);
+		if (rc < 0)
+			return rc;
+	}
+	pkt = iobuffer_create(-1, endpt->rx_pending_pkt_len);
+	rc = recv_buf(endpt, iobuffer_data(pkt, 0), endpt->rx_pending_pkt_len);
+	if (rc < 0) {
+		iobuffer_destroy(pkt);
+		return rc;
+	}
+
+	iobuffer_push_count(pkt, endpt->rx_pending_pkt_len);
+	*p_pkt = pkt;
+	endpt->rx_pending = 0;
+	return 0;
+}
+
+static int append_rx_pkt(bus_endpt_t* endpt, iobuffer_t* pkt)
+{
+	int type;
+	int read_state;
+
+	read_state = iobuffer_get_read_state(pkt);
+	iobuffer_read(pkt, IOBUF_T_I4, &type, 0);
+	iobuffer_restore_read_state(pkt, read_state);
+	if (type == BUS_TYPE_PACKET || type == BUS_TYPE_INFO || type == BUS_TYPE_EVENT || type == BUS_TYPE_SYSTEM) {
+		iobuffer_queue_enqueue(endpt->rx_buf_queue, pkt);
+		return 1;
+	}
+	else {
+		return -1;
+	}
+}
+
+TOOLKIT_API int bus_endpt_create(const char* url, int epid, const bus_endpt_callback* callback, bus_endpt_t** p_endpt)
+{
+	bus_endpt_t* endpt = NULL;
+	char* tmp_url;
+	url_fields uf;
+	int rc;
+	int v;
+	iobuffer_t* buf = NULL;
+	iobuffer_t* ans_buf = NULL;
+
+	if (!url)
+		return -1;
+
+	tmp_url = _strdup(url);
+
+	if (url_parse(tmp_url, &uf) < 0) {
+		free(tmp_url);
+		return -1;
+	}
+
+	endpt = ZALLOC_T(bus_endpt_t);
+	endpt->sock_handle = -1;
+	endpt->url = tmp_url;
+
+	if (_stricmp(uf.scheme, "tcp") == 0) {
+		endpt->type = TYPE_TCP;
+		endpt->sock_handle = create_socket_handle(uf.host, uf.port);
+		if (endpt->sock_handle == INVALID_SOCKET)
+			goto on_error;
+	}
+	else if (_stricmp(uf.scheme, "pipe") == 0) {
+		endpt->type = TYPE_PIPE;
+		endpt->pipe_handle = create_pipe_handle(uf.host);
+		if (endpt->pipe_handle == INVALID_HANDLE_VALUE)
+			goto on_error;
+	}
+	else {
+		goto on_error;
+	}
+
+	endpt->epid = epid;
+	endpt->tx_evt = CreateEventA(NULL, TRUE, FALSE, NULL);
+	endpt->rx_evt = CreateEventA(NULL, TRUE, FALSE, NULL);
+	endpt->rx_buf_queue = iobuffer_queue_create();
+	endpt->msg_sem = CreateSemaphoreA(NULL, 0, 0x7fffffff, NULL);
+	INIT_LIST_HEAD(&endpt->msg_list);
+	spinlock_init(&endpt->msg_lock);
+	memcpy(&endpt->callback, callback, sizeof(bus_endpt_callback));
+
+	buf = iobuffer_create(-1, -1);
+	v = BUS_TYPE_ENDPT_REGISTER;
+	iobuffer_write(buf, IOBUF_T_I4, &v, 0);
+	v = endpt->epid;
+	iobuffer_write(buf, IOBUF_T_I4, &v, 0);
+	rc = send_pkt_raw(endpt, buf);
+	if (rc != 0)
+		goto on_error;
+	rc = recv_pkt_raw(endpt, &ans_buf);
+	if (rc != 0) {
+		DWORD dwError = GetLastError();
+		goto on_error;
+	}
+	iobuffer_read(ans_buf, IOBUF_T_I4, &v, 0);
+	iobuffer_read(ans_buf, IOBUF_T_I4, &rc, 0);
+	if (rc != 0)
+		goto on_error;
+
+	url_free_fields(&uf);
+	if (buf)
+		iobuffer_destroy(buf);
+	if (ans_buf)
+		iobuffer_destroy(ans_buf);
+
+	*p_endpt = endpt;
+
+	return 0;
+
+on_error:
+	if (endpt->type == TYPE_TCP) {
+		closesocket(endpt->sock_handle);
+	}
+	else if (endpt->type == TYPE_PIPE) {
+		CloseHandle(endpt->pipe_handle);
+	}
+	if (endpt->msg_sem)
+		CloseHandle(endpt->msg_sem);
+	if (endpt->tx_evt)
+		CloseHandle(endpt->tx_evt);
+	if (endpt->rx_evt)
+		CloseHandle(endpt->rx_evt);
+	if (endpt->rx_buf_queue)
+		iobuffer_queue_destroy(endpt->rx_buf_queue);
+	if (endpt->url)
+		free(endpt->url);
+	free(endpt);
+	url_free_fields(&uf);
+	if (buf)
+		iobuffer_destroy(buf);
+	if (ans_buf)
+		iobuffer_destroy(ans_buf);
+	return -1;
+}
+
+TOOLKIT_API void bus_endpt_destroy(bus_endpt_t* endpt)
+{
+	int rc = -1;
+	iobuffer_t* buf = NULL;
+	iobuffer_t* ans_buf = NULL;
+	int v;
+
+	assert(endpt);
+
+	buf = iobuffer_create(-1, -1);
+	v = BUS_TYPE_ENDPT_UNREGISTER;
+	iobuffer_write(buf, IOBUF_T_I4, &v, 0);
+	v = endpt->epid;
+	iobuffer_write(buf, IOBUF_T_I4, &v, 0);
+	rc = send_pkt_raw(endpt, buf);
+	if (rc != 0)
+		goto on_error;
+	rc = recv_pkt_raw(endpt, &ans_buf);
+	if (rc != 0)
+		goto on_error;
+	iobuffer_read(ans_buf, IOBUF_T_I4, &v, 0);
+	iobuffer_read(ans_buf, IOBUF_T_I4, &rc, 0);
+	{
+		msg_t* msg, * t;
+		list_for_each_entry_safe(msg, t, &endpt->msg_list, msg_t, entry) {
+			list_del(&msg->entry);
+			if (msg->evt)
+				CloseHandle(msg->evt);
+			free(msg);
+		}
+	}
+
+on_error:
+
+	if (buf)
+		iobuffer_destroy(buf);
+	if (ans_buf)
+		iobuffer_destroy(ans_buf);
+	if (endpt->type == TYPE_TCP) {
+		closesocket(endpt->sock_handle);
+	}
+	else if (endpt->type == TYPE_PIPE) {
+		CloseHandle(endpt->pipe_handle);
+	}
+	if (endpt->msg_sem)
+		CloseHandle(endpt->msg_sem);
+	if (endpt->tx_evt)
+		CloseHandle(endpt->tx_evt);
+	if (endpt->rx_evt)
+		CloseHandle(endpt->rx_evt);
+	if (endpt->rx_buf_queue)
+		iobuffer_queue_destroy(endpt->rx_buf_queue);
+	if (endpt->url)
+		free(endpt->url);
+	free(endpt);
+}
+
+// 1 : recv ok
+// 0 : time out
+// <0 : error
+static int bus_endpt_poll_internal(bus_endpt_t* endpt, int* result, int timeout)
+{
+	iobuffer_t* pkt = NULL;
+	int rc;
+	BOOL ret;
+
+	assert(endpt);
+
+	// peek first packge type
+	if (iobuffer_queue_count(endpt->rx_buf_queue) > 0) {
+		iobuffer_t* pkt = iobuffer_queue_head(endpt->rx_buf_queue);
+		int pkt_type;
+		int read_state = iobuffer_get_read_state(pkt);
+		iobuffer_read(pkt, IOBUF_T_I4, &pkt_type, NULL);
+		iobuffer_restore_read_state(pkt, read_state);
+		*result = to_result(pkt_type);
+		if (*result == BUS_RESULT_UNKNOWN) {
+			OutputDebugStringA("bug: unknown pkt type!\n");
+			return -1;
+		}
+		return 1;
+	}
+
+	// no received package, try to receive one
+	if (!endpt->rx_pending) {
+		rc = start_read_pkt(endpt, &pkt);
+		if (rc < 0)
+			return rc;
+		if (pkt) {
+			OutputDebugStringA("pkt has read\n");
+			rc = append_rx_pkt(endpt, pkt);
+			if (rc < 0) {
+				iobuffer_destroy(pkt);
+				return -1;
+			}
+		}
+		else {
+			OutputDebugStringA("pending\n");
+		}
+	}
+
+	// if receive is pending, wait for send or receive complete event
+	if (!pkt) {
+		HANDLE hs[] = { endpt->msg_sem, endpt->rx_evt };
+		ret = WaitForMultipleObjects(array_size(hs), &hs[0], FALSE, (DWORD)timeout);
+		if (ret == WAIT_TIMEOUT) {
+			return 0;
+		}
+		else if (ret == WAIT_OBJECT_0) {
+			*result = BUS_RESULT_MSG;			// indicate send package event
+			return 1;
+		}
+		else if (ret == WAIT_OBJECT_0 + 1) {
+			rc = read_left_pkt(endpt, &pkt);
+			if (rc < 0)
+				return rc;
+
+			if (pkt) {
+				rc = append_rx_pkt(endpt, pkt);
+				if (rc < 0) {
+					iobuffer_destroy(pkt);
+					return -1;
+				}
+			}
+		}
+	}
+	else {
+		OutputDebugStringA("pkt has readed\n");
+	}
+
+	if (pkt) {
+		int type;
+		int read_state = iobuffer_get_read_state(pkt);
+		iobuffer_read(pkt, IOBUF_T_I4, &type, 0);
+		iobuffer_restore_read_state(pkt, read_state);
+		*result = to_result(type);
+		if (*result == BUS_RESULT_UNKNOWN) {
+			OutputDebugStringA("bug: unknown pkt type!\n");
+			return -1;
+		}
+	}
+	else {
+		return -1;
+	}
+
+	return 1;
+}
+
+static int recv_until(bus_endpt_t* endpt, int type, iobuffer_t** p_ansbuf)
+{
+	int rc;
+	iobuffer_t* ans_pkt = NULL;
+	int ans_type;
+
+	for (;;) {
+		if (!endpt->rx_pending) {
+			rc = start_read_pkt(endpt, &ans_pkt);
+			if (rc < 0) {
+				DWORD dwError = WSAGetLastError();
+				break;
+			}
+		}
+		if (!ans_pkt) {
+			DWORD ret = WaitForSingleObject(endpt->rx_evt, INFINITE);
+			if (ret != WAIT_OBJECT_0)
+				return -1;
+			rc = read_left_pkt(endpt, &ans_pkt);
+			if (rc < 0)
+				break;
+		}
+		if (ans_pkt) {
+			int read_state = iobuffer_get_read_state(ans_pkt);
+			iobuffer_read(ans_pkt, IOBUF_T_I4, &ans_type, 0);
+			iobuffer_restore_read_state(ans_pkt, read_state);
+			if (ans_type == type) {
+				*p_ansbuf = ans_pkt;
+				break;
+			}
+			else {
+				rc = append_rx_pkt(endpt, ans_pkt);
+				if (rc < 0) {
+					iobuffer_destroy(ans_pkt);
+					break;
+				}
+				else {
+					ans_pkt = NULL;
+				}
+			}
+		}
+	}
+
+	return rc;
+}
+
+static int recv_until_result(bus_endpt_t* endpt, int* p_result)
+{
+	int rc;
+	iobuffer_t* ans_pkt = NULL;
+	int type, error;
+
+	rc = recv_until(endpt, BUS_TYPE_ERROR, &ans_pkt);
+	if (rc < 0)
+		return rc;
+
+	iobuffer_read(ans_pkt, IOBUF_T_I4, &type, 0);
+	iobuffer_read(ans_pkt, IOBUF_T_I4, &error, 0);
+	iobuffer_destroy(ans_pkt);
+
+	*p_result = error;
+
+	return rc;
+}
+
+static int recv_until_state(bus_endpt_t* endpt, int* p_state)
+{
+	int rc;
+	iobuffer_t* ans_pkt = NULL;
+	int type, epid, state;
+
+	rc = recv_until(endpt, BUS_TYPE_ENDPT_GET_STATE, &ans_pkt);
+	if (rc < 0)
+		return rc;
+
+	iobuffer_read(ans_pkt, IOBUF_T_I4, &type, 0);
+	iobuffer_read(ans_pkt, IOBUF_T_I4, &epid, 0);
+	iobuffer_read(ans_pkt, IOBUF_T_I4, &state, 0);
+	iobuffer_destroy(ans_pkt);
+
+	*p_state = state;
+
+	return rc;
+}
+
+TOOLKIT_API int bus_endpt_send_pkt(bus_endpt_t* endpt, int epid, int type, iobuffer_t* pkt)
+{
+	int t;
+	int rc;
+	int read_state;
+	int write_state;
+	int error;
+
+	assert(endpt);
+
+	read_state = iobuffer_get_read_state(pkt);
+	write_state = iobuffer_get_write_state(pkt);
+
+	t = epid; // remote epid
+	iobuffer_write_head(pkt, IOBUF_T_I4, &t, 0);
+	t = endpt->epid; // local epid
+	iobuffer_write_head(pkt, IOBUF_T_I4, &t, 0);
+	t = type; // user type
+	iobuffer_write_head(pkt, IOBUF_T_I4, &t, 0);
+	t = BUS_TYPE_PACKET; // type
+	iobuffer_write_head(pkt, IOBUF_T_I4, &t, 0);
+
+	rc = send_pkt_raw(endpt, pkt);
+
+	iobuffer_restore_read_state(pkt, read_state);
+	iobuffer_restore_write_state(pkt, write_state);
+
+	if (rc < 0)
+		return rc;
+
+	rc = recv_until_result(endpt, &error);
+
+	if (rc == 0 && error != 0)
+		rc = error;
+
+	return rc;
+}
+
+TOOLKIT_API int bus_endpt_send_info(bus_endpt_t* endpt, int epid, int type, iobuffer_t* pkt)
+{
+	int t;
+	int rc;
+	int read_state;
+	int write_state;
+
+	assert(endpt);
+
+	read_state = iobuffer_get_read_state(pkt);
+	write_state = iobuffer_get_write_state(pkt);
+
+	t = epid; // remote epid
+	iobuffer_write_head(pkt, IOBUF_T_I4, &t, 0);
+	t = endpt->epid; // local epid
+	iobuffer_write_head(pkt, IOBUF_T_I4, &t, 0);
+	t = type; // user type
+	iobuffer_write_head(pkt, IOBUF_T_I4, &t, 0);
+	t = BUS_TYPE_INFO; // type
+	iobuffer_write_head(pkt, IOBUF_T_I4, &t, 0);
+
+	rc = send_pkt_raw(endpt, pkt);
+
+	iobuffer_restore_read_state(pkt, read_state);
+	iobuffer_restore_write_state(pkt, write_state);
+
+	return rc;
+}
+
+TOOLKIT_API int bus_endpt_bcast_evt(bus_endpt_t* endpt, int type, iobuffer_t* pkt)
+{
+	int t;
+	int rc;
+	int read_state;
+	int write_state;
+
+	assert(endpt);
+
+	read_state = iobuffer_get_read_state(pkt);
+	write_state = iobuffer_get_write_state(pkt);
+
+	t = endpt->epid;
+	iobuffer_write_head(pkt, IOBUF_T_I4, &t, 0);
+	t = type;
+	iobuffer_write_head(pkt, IOBUF_T_I4, &t, 0);
+	t = BUS_TYPE_EVENT;
+	iobuffer_write_head(pkt, IOBUF_T_I4, &t, 0);
+
+	rc = send_pkt_raw(endpt, pkt);
+
+	iobuffer_restore_read_state(pkt, read_state);
+	iobuffer_restore_write_state(pkt, write_state);
+
+	return rc;
+}
+
+static int bus_endpt_recv_pkt(bus_endpt_t* endpt, int* p_epid, int* p_type, iobuffer_t** p_pkt)
+{
+	if (iobuffer_queue_count(endpt->rx_buf_queue) > 0) {
+		iobuffer_t* pkt = iobuffer_queue_head(endpt->rx_buf_queue);
+		int read_state = iobuffer_get_read_state(pkt);
+		int pkt_type, usr_type, from_epid, to_epid;
+		iobuffer_read(pkt, IOBUF_T_I4, &pkt_type, 0);
+		if (pkt_type == BUS_TYPE_PACKET || pkt_type == BUS_TYPE_INFO) {
+			iobuffer_read(pkt, IOBUF_T_I4, &usr_type, 0);
+			iobuffer_read(pkt, IOBUF_T_I4, &from_epid, 0);
+			iobuffer_read(pkt, IOBUF_T_I4, &to_epid, 0);
+			if (p_epid)
+				*p_epid = from_epid;
+			if (p_type)
+				*p_type = usr_type;
+			iobuffer_queue_deque(endpt->rx_buf_queue);
+			if (p_pkt) {
+				*p_pkt = pkt;
+			}
+			else {
+				iobuffer_destroy(pkt);
+			}
+			return 0;
+		}
+		else {
+			iobuffer_restore_read_state(pkt, read_state);
+		}
+	}
+	return -1;
+}
+
+static int bus_endpt_recv_evt(bus_endpt_t* endpt, int* p_epid, int* p_type, iobuffer_t** p_pkt)
+{
+	if (iobuffer_queue_count(endpt->rx_buf_queue) > 0) {
+		iobuffer_t* pkt = iobuffer_queue_head(endpt->rx_buf_queue);
+		int read_state = iobuffer_get_read_state(pkt);
+		int pkt_type, usr_type, from_epid;
+		iobuffer_read(pkt, IOBUF_T_I4, &pkt_type, 0);
+		if (pkt_type == BUS_TYPE_EVENT) {
+			iobuffer_read(pkt, IOBUF_T_I4, &usr_type, 0);
+			iobuffer_read(pkt, IOBUF_T_I4, &from_epid, 0);
+			if (p_epid)
+				*p_epid = from_epid;
+			if (p_type)
+				*p_type = usr_type;
+			iobuffer_queue_deque(endpt->rx_buf_queue);
+			if (p_pkt) {
+				*p_pkt = pkt;
+			}
+			else {
+				iobuffer_destroy(pkt);
+			}
+			return 0;
+		}
+		else {
+			iobuffer_restore_read_state(pkt, read_state);
+		}
+	}
+	return -1;
+}
+
+static int bus_endpt_recv_sys(bus_endpt_t* endpt, int* p_epid, int* p_state)
+{
+	if (iobuffer_queue_count(endpt->rx_buf_queue) > 0) {
+		iobuffer_t* pkt = iobuffer_queue_head(endpt->rx_buf_queue);
+		int read_state = iobuffer_get_read_state(pkt);
+		int pkt_type, epid, state;
+		iobuffer_read(pkt, IOBUF_T_I4, &pkt_type, 0);
+		if (pkt_type == BUS_TYPE_SYSTEM) {
+			iobuffer_read(pkt, IOBUF_T_I4, &epid, 0);
+			iobuffer_read(pkt, IOBUF_T_I4, &state, 0);
+			if (p_epid)
+				*p_epid = epid;
+			if (p_state)
+				*p_state = state;
+			iobuffer_queue_deque(endpt->rx_buf_queue);
+			iobuffer_destroy(pkt);
+			return 0;
+		}
+		else {
+			iobuffer_restore_read_state(pkt, read_state);
+		}
+	}
+	return -1;
+}
+
+static int bus_endpt_recv_msg(bus_endpt_t* endpt, msg_t** p_msg)
+{
+	int rc = -1;
+
+	assert(endpt);
+	assert(p_msg);
+
+	spinlock_enter(&endpt->msg_lock, -1);
+	if (!list_empty(&endpt->msg_list)) {
+		msg_t* e = list_first_entry(&endpt->msg_list, msg_t, entry);
+		list_del(&e->entry);
+		rc = 0;
+		*p_msg = e;
+	}
+	spinlock_leave(&endpt->msg_lock);
+
+	return rc;
+}
+
+TOOLKIT_API int bus_endpt_get_state(bus_endpt_t* endpt, int epid, int* p_state)
+{
+	iobuffer_t* buf = NULL;
+	int v;
+	int rc = -1;
+
+	assert(endpt);
+
+	buf = iobuffer_create(-1, -1);
+	v = BUS_TYPE_ENDPT_GET_STATE;
+	iobuffer_write(buf, IOBUF_T_I4, &v, 0);
+	v = epid;
+	iobuffer_write(buf, IOBUF_T_I4, &v, 0);
+
+	rc = send_pkt_raw(endpt, buf);
+	if (rc < 0)
+		goto on_error;
+
+	rc = recv_until_state(endpt, p_state);
+
+on_error:
+
+	if (buf)
+		iobuffer_destroy(buf);
+
+	return rc;
+}
+
+TOOLKIT_API int bus_endpt_post_msg(bus_endpt_t* endpt, int msg, int nparam, int params[])
+{
+	msg_t* e;
+
+	assert(endpt);
+
+	e = MALLOC_T(msg_t);
+	e->type = msg;
+	e->nparam = nparam;
+	if (nparam) {
+		e->params = (int*)malloc(sizeof(int) * nparam);
+		memcpy(e->params, params, sizeof(int) * nparam);
+	}
+	else {
+		e->params = NULL;
+	}
+	e->evt = NULL;
+	spinlock_enter(&endpt->msg_lock, -1);
+	list_add_tail(&e->entry, &endpt->msg_list);
+	spinlock_leave(&endpt->msg_lock);
+	ReleaseSemaphore(endpt->msg_sem, 1, NULL);
+
+	return 0;
+}
+
+TOOLKIT_API int bus_endpt_send_msg(bus_endpt_t* endpt, int msg, int nparam, int params[])
+{
+	msg_t e;
+
+	assert(endpt);
+
+	e.type = msg;
+	e.nparam = nparam;
+	if (nparam) {
+		e.params = (int*)malloc(sizeof(int) * nparam);
+		memcpy(e.params, params, sizeof(int) * nparam);
+	}
+	else {
+		e.params = NULL;
+	}
+	e.evt_result = 0;
+	e.evt = CreateEventA(NULL, TRUE, FALSE, NULL);
+	spinlock_enter(&endpt->msg_lock, -1);
+	list_add_tail(&e.entry, &endpt->msg_list);
+	spinlock_leave(&endpt->msg_lock);
+	ReleaseSemaphore(endpt->msg_sem, 1, NULL);
+
+	WaitForSingleObject(e.evt, INFINITE);
+	CloseHandle(e.evt);
+
+	if (nparam) {
+		free(e.params);
+	}
+
+	return e.evt_result;
+}
+
+TOOLKIT_API int bus_endpt_get_epid(bus_endpt_t* endpt)
+{
+	return endpt->epid;
+}
+
+TOOLKIT_API const char* bus_endpt_get_url(bus_endpt_t* endpt)
+{
+	return endpt->url;
+}
+
+TOOLKIT_API int bus_endpt_poll(bus_endpt_t* endpt, int timeout)
+{
+	int result;
+	int rc;
+	int epid, type, state;
+	iobuffer_t* pkt = NULL;
+
+	rc = bus_endpt_poll_internal(endpt, &result, timeout);
+	if (rc > 0) {
+		if (result == BUS_RESULT_DATA) {
+			bus_endpt_recv_pkt(endpt, &epid, &type, &pkt);
+			if (endpt->callback.on_pkt)
+				endpt->callback.on_pkt(endpt, epid, type, &pkt, endpt->callback.user_data);
+			if (pkt)
+				iobuffer_dec_ref(pkt);
+		}
+		else if (result == BUS_RESULT_INFO) {
+			bus_endpt_recv_pkt(endpt, &epid, &type, &pkt);
+			if (endpt->callback.on_inf)
+				endpt->callback.on_inf(endpt, epid, type, &pkt, endpt->callback.user_data);
+			if (pkt)
+				iobuffer_dec_ref(pkt);
+		}
+		else if (result == BUS_RESULT_EVENT) {
+			bus_endpt_recv_evt(endpt, &epid, &type, &pkt);
+			if (endpt->callback.on_evt)
+				endpt->callback.on_evt(endpt, epid, type, &pkt, endpt->callback.user_data);
+			if (pkt)
+				iobuffer_dec_ref(pkt);
+		}
+		else if (result == BUS_RESULT_SYSTEM) {
+			bus_endpt_recv_sys(endpt, &epid, &state);
+			if (endpt->callback.on_sys)
+				endpt->callback.on_sys(endpt, epid, state, endpt->callback.user_data);
+		}
+		else if (result == BUS_RESULT_MSG) {
+			msg_t* msg = NULL;
+			bus_endpt_recv_msg(endpt, &msg);
+			if (endpt->callback.on_msg) {
+				endpt->callback.on_msg(endpt, 
+					msg->type,
+					msg->nparam, 
+					msg->params, 
+					msg->evt ? &msg->evt_result : NULL,
+					endpt->callback.user_data);
+				if (msg->evt)
+					SetEvent(msg->evt);
+				else
+					free_msg(msg);
+			}
+			else {
+				if (msg->evt) {
+					msg->evt_result = -1;
+					SetEvent(msg->evt);
+				}
+				else {
+					free_msg(msg);
+				}
+			}
+		}
+		else {
+			assert(0);
+			rc = -1;
+		}
+	}
+
+	return rc;
+}
+
+TOOLKIT_API int bus_endpt_set_quit_flag(bus_endpt_t* endpt)
+{
+	endpt->quit_flag = 1;
+	return 0;
+}
+
+TOOLKIT_API int bus_endpt_get_quit_flag(bus_endpt_t* endpt)
+{
+	return endpt->quit_flag;
+}
+

+ 0 - 0
libtoolkit/bus.c → libtoolkit/bus-win.c


+ 10 - 10
libtoolkit/bus_daemon.c → libtoolkit/bus_daemon-unix.c

@@ -17,6 +17,8 @@
 #include <winpr/thread.h>
 #include <winpr/winsock.h>
 
+#define TAG TOOLKIT_TAG("bus_deamon")
+
 #define MAX_THREADS					            32
 #define DEFAULT_ACCEPT_OP_COUNT		5
 #define MSG_REMOVE_REGISTAR			    1
@@ -888,27 +890,27 @@ TOOLKIT_API int bus_daemon_create(int n_url, const char *urls[], int nthread, bu
 
 	if (n_url == 0)
 		return -1;
-
 	if (nthread <= 0) {
 		SYSTEM_INFO si;
 		GetSystemInfo(&si);
 		nthread = min(MAX_THREADS, si.dwNumberOfProcessors << 1);
 	}
-
+	WLog_DBG(TAG, "thread num: %d", nthread);
 	daemon = MALLOC_T(bus_daemon_t);
 	memset(daemon, 0, sizeof(bus_daemon_t));
 	daemon->nthread = nthread;
 	daemon->arr_acceptor = array_make(n_url, sizeof(daemon_accetpor_t*));
 	daemon->arr_thread = array_make(nthread, sizeof(HANDLE));
 	daemon->arr_uri = array_make(n_url, sizeof(char*));
-	for (i = 0; i < n_url; ++i)
+	for (i = 0; i < n_url; ++i) {
+		WLog_DBG(TAG, "urls[%d]: %s", i, urls[i]);
 		ARRAY_PUSH(daemon->arr_uri, char*) = _strdup(urls[i]);
+	}
 	InitializeCriticalSection(&daemon->lock);
 	INIT_LIST_HEAD(&daemon->registered_session_list);
 	INIT_LIST_HEAD(&daemon->unregistered_session_list);
 
 	*p_daemon = daemon;
-
 	return 0;
 }
 
@@ -930,12 +932,12 @@ TOOLKIT_API int bus_daemon_destroy(bus_daemon_t *daemon)
 TOOLKIT_API int bus_daemon_start(bus_daemon_t *daemon)
 {
 	int i;
-
 	daemon->ioq = ioqueue_create();
-	if (!daemon->ioq)
+	if (!daemon->ioq) {
+		WLog_ERR(TAG, "ioqueuq create failed!");
 		return -1;
+	}
 	ioqueue_msg_add_handler(daemon->ioq, MSG_REMOVE_REGISTAR, 0, &on_msg);
-
 	for (i = 0; i < daemon->arr_uri->nelts; ++i) {
 		char *url = ARRAY_IDX(daemon->arr_uri, i, char*);
 		daemon_accetpor_t *dacceptor = create_daemon_acceptor(daemon, url);
@@ -945,12 +947,11 @@ TOOLKIT_API int bus_daemon_start(bus_daemon_t *daemon)
 				start_accept(dacceptor, kk);
 			ARRAY_PUSH(daemon->arr_acceptor, daemon_accetpor_t*) = dacceptor;
 		} else {
+			WLog_ERR(TAG, "create daemon acceptor failed!");
 			return -1;
 		}
 	}
-
 	daemon->lstop = 0;
-
 	for (i = 0; i < daemon->nthread; ++i) {
 		HANDLE thread = (HANDLE)_beginthreadex(NULL, 0, &thread_proc, daemon, 0, NULL);
 		if (thread) {
@@ -959,7 +960,6 @@ TOOLKIT_API int bus_daemon_start(bus_daemon_t *daemon)
 			return -1;
 		}
 	}
-
 	return 0;
 }
 

+ 1018 - 0
libtoolkit/bus_daemon-win.c

@@ -0,0 +1,1018 @@
+#include "precompile.h"
+#include "ioqueue.h"
+#include "bus.h"
+#include "bus_internal.h"
+#include "url.h"
+#include "refcnt.h"
+#include "list.h"
+#include "iobuffer.h"
+#include "array.h"
+
+#include <time.h>
+
+#include <winpr/synch.h>
+#include <winpr/interlocked.h>
+#include <winpr/string.h>
+#include <winpr/sysinfo.h>
+#include <winpr/thread.h>
+#include <winpr/winsock.h>
+
+#define TAG TOOLKIT_TAG("bus_deamon")
+
+#define MAX_THREADS					            32
+#define DEFAULT_ACCEPT_OP_COUNT		5
+#define MSG_REMOVE_REGISTAR			    1
+
+typedef struct endpt_session_t  endpt_session_t;
+typedef struct daemon_accetpor_t daemon_accetpor_t;
+
+struct endpt_session_t 
+{
+	DECLARE_REF_COUNT_MEMBER(ref_cnt);
+	int epid;
+	int registered;	
+	union {
+		ioqueue_tcpsock_t tcp;
+		ioqueue_file_t pipe;
+	};
+	CRITICAL_SECTION lock;
+	time_t start_time;
+	int err;
+	int rx_pending_pkt_len;
+	int type;
+	int tx_pending;
+	struct list_head entry;
+	iobuffer_queue_t *tx_iobuf_queue;
+	ioqueue_overlapped_t rx_overlapped;
+	ioqueue_overlapped_t tx_overlapped;
+	iobuffer_t *rx_pending_buf;
+	iobuffer_t *tx_pending_buf;
+	bus_daemon_t *daemon;
+	daemon_accetpor_t *acceptor;
+};
+
+struct daemon_accetpor_t 
+{
+	DECLARE_REF_COUNT_MEMBER(ref_cnt);
+	union {
+		ioqueue_acceptor_t tcp_acceptor;
+		ioqueue_pipe_acceptor_t pipe_acceptor;
+	};
+	int type;
+	array_header_t *arr_ov;
+	bus_daemon_t *daemon;
+};
+
+struct bus_daemon_t
+{
+	ioqueue_t *ioq;
+	volatile LONG lstop;
+	CRITICAL_SECTION lock;
+	int nthread;
+	array_header_t *arr_uri;
+	array_header_t *arr_acceptor;
+	array_header_t *arr_thread;
+	struct list_head registered_session_list;
+	struct list_head unregistered_session_list;
+};
+
+DECLARE_REF_COUNT_STATIC(endpt_session, endpt_session_t)
+DECLARE_REF_COUNT_STATIC(daemon_accetpor, daemon_accetpor_t)
+
+static unsigned int __stdcall thread_proc(void *param)
+{
+	bus_daemon_t *daemon = (bus_daemon_t*)param;
+
+	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
+
+	while (!daemon->lstop) {
+		ioqueue_poll(daemon->ioq, 10);
+	}
+
+	return 0;
+}
+
+static int start_accept(daemon_accetpor_t *acceptor, int idx);
+static int queue_ans_pkt(endpt_session_t *session, int rc);
+static int start_send_pkt(endpt_session_t *session, iobuffer_t **pkt);
+static int session_start_recv_hdr(endpt_session_t *session);
+static int queue_sys_pkt(endpt_session_t *session, int epid, int state);
+
+static void daemon_lock(bus_daemon_t *daemon)
+{
+	EnterCriticalSection(&daemon->lock);
+}
+
+static void daemon_unlock(bus_daemon_t *daemon)
+{
+	LeaveCriticalSection(&daemon->lock);
+}
+
+static void session_lock(endpt_session_t *session)
+{
+	EnterCriticalSection(&session->lock);
+}
+
+static void session_unlock(endpt_session_t *session)
+{
+	LeaveCriticalSection(&session->lock);
+}
+
+static void add_unregistered_list(endpt_session_t *session)
+{
+	bus_daemon_t *daemon = session->daemon;
+	daemon_lock(daemon);
+	list_add_tail(&session->entry, &daemon->unregistered_session_list);
+	daemon_unlock(daemon);
+}
+
+static void remove_session_list(endpt_session_t *session)
+{
+	bus_daemon_t *daemon = session->daemon;
+	daemon_lock(daemon);
+	list_del(&session->entry);
+	if (session->epid != BUS_INVALID_EPID) {
+		endpt_session_t *pos, *n;
+		list_for_each_entry_safe(pos, n, &daemon->registered_session_list, endpt_session_t, entry) {
+			queue_sys_pkt(pos, session->epid, BUS_STATE_OFF);
+		}
+	}
+	daemon_unlock(daemon);
+}
+
+static void move_to_registered_session(endpt_session_t *session)
+{
+	bus_daemon_t *daemon = session->daemon;
+	daemon_lock(daemon);
+	{
+		endpt_session_t *pos, *n;
+		list_for_each_entry_safe(pos, n, &daemon->registered_session_list, endpt_session_t, entry) {
+			queue_sys_pkt(pos, session->epid, BUS_STATE_ON);
+		}
+	}
+	list_move_tail(&session->entry, &daemon->registered_session_list);
+	daemon_unlock(daemon);
+}
+
+static void move_to_unregistered_session(endpt_session_t *session)
+{
+	bus_daemon_t *daemon = session->daemon;
+	daemon_lock(daemon);
+	list_move_tail(&session->entry, &daemon->unregistered_session_list);
+	daemon_unlock(daemon);
+}
+
+static endpt_session_t* create_session(daemon_accetpor_t *acceptor, int type, int fd)
+{
+	int rc;
+	endpt_session_t *session;
+
+	session = ZALLOC_T(endpt_session_t);
+
+	if (type == TYPE_PIPE) {
+		rc = ioqueue_pipe_acceptor_create_client(&acceptor->pipe_acceptor, (HANDLE)fd, &session->pipe);
+		if (rc < 0)
+			goto on_error;
+	} else if (type == TYPE_TCP) {
+		rc = ioqueue_acceptor_create_client(&acceptor->tcp_acceptor, fd, &session->tcp);
+		if (rc < 0)
+			goto on_error;
+	}
+
+	session->epid = BUS_INVALID_EPID;
+	session->start_time = time(NULL);
+	session->daemon = acceptor->daemon;
+	session->acceptor = acceptor;
+	session->type = type;
+	REF_COUNT_INIT(&session->ref_cnt);
+	InitializeCriticalSection(&session->lock);
+	session->tx_iobuf_queue = iobuffer_queue_create();
+
+	return session;
+
+on_error:
+	free(session);
+	return NULL;
+}
+
+static void destroy_session(endpt_session_t *session)
+{
+
+	while (iobuffer_queue_count(session->tx_iobuf_queue) > 0) {
+		iobuffer_t *iobuf = iobuffer_queue_deque(session->tx_iobuf_queue);
+		endpt_session_t *ts = iobuffer_get_user_data(iobuf);
+		if (ts) {
+			queue_ans_pkt(ts, BUS_E_NETBROKEN);
+			endpt_session_dec_ref(ts);
+		}
+		iobuffer_destroy(iobuf);
+	}
+	iobuffer_queue_destroy(session->tx_iobuf_queue);
+
+	DeleteCriticalSection(&session->lock);
+
+	if (session->type == TYPE_PIPE) {
+		ioqueue_file_destroy(&session->pipe);
+	} else if (session->type == TYPE_TCP) {
+		ioqueue_tcpsock_destroy(&session->tcp);
+	} else {
+		assert(0);
+	}
+
+	free(session);
+}
+
+IMPLEMENT_REF_COUNT_MT_STATIC(endpt_session, endpt_session_t, ref_cnt, destroy_session)
+
+static endpt_session_t *find_session(bus_daemon_t *daemon, int epid)
+{
+	endpt_session_t *session = NULL, *pos;
+
+	daemon_lock(daemon);
+	list_for_each_entry(pos, &daemon->registered_session_list, endpt_session_t, entry) {
+		if (pos->epid == epid) {
+			endpt_session_inc_ref(pos);
+			session = pos;
+			break;
+		}
+	}
+	daemon_unlock(daemon);
+
+	return session;
+}
+
+static void on_send_pkt(endpt_session_t *session, int err)
+{
+	iobuffer_t *pkt;
+
+	session_lock(session);
+	pkt = session->tx_pending_buf;	
+	session->tx_pending_buf = NULL;
+	session->tx_pending = 0;	
+	session_unlock(session);
+
+	if (pkt) {
+		if (iobuffer_get_user_data(pkt)) {
+			endpt_session_t *ts = iobuffer_get_user_data(pkt);
+			queue_ans_pkt(ts, err ? BUS_E_NETBROKEN : BUS_E_OK);
+			endpt_session_dec_ref(ts);		
+		}
+		iobuffer_destroy(pkt);
+		pkt = NULL;
+	}
+
+	if (!err) {
+		int rc = 0;
+
+		session_lock(session);
+		if (!session->tx_pending) {
+			if (iobuffer_queue_count(session->tx_iobuf_queue)) {
+				iobuffer_t *tpkt = iobuffer_queue_deque(session->tx_iobuf_queue);
+				session->tx_pending = 1;
+				rc = start_send_pkt(session, &tpkt);
+				if (rc < 0) {
+					session->tx_pending = 0;
+				}
+				if (tpkt) {
+					pkt = tpkt;
+				}
+			}
+		}
+		session_unlock(session);
+
+		if (rc < 0) {
+			if (InterlockedCompareExchange((LONG*)&session->err, rc, 0) == 0) {
+				remove_session_list(session);
+				endpt_session_dec_ref(session);
+			}
+		}
+
+		if (pkt) {
+			if (iobuffer_get_user_data(pkt)) {
+				endpt_session_t *ts = iobuffer_get_user_data(pkt);
+				queue_ans_pkt(ts, err ? BUS_E_NETBROKEN : BUS_E_OK);
+				endpt_session_dec_ref(ts);		
+			}
+			iobuffer_destroy(pkt);
+			pkt = NULL;
+		}
+
+	} else {
+		session_lock(session);
+		while (iobuffer_queue_count(session->tx_iobuf_queue) > 0) {
+			iobuffer_t *iobuf = iobuffer_queue_deque(session->tx_iobuf_queue);
+			endpt_session_t *ts = iobuffer_get_user_data(iobuf);
+			if (ts) {
+				session_unlock(session);
+				queue_ans_pkt(ts, BUS_E_NETBROKEN);
+				session_lock(session);
+				endpt_session_dec_ref(ts);
+			}
+			iobuffer_destroy(iobuf);
+		}
+		session_unlock(session);
+	}
+
+	endpt_session_dec_ref(session);
+}
+
+static void on_pipe_send_pkt(ioqueue_file_t* file, 
+							 ioqueue_overlapped_t *overlapped,
+							 void *buf,
+							 unsigned int transfer_bytes, 
+							 void *user_data,
+							 int err)
+{
+	on_send_pkt(user_data, err);
+}
+static void on_tcp_send_pkt(ioqueue_tcpsock_t* tcpsock, 
+							ioqueue_overlapped_t *overlapped,
+							void *buf,
+							unsigned int transfer_bytes, 
+							void *user_data,
+							int err)
+{
+	on_send_pkt(user_data, err);
+}
+
+static int start_send_pkt(endpt_session_t *session, iobuffer_t **pkt)
+{
+	int rc = -1;
+	int v;
+
+	if (session->err < 0)
+		return rc;
+
+	v = iobuffer_get_length(*pkt);
+	iobuffer_write_head(*pkt, IOBUF_T_I4, &v, 0);
+
+	endpt_session_inc_ref(session);
+
+	session->tx_pending_buf = *pkt;
+	*pkt = NULL;
+
+	if (session->type == TYPE_PIPE) {
+		rc = ioqueue_file_async_writen(&session->pipe, 
+			&session->tx_overlapped, 
+			iobuffer_data(session->tx_pending_buf, 0), 
+			iobuffer_get_length(session->tx_pending_buf), 
+			&on_pipe_send_pkt, session);
+	} else if (session->type == TYPE_TCP) {
+		rc = ioqueue_tcpsock_async_sendn(&session->tcp,
+			&session->tx_overlapped,
+			iobuffer_data(session->tx_pending_buf, 0), 
+			iobuffer_get_length(session->tx_pending_buf), 
+			&on_tcp_send_pkt, session);
+	} else {
+		assert(0);
+	}
+
+	if (rc < 0) {
+		*pkt = session->tx_pending_buf;
+		endpt_session_dec_ref(session);
+	}
+
+	return rc;
+}
+
+static int queue_tx_pkt(endpt_session_t *session, iobuffer_t **pkt)
+{
+	int rc = -1;
+
+	session_lock(session);
+
+	if (session->tx_pending) {
+		iobuffer_queue_enqueue(session->tx_iobuf_queue, *pkt);
+		*pkt = NULL;
+		rc = 0;
+	} else {
+		session->tx_pending = 1;
+		rc = start_send_pkt(session, pkt);
+	}
+
+	session_unlock(session);
+
+	if (rc < 0) {
+		if (InterlockedCompareExchange((LONG*)&session->err, rc, 0) == 0) {
+			ioqueue_post_message(session->daemon->ioq, MSG_REMOVE_REGISTAR, (int)session, 0);
+		}
+	}
+
+	return rc;
+}
+
+static iobuffer_t *make_ans_pkt(int rc)
+{
+	int v;
+	iobuffer_t *pkt = iobuffer_create(-1, -1);
+	v = BUS_TYPE_ERROR;
+	iobuffer_write(pkt, IOBUF_T_I4, &v, 0);
+	v = rc;
+	iobuffer_write(pkt, IOBUF_T_I4, &v, 0);
+	return pkt;
+}
+
+static int queue_ans_pkt(endpt_session_t *session, int rc)
+{
+	iobuffer_t *pkt = make_ans_pkt(rc);
+	int err = queue_tx_pkt(session, &pkt);
+	if (pkt) {
+		iobuffer_destroy(pkt);
+	}
+	return err;
+}
+
+static iobuffer_t *make_ans_state_pkt(bus_daemon_t *daemon, int epid)
+{
+	int v;
+	endpt_session_t *ts;
+	iobuffer_t *pkt;
+	int state;
+
+	ts = find_session(daemon, epid);
+	if (!ts) {
+		state = BUS_STATE_OFF;
+	} else {
+		state = BUS_STATE_ON;
+		endpt_session_dec_ref(ts);
+		ts = NULL;
+	}
+	
+	pkt = iobuffer_create(-1, -1);
+	v = BUS_TYPE_ENDPT_GET_STATE;
+	iobuffer_write(pkt, IOBUF_T_I4, &v, 0);
+	v = epid;
+	iobuffer_write(pkt, IOBUF_T_I4, &v, 0);
+	v = state;
+	iobuffer_write(pkt, IOBUF_T_I4, &v, 0);
+	
+	return pkt;
+}
+
+static iobuffer_t *make_sys_pkt(int epid, int state)
+{
+	int v;
+	iobuffer_t *pkt = iobuffer_create(-1, -1);
+	v = BUS_TYPE_SYSTEM;
+	iobuffer_write(pkt, IOBUF_T_I4, &v, 0);
+	v = epid;
+	iobuffer_write(pkt, IOBUF_T_I4, &v, 0);
+	v = state;
+	iobuffer_write(pkt, IOBUF_T_I4, &v, 0);
+	return pkt;
+}
+
+static int queue_sys_pkt(endpt_session_t *session, int epid, int state)
+{
+	iobuffer_t *pkt = make_sys_pkt(epid, state);
+	int err = queue_tx_pkt(session, &pkt);
+	if (pkt) {
+		iobuffer_destroy(pkt);
+	}
+	return err;
+}
+
+static int on_process_pkt(endpt_session_t *session, iobuffer_t **ppkt)
+{
+	bus_daemon_t *daemon = session->daemon;
+	iobuffer_t *pkt = *ppkt;
+	int type;
+	int read_state;
+	int rc = -1;
+
+	read_state = iobuffer_get_read_state(pkt);
+
+	iobuffer_read(pkt, IOBUF_T_I4, &type, 0);
+
+	if (session->registered) {
+		if (type == BUS_TYPE_ENDPT_UNREGISTER) {
+			move_to_unregistered_session(session);
+			session->registered = 0;
+			queue_ans_pkt(session, BUS_E_OK);
+			rc = -1;
+		} else if (type == BUS_TYPE_ENDPT_GET_STATE) {
+			int epid;
+			iobuffer_t *ans_pkt;
+			iobuffer_read(pkt, IOBUF_T_I4, &epid, 0);
+			ans_pkt = make_ans_state_pkt(session->daemon, epid);
+			rc = queue_tx_pkt(session, &ans_pkt);
+			if (ans_pkt)
+				iobuffer_dec_ref(ans_pkt);
+		} else if (type == BUS_TYPE_PACKET) {
+			int from_epid;
+			int to_epid;
+			int user_type;
+			iobuffer_read(pkt, IOBUF_T_I4, &user_type, 0);
+			iobuffer_read(pkt, IOBUF_T_I4, &from_epid, 0);
+			iobuffer_read(pkt, IOBUF_T_I4, &to_epid, 0);
+			iobuffer_restore_read_state(pkt, read_state);
+			if (to_epid == session->epid) {
+				endpt_session_inc_ref(session);
+				iobuffer_set_user_data(pkt, session);
+				rc = queue_tx_pkt(session, ppkt);
+				if (rc < 0) {
+					endpt_session_dec_ref(session);
+					iobuffer_set_user_data(pkt, NULL);
+				}
+			} else {
+				endpt_session_t *ts = find_session(session->daemon, to_epid);
+				if (!ts) {
+					rc = queue_ans_pkt(session, BUS_E_NOTFOUND);
+				} else {
+					if (ts->err) {
+						rc = queue_ans_pkt(session, BUS_E_NETBROKEN);
+					} else {
+						endpt_session_inc_ref(session);
+						iobuffer_set_user_data(pkt, session);
+						rc = queue_tx_pkt(ts, ppkt);
+						if (rc < 0) {
+							endpt_session_dec_ref(session);
+							iobuffer_set_user_data(pkt, NULL);
+							rc = queue_ans_pkt(session, BUS_E_NETBROKEN);
+						}
+					}
+					endpt_session_dec_ref(ts); // find session
+				}
+			}
+		} else if (type == BUS_TYPE_EVENT) {
+			int epid;
+			int user_type;
+			iobuffer_read(pkt, IOBUF_T_I4, &user_type, 0);
+			iobuffer_read(pkt, IOBUF_T_I4, &epid, 0);
+			iobuffer_restore_read_state(pkt, read_state);
+			daemon_lock(session->daemon);
+			{
+				endpt_session_t *pos, *n;
+				list_for_each_entry_safe(pos, n, &daemon->registered_session_list, endpt_session_t, entry) {
+					if (pos != session) {
+						iobuffer_t *tbuf = iobuffer_clone(pkt);
+						queue_tx_pkt(pos, &tbuf);
+						if (tbuf)
+							iobuffer_destroy(tbuf);
+					}
+				}
+			}
+			daemon_unlock(session->daemon);
+			rc = 0;
+		} else if (type == BUS_TYPE_INFO) {
+			int from_epid;
+			int to_epid;
+			int user_type;
+			iobuffer_read(pkt, IOBUF_T_I4, &user_type, 0);
+			iobuffer_read(pkt, IOBUF_T_I4, &from_epid, 0);
+			iobuffer_read(pkt, IOBUF_T_I4, &to_epid, 0);
+			iobuffer_restore_read_state(pkt, read_state);
+			if (to_epid == session->epid) {
+				queue_tx_pkt(session, ppkt);
+			} else {
+				endpt_session_t *ts = find_session(session->daemon, to_epid);
+				if (ts) {
+					if (!ts->err) {
+						queue_tx_pkt(ts, ppkt);
+					}
+					endpt_session_dec_ref(ts); // find session
+				}
+			}
+			rc = 0;
+		}
+	} else {
+		if (type == BUS_TYPE_ENDPT_REGISTER) {
+			int epid;
+			endpt_session_t *ts;
+			iobuffer_read(pkt, IOBUF_T_I4, &epid, 0);
+			ts = find_session(session->daemon, epid);
+			if (!ts) {
+				session->registered = 1;
+				session->epid = epid;
+				rc = queue_ans_pkt(session, BUS_E_OK);
+				if (rc == 0) {
+					move_to_registered_session(session);
+				}
+			} else {
+				endpt_session_dec_ref(ts);
+			}
+		}
+	}
+
+	if (*ppkt)
+		iobuffer_restore_read_state(pkt, read_state);
+
+	return rc;
+}
+
+static void on_recv_body(endpt_session_t *session, unsigned int transfer_bytes, int err)
+{
+	int rc = -1;
+
+	if (!err) {
+		iobuffer_t *pkt = session->rx_pending_buf;
+		session->rx_pending_buf = NULL;
+		iobuffer_push_count(pkt, transfer_bytes);
+		rc = on_process_pkt(session, &pkt);
+		if (rc == 0) {
+			rc = session_start_recv_hdr(session);
+		}
+		if (pkt)
+			iobuffer_dec_ref(pkt);
+	}
+
+	if (rc < 0) {
+		if (InterlockedCompareExchange((LONG*)&session->err, rc, 0) == 0) {
+			remove_session_list(session);
+			endpt_session_dec_ref(session);
+		}
+	}
+
+	endpt_session_dec_ref(session);
+}
+
+static void on_pipe_recv_body(ioqueue_file_t* file, 
+							  ioqueue_overlapped_t *overlapped,
+							  void *buf, 
+							  unsigned int transfer_bytes, 
+							  void *user_data,
+							  int err)
+{
+	on_recv_body(user_data, transfer_bytes, err);
+}
+static void on_tcp_recv_body(ioqueue_tcpsock_t* tcpsock, 
+							 ioqueue_overlapped_t *overlapped,
+							 void *buf, 
+							 unsigned int transfer_bytes, 
+							 void *user_data,
+							 int err)
+{
+	on_recv_body(user_data, transfer_bytes, err);
+}
+
+static int session_start_recv_body(endpt_session_t *session)
+{
+	int rc = -1;
+
+	if (session->err < 0)
+		return rc;
+
+	endpt_session_inc_ref(session);
+	session->rx_pending_buf = iobuffer_create(-1, session->rx_pending_pkt_len);
+
+	if (session->type == TYPE_PIPE) {
+		rc = ioqueue_file_async_readn(&session->pipe, 
+			&session->rx_overlapped, 
+			iobuffer_data(session->rx_pending_buf, 0), 
+			session->rx_pending_pkt_len,
+			&on_pipe_recv_body,
+			session);
+	} else if (session->type == TYPE_TCP) {
+		rc = ioqueue_tcpsock_async_recvn(&session->tcp,
+			&session->rx_overlapped,
+			iobuffer_data(session->rx_pending_buf, 0),
+			session->rx_pending_pkt_len,
+			&on_tcp_recv_body,
+			session);
+	}
+
+	if (rc < 0) {
+		iobuffer_destroy(session->rx_pending_buf);
+		session->rx_pending_buf = NULL;
+		endpt_session_dec_ref(session);
+	}
+
+	return rc;
+}
+
+static void on_recv_hdr(endpt_session_t *session, int err)
+{
+	int rc = -1;
+
+	if (!err) {
+		rc = session_start_recv_body(session);
+	}
+
+	if (rc < 0) {
+		if (InterlockedCompareExchange((LONG*)&session->err, rc, 0) == 0) {
+			remove_session_list(session);
+			endpt_session_dec_ref(session);
+		}
+	}
+
+	endpt_session_dec_ref(session);
+}
+
+static void on_pipe_recv_hdr(ioqueue_file_t *file, 
+							ioqueue_overlapped_t *overlapped,
+							void *buf, 
+							unsigned int transfer_bytes, 
+							void *user_data, 
+							int err)
+{
+	on_recv_hdr(user_data, err);
+}
+
+static void on_tcp_recv_hdr(ioqueue_tcpsock_t *tcpsock, 
+							ioqueue_overlapped_t *overlapped,
+							void *buf, 
+							unsigned int transfer_bytes, 
+							void *user_data, 
+							int err)
+{
+	on_recv_hdr(user_data, err);
+}
+
+static int session_start_recv_hdr(endpt_session_t *session)
+{
+	int rc = -1;
+
+	if (session->err < 0)
+		return rc;
+
+	endpt_session_inc_ref(session);
+
+	if (session->type == TYPE_PIPE) {
+		rc = ioqueue_file_async_readn(&session->pipe, 
+			&session->rx_overlapped, 
+			&session->rx_pending_pkt_len, 
+			4, 
+			&on_pipe_recv_hdr, 
+			session);
+	} else if (session->type == TYPE_TCP) {
+		rc = ioqueue_tcpsock_async_recvn(&session->tcp, 
+			&session->rx_overlapped,
+			&session->rx_pending_pkt_len,
+			4,
+			&on_tcp_recv_hdr,
+			session);
+	}
+
+	if (rc < 0)
+		endpt_session_dec_ref(session);
+
+	return rc;
+}
+
+static int on_msg(unsigned short msg_id, int param1, int param2)
+{
+	if (msg_id == MSG_REMOVE_REGISTAR) {
+		endpt_session_t *session = (endpt_session_t*)param1;
+		remove_session_list(session);
+		endpt_session_dec_ref(session);
+	}
+	return TRUE;
+}
+
+static int on_accept(daemon_accetpor_t *dacceptor, void *user_data, int fd, int err)
+{
+	int idx = (int)user_data;
+
+	if (!err) {
+		endpt_session_t *session = create_session(dacceptor, dacceptor->type, fd);
+		if (session) {
+			int rc;
+			add_unregistered_list(session);
+			rc = session_start_recv_hdr(session);
+			if (rc < 0) {
+				remove_session_list(session);
+				endpt_session_dec_ref(session);
+			}
+		}
+		start_accept(dacceptor, idx);
+	}
+
+	daemon_accetpor_dec_ref(dacceptor);
+
+	return 1;
+}
+
+static int on_pipe_accept_callback(ioqueue_pipe_acceptor_t *acceptor, 
+								   ioqueue_overlapped_t *overlapped,
+								   HANDLE pipe,
+								   void *user_data, 
+								   int err)
+{
+	daemon_accetpor_t *dacceptor = CONTAINING_RECORD(acceptor, daemon_accetpor_t, pipe_acceptor);
+	return on_accept(dacceptor, user_data, (int)pipe, err);
+}
+
+static int on_tcp_accept_callback(ioqueue_acceptor_t *acceptor, 
+								  ioqueue_overlapped_t *overlapped,
+								  SOCKET in_sock,
+								  void *user_data, 
+								  int err)
+{
+	daemon_accetpor_t *dacceptor = CONTAINING_RECORD(acceptor, daemon_accetpor_t, tcp_acceptor);
+	return on_accept(dacceptor, user_data, in_sock, err);
+}
+
+static int start_accept(daemon_accetpor_t *acceptor, int idx)
+{
+	int rc = -1;
+
+	daemon_accetpor_inc_ref(acceptor);
+
+	if (acceptor->type == TYPE_PIPE) {
+		ioqueue_overlapped_t *ov = ARRAY_IDX(acceptor->arr_ov, idx, ioqueue_overlapped_t*);
+		rc = ioqueue_pipe_acceptor_async_accept(&acceptor->pipe_acceptor, ov, &on_pipe_accept_callback, (void*)idx);
+	} else if (acceptor->type == TYPE_TCP) {
+		ioqueue_overlapped_t *ov = ARRAY_IDX(acceptor->arr_ov, idx, ioqueue_overlapped_t*);
+		rc = ioqueue_acceptor_async_accept(&acceptor->tcp_acceptor, ov, &on_tcp_accept_callback, (void*)idx);
+	} else {
+		assert(0);
+	}
+
+	if (rc < 0)
+		daemon_accetpor_dec_ref(acceptor);
+
+	return rc;
+}
+
+static daemon_accetpor_t* create_daemon_acceptor(bus_daemon_t *daemon, char *url)
+{
+	url_fields uf;
+	int kk;
+	int rc;
+	daemon_accetpor_t *dacceptor = NULL;
+
+	dacceptor = MALLOC_T(daemon_accetpor_t);
+
+	if (url_parse(url, &uf) == 0) {
+		if (_stricmp(uf.scheme, "tcp") == 0) {
+			rc = ioqueue_acceptor_create(daemon->ioq, uf.host, uf.port, &dacceptor->tcp_acceptor);
+			if (rc < 0) {
+				free(dacceptor);
+				url_free_fields(&uf);
+				return NULL;
+			}
+			ioqueue_acceptor_listen(&dacceptor->tcp_acceptor, 10);
+			dacceptor->type = TYPE_TCP;
+		} else if (_stricmp(uf.scheme, "pipe") == 0) {
+			rc = ioqueue_pipe_acceptor_create(daemon->ioq, uf.host, &dacceptor->tcp_acceptor);
+			if (rc < 0) {
+				free(dacceptor);
+				url_free_fields(&uf);
+				return NULL;
+			}
+			dacceptor->type = TYPE_PIPE;
+		}
+		url_free_fields(&uf);
+	}
+
+	dacceptor->daemon = daemon;
+	REF_COUNT_INIT(&dacceptor->ref_cnt);
+	dacceptor->arr_ov = array_make(DEFAULT_ACCEPT_OP_COUNT, sizeof(ioqueue_overlapped_t*));
+	for (kk = 0; kk < DEFAULT_ACCEPT_OP_COUNT; ++kk)
+		ARRAY_PUSH(dacceptor->arr_ov, ioqueue_overlapped_t*) = MALLOC_T(ioqueue_overlapped_t);
+
+	return dacceptor;
+}
+
+static void destroy_daemon_acceptor(daemon_accetpor_t *dacceptor)
+{
+	int i;
+
+	for (i = 0; i < dacceptor->arr_ov->nelts; ++i)
+		free(ARRAY_IDX(dacceptor->arr_ov, i, ioqueue_overlapped_t*));
+	array_free(dacceptor->arr_ov);
+
+	if (dacceptor->type == TYPE_PIPE) {
+		ioqueue_pipe_acceptor_destroy(&dacceptor->pipe_acceptor);
+	} else if (dacceptor->type == TYPE_TCP) {
+		ioqueue_acceptor_destroy(&dacceptor->tcp_acceptor);
+	}
+
+	free(dacceptor);
+}
+
+IMPLEMENT_REF_COUNT_MT_STATIC(daemon_accetpor, daemon_accetpor_t, ref_cnt, destroy_daemon_acceptor)
+
+TOOLKIT_API int bus_daemon_create(int n_url, const char *urls[], int nthread, bus_daemon_t **p_daemon)
+{
+	bus_daemon_t *daemon;
+	int i;
+
+	if (n_url == 0)
+		return -1;
+	if (nthread <= 0) {
+		SYSTEM_INFO si;
+		GetSystemInfo(&si);
+		nthread = min(MAX_THREADS, si.dwNumberOfProcessors << 1);
+	}
+	WLog_DBG(TAG, "thread num: %d", nthread);
+	daemon = MALLOC_T(bus_daemon_t);
+	memset(daemon, 0, sizeof(bus_daemon_t));
+	daemon->nthread = nthread;
+	daemon->arr_acceptor = array_make(n_url, sizeof(daemon_accetpor_t*));
+	daemon->arr_thread = array_make(nthread, sizeof(HANDLE));
+	daemon->arr_uri = array_make(n_url, sizeof(char*));
+	for (i = 0; i < n_url; ++i) {
+		WLog_DBG(TAG, "urls[%d]: %s", i, urls[i]);
+		ARRAY_PUSH(daemon->arr_uri, char*) = _strdup(urls[i]);
+	}
+	InitializeCriticalSection(&daemon->lock);
+	INIT_LIST_HEAD(&daemon->registered_session_list);
+	INIT_LIST_HEAD(&daemon->unregistered_session_list);
+
+	*p_daemon = daemon;
+	return 0;
+}
+
+TOOLKIT_API int bus_daemon_destroy(bus_daemon_t *daemon)
+{
+	int i;
+
+	DeleteCriticalSection(&daemon->lock);
+	for (i = 0; i < daemon->arr_uri->nelts; ++i)
+		free(ARRAY_IDX(daemon->arr_uri, i, char*));
+	array_free(daemon->arr_uri);
+	array_free(daemon->arr_acceptor);
+	array_free(daemon->arr_thread);
+	free(daemon);
+
+	return 0;
+}
+
+TOOLKIT_API int bus_daemon_start(bus_daemon_t *daemon)
+{
+	int i;
+	daemon->ioq = ioqueue_create();
+	if (!daemon->ioq) {
+		WLog_ERR(TAG, "ioqueuq create failed!");
+		return -1;
+	}
+	ioqueue_msg_add_handler(daemon->ioq, MSG_REMOVE_REGISTAR, 0, &on_msg);
+	for (i = 0; i < daemon->arr_uri->nelts; ++i) {
+		char *url = ARRAY_IDX(daemon->arr_uri, i, char*);
+		daemon_accetpor_t *dacceptor = create_daemon_acceptor(daemon, url);
+		if (dacceptor) {
+			int kk;
+			for (kk = 0; kk < DEFAULT_ACCEPT_OP_COUNT; ++kk)		//ÊýÁ¿5£¿
+				start_accept(dacceptor, kk);
+			ARRAY_PUSH(daemon->arr_acceptor, daemon_accetpor_t*) = dacceptor;
+		} else {
+			WLog_ERR(TAG, "create daemon acceptor failed!");
+			return -1;
+		}
+	}
+	daemon->lstop = 0;
+	for (i = 0; i < daemon->nthread; ++i) {
+		HANDLE thread = (HANDLE)_beginthreadex(NULL, 0, &thread_proc, daemon, 0, NULL);
+		if (thread) {
+			ARRAY_PUSH(daemon->arr_thread, HANDLE) = thread;
+		} else {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+TOOLKIT_API int bus_daemon_stop(bus_daemon_t *daemon)
+{
+	int i;
+
+	// exit all worker thread
+	InterlockedExchange(&daemon->lstop, 1);
+	for (i = 0; i < daemon->arr_thread->nelts; ++i) {
+		HANDLE t = ARRAY_IDX(daemon->arr_thread, i, HANDLE);
+		WaitForSingleObject(t, INFINITE);
+		CloseHandle(t);
+	}
+	array_clear(daemon->arr_thread);
+
+	// close all pending handles
+	{
+		int i;
+		endpt_session_t *pos;
+		for (i = 0; i < daemon->arr_acceptor->nelts; ++i) {
+			daemon_accetpor_t *dacceptor = ARRAY_IDX(daemon->arr_acceptor, i, daemon_accetpor_t*);
+			if (dacceptor->type == TYPE_PIPE) {
+				ioqueue_pipe_acceptor_close_pending_handle(&dacceptor->pipe_acceptor);
+			} else if (dacceptor->type == TYPE_TCP) {
+				ioqueue_acceptor_close(&dacceptor->tcp_acceptor);
+			}
+			daemon_accetpor_dec_ref(dacceptor);
+		}
+		array_clear(daemon->arr_acceptor);
+		list_for_each_entry(pos, &daemon->registered_session_list, endpt_session_t, entry) {
+			if (pos->type == TYPE_PIPE) {
+				ioqueue_file_close(&pos->pipe);
+			} else if (pos->type == TYPE_TCP) {
+				ioqueue_tcpsock_close(&pos->tcp);
+			}
+		}
+		list_for_each_entry(pos, &daemon->unregistered_session_list, endpt_session_t, entry) {
+			if (pos->type == TYPE_PIPE) {
+				ioqueue_file_close(&pos->pipe);
+			} else if (pos->type == TYPE_TCP) {
+				ioqueue_tcpsock_close(&pos->tcp);
+			}
+		}
+	}
+
+	// poll until all pending io are aborted
+	while (!ioqueue_can_exit(daemon->ioq)) {
+		ioqueue_poll(daemon->ioq, 10);
+	}
+
+	ioqueue_destroy(daemon->ioq);
+
+	return 0;
+}
+

+ 7 - 0
libtoolkit/config.h

@@ -40,6 +40,13 @@
 #endif //u__int64_t
 #endif
 
+#include <winpr/wtypes.h>
+#ifndef TOOLKIT_TAG
+#include <winpr/wlog.h>
+#define RVC_TAG(tag) "rvc." tag
+#define TOOLKIT_TAG(tag) RVC_TAG("libtoolkit.") tag
+#endif //TOOLKIT_TAG
+
 #ifdef __cplusplus
 extern "C" {
 #endif

+ 2532 - 0
libtoolkit/ioqueue-unix.c

@@ -0,0 +1,2532 @@
+#include "precompile.h"
+#include "ioqueue.h"
+#include "timerqueue.h"
+#include "memutil.h"
+#include "refcnt.h"
+#include "strutil.h"
+#include "sockutil.h"
+
+#ifdef _WIN32
+#include <MSWSock.h>
+#endif //_WIN32
+#include <winpr/file.h>
+#include <winpr/handle.h>
+#include <winpr/synch.h>
+#include <winpr/pipe.h>
+#include <winpr/string.h>
+
+
+#ifndef SO_UPDATE_CONNECT_CONTEXT
+#define SO_UPDATE_CONNECT_CONTEXT 0x7010
+#endif
+
+#ifndef WSAID_CONNECTEX
+#define WSAID_CONNECTEX \
+	{0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}}
+#endif
+
+#define MT_TTL						10*60*1000 /* maintenance 10 minutes */
+#define MT_INTERVAL					30*1000 /* maintenance 30 seconds */
+
+/* The address specified in AcceptEx() must be 16 more than the size of
+ * SOCKADDR (source: MSDN).
+ */
+#define ACCEPT_ADDR_LEN	(16+sizeof(SOCKADDR))
+
+struct ioqueue_t {
+	HANDLE iocp;
+	void *user_data;
+	/* timer */
+	spinlock_t tm_queue_lock;
+	timer_queue_t *tm_queue;
+	/* msg handler */
+	ioqueue_on_msg_callback msg_handlers[MAX_MSG][MAX_MSG_PRIORITY];
+	LONG msg_cnt;
+	/* connect */
+	spinlock_t connect_list_lock;
+	struct list_head connect_list;
+	spinlock_t handler_list_lock;
+	struct list_head handler_list;
+	LONG stop;
+};
+
+typedef struct ioqueue_msg {
+	int msg_type;
+	int param1;
+	int param2;
+	HANDLE evt; /* for send message */
+}ioqueue_msg;
+
+#define HANDLE_TYPE_ACCEPTOR		    0x01
+#define HANDLE_TYPE_TCPSOCK			    0x02
+#define HANDLE_TYPE_UDPSOCK			0x03
+#define HANDLE_TYPE_FILE			            0x04
+#define HANDLE_TYPE_PIPEACCEPTOR	0x05
+
+#define OV_ACCEPT			        0x01
+#define OV_CONNECT			    0x02
+#define OV_SENDSOME			0x03
+#define OV_SENDN			        0x04
+#define OV_RECVSOME			0x05
+#define OV_RECVN			        0x06
+#define OV_SENDTO			    0x07
+#define OV_RECVFROM			0x08
+#define OV_READFILESOME		0x09
+#define OV_WRITEFILESOME	0x0a
+#define OV_READFILEN		    0x0b
+#define OV_WRITEFILEN		    0x0c
+#define OV_RECVUNTIL		    0x0d
+#define OV_CONNECTPIPE		0x0e
+
+typedef struct ioqueue_base_overlapped_t {
+	OVERLAPPED ov;
+	int type;
+	void *user_data;
+	struct list_head pending_entry;
+	ioqueue_handle_context *handle_ctx;
+}ioqueue_base_overlapped_t;
+
+typedef struct ioqueue_accept_overlapped_t {
+	ioqueue_base_overlapped_t base;
+	SOCKET client;
+	ioqueue_on_accept_callback on_accept_callback;
+	char accept_buf[2*ACCEPT_ADDR_LEN];
+}ioqueue_accept_overlapped_t;
+
+typedef struct ioqueue_connect_overlapped_t {
+	ioqueue_base_overlapped_t base;
+	ioqueue_on_connect_callback on_connect_callback;
+	struct list_head node;
+	HANDLE hevt;
+}ioqueue_connect_overlapped_t;
+
+typedef struct ioqueue_sendsome_overlapped_t {
+	ioqueue_base_overlapped_t base;
+	ioqueue_on_send_callback on_send_callback;
+	WSABUF wsabuf;
+}ioqueue_sendsome_overlapped_t;
+
+typedef struct ioqueue_sendn_overlapped_t {
+	ioqueue_base_overlapped_t base;
+	ioqueue_on_send_callback on_send_callback;
+	WSABUF wsabuf;
+	char *original_buf;
+	unsigned int sended_bytes;
+	unsigned int total_bytes;
+}ioqueue_sendn_overlapped_t;
+
+typedef struct ioqueue_recvsome_overlapped_t {
+	ioqueue_base_overlapped_t base;
+	ioqueue_on_recv_callback on_recv_callback;
+	WSABUF wsabuf;
+	DWORD dwFlags;
+}ioqueue_recvsome_overlapped_t;
+
+typedef struct ioqueue_recvn_overlapped_t {
+	ioqueue_base_overlapped_t base;
+	ioqueue_on_recv_callback on_recv_callback;
+	WSABUF wsabuf;
+	char *original_buf;
+	unsigned int recved_bytes;
+	unsigned int total_bytes;
+	DWORD dwFlags;
+}ioqueue_recvn_overlapped_t;
+
+typedef struct ioqueue_recvuntil_overlapped_t {
+	ioqueue_base_overlapped_t base;
+	ioqueue_on_recvuntil_callback on_recvuntil_callback;
+	WSABUF wsabuf;
+	char *original_buf;
+	char *delimer;
+	unsigned int recved_bytes;
+	unsigned int total_bytes;
+	DWORD dwFlags;
+}ioqueue_recvuntil_overlapped_t;
+
+typedef struct ioqueue_sendto_overlapped_t {
+	ioqueue_base_overlapped_t base;
+	ioqueue_on_sendto_callback on_sendto_callback;
+	WSABUF wsabuf;
+}ioqueue_sendto_overlapped_t;
+
+typedef struct ioqueue_recvfrom_overlapped_t {
+	ioqueue_base_overlapped_t base;
+	ioqueue_on_recvfrom_callback on_recvfrom_callback;
+	WSABUF wsabuf;
+	struct sockaddr_in peer;
+	int addrlen;
+	DWORD dwFlags;
+}ioqueue_recvfrom_overlapped_t;
+
+typedef struct ioqueue_readfilesome_overlapped_t {
+	ioqueue_base_overlapped_t base;
+	ioqueue_on_read_callback on_read_callback;
+	char *buf;
+	HANDLE hevt;
+}ioqueue_readfilesome_overlapped_t;
+
+typedef struct ioqueue_readfilen_overlapped_t {
+	ioqueue_base_overlapped_t base;
+	ioqueue_on_read_callback on_read_callback;
+	char *buf;
+	HANDLE hevt;
+	unsigned int recved_bytes;
+	unsigned int total_bytes;
+}ioqueue_readfilen_overlapped_t;
+
+typedef struct ioqueue_writefilesome_overlapped_t {
+	ioqueue_base_overlapped_t base;
+	ioqueue_on_write_callback on_write_callback;
+	HANDLE hevt;
+	char *buf;
+}ioqueue_writefilesome_overlapped_t;
+
+typedef struct ioqueue_writefilen_overlapped_t {
+	ioqueue_base_overlapped_t base;
+	ioqueue_on_write_callback on_write_callback;
+	char *buf;
+	HANDLE hevt;
+	unsigned int sended_bytes;
+	unsigned int total_bytes;
+}ioqueue_writefilen_overlapped_t;
+
+typedef struct ioqueue_connectpipe_overlapped_t {
+	ioqueue_base_overlapped_t base;
+	HANDLE client;
+	HANDLE hevt;
+	ioqueue_on_pipe_accept_callback on_accept_callback;
+}ioqueue_connectpipe_overlapped_t;
+
+static int reuse_addr(SOCKET sock)
+{
+	BOOL reuseaddr = 1;
+	return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&reuseaddr, sizeof(reuseaddr));
+}
+
+static int nonblock_sock(SOCKET sock)
+{
+	unsigned long v = 1;
+	return _ioctlsocket(sock, FIONBIO, &v);
+}
+
+static int is_os_gte_xp() /* is os version greater and equal than xp */
+{
+	static int yes = -1;
+#ifdef _WIN32
+	if (yes == -1) {
+		OSVERSIONINFO ver;
+		ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+		GetVersionEx(&ver);
+		if (ver.dwMajorVersion > 5 || (ver.dwMajorVersion == 5 && ver.dwMinorVersion > 0)) {
+			yes = 1;
+		}
+	}
+#endif//_WIN32
+	return yes;
+}
+
+static __inline LONG inc_msg_cnt(ioqueue_t *ioq) 
+{ 
+	return InterlockedIncrement(&ioq->msg_cnt); 
+}
+
+static __inline LONG dec_msg_cnt(ioqueue_t *ioq) 
+{ 
+	return InterlockedDecrement(&ioq->msg_cnt); 
+}
+
+static __inline void add_handler_list(ioqueue_handle_context *handle_ctx, ioqueue_t *ioq)
+{
+	spinlock_enter(&ioq->handler_list_lock, -1);
+	list_add(&handle_ctx->node, &ioq->handler_list);
+	spinlock_leave(&ioq->handler_list_lock);
+}
+
+static __inline void del_handler_list(ioqueue_handle_context *handle_ctx, ioqueue_t *ioq)
+{
+	if (handle_ctx->node.next) {
+		spinlock_enter(&ioq->handler_list_lock, -1);
+		list_del(&handle_ctx->node);
+		handle_ctx->node.next = handle_ctx->node.prev = NULL;
+		spinlock_leave(&ioq->handler_list_lock);
+	}
+}
+
+static void ioqueue_handle_context_free(ioqueue_handle_context *handle_ctx)
+{
+	if (handle_ctx->type == HANDLE_TYPE_UDPSOCK 
+		|| handle_ctx->type == HANDLE_TYPE_TCPSOCK 
+		|| handle_ctx->type == HANDLE_TYPE_ACCEPTOR) {
+		if (handle_ctx->u.sock != INVALID_SOCKET) {
+			closesocket(handle_ctx->u.sock);
+			handle_ctx->u.sock = INVALID_SOCKET;
+		}
+	} else if (handle_ctx->type == HANDLE_TYPE_FILE) {
+		if (handle_ctx->u.file != INVALID_HANDLE_VALUE) {
+			CloseHandle(handle_ctx->u.file);
+			handle_ctx->u.file = INVALID_HANDLE_VALUE;
+		}
+	} else if (handle_ctx->type == HANDLE_TYPE_PIPEACCEPTOR) {
+		if (handle_ctx->u.pipe_name) {
+			free(handle_ctx->u.pipe_name);
+			handle_ctx->u.pipe_name = NULL;
+		}
+	} else {
+		assert(0);
+		return;
+	}
+	del_handler_list(handle_ctx, handle_ctx->owner);
+}
+
+IMPLEMENT_REF_COUNT_MT(ioqueue_handle_context, ioqueue_handle_context, pending_ios, ioqueue_handle_context_free)
+
+static __inline LONG inc_pending_io(ioqueue_handle_context *handle_ctx)
+{
+	return inc_ref(ioqueue_handle_context, handle_ctx);
+}
+
+static __inline LONG dec_pending_io(ioqueue_handle_context *handle_ctx)
+{
+	return dec_ref(ioqueue_handle_context, handle_ctx);
+}
+
+static SOCKET new_socket()
+{
+	SOCKET sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 
+		NULL, 0, WSA_FLAG_OVERLAPPED);
+	if (sock != INVALID_SOCKET) {
+		reuse_addr(sock);
+	}
+	return sock;
+}
+
+static void delete_socket(SOCKET sock)
+{
+	LINGER l;
+	l.l_onoff = 1;
+	l.l_linger = 0;
+	setsockopt(sock, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l));
+	closesocket(sock);
+}
+
+TOOLKIT_API ioqueue_t *ioqueue_create()
+{
+	ioqueue_t *ioq = ZALLOC_T(ioqueue_t);
+	if (!ioq)
+		return NULL;
+	ioq->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
+	if (!ioq->iocp)
+		goto on_error_0;
+	if (timer_heap_create(&ioq->tm_queue) != 0)
+		goto on_error_3;
+	spinlock_init(&ioq->tm_queue_lock);
+	spinlock_init(&ioq->connect_list_lock);
+	INIT_LIST_HEAD(&ioq->connect_list);
+	spinlock_init(&ioq->handler_list_lock);
+	INIT_LIST_HEAD(&ioq->handler_list);
+	///ioq->stop = 0;
+	//ioq->msg_cnt = 0;
+	return ioq;
+
+on_error_3:
+	
+	CloseHandle(ioq->iocp);
+on_error_0:
+	free(ioq);
+	return NULL;
+}
+
+TOOLKIT_API void ioqueue_destroy(ioqueue_t *ioq)
+{
+	assert(ioq);
+	assert(ioqueue_handler_empty(ioq));
+	assert(ioqueue_msg_empty(ioq));
+	timer_queue_destroy(ioq->tm_queue);
+	CloseHandle(ioq->iocp);
+	free(ioq);
+}
+
+TOOLKIT_API int ioqueue_handler_empty(ioqueue_t *ioq)
+{
+	int ret;
+	assert(ioq);
+	spinlock_enter(&ioq->handler_list_lock, -1);
+	ret = list_empty(&ioq->handler_list);
+	spinlock_leave(&ioq->handler_list_lock);
+	return ret;
+}
+
+TOOLKIT_API int ioqueue_msg_empty(ioqueue_t *ioq)
+{
+	assert(ioq);
+	return ioq->msg_cnt == 0;
+}
+
+TOOLKIT_API int ioqueue_msg_add_handler(ioqueue_t *ioq, int msg_type, int priority, ioqueue_on_msg_callback cb)
+{
+	assert(ioq);
+	assert(cb);
+	assert(msg_type >= 0 && msg_type <= MAX_MSG);
+	assert(priority >= 0 && priority <= MAX_MSG_PRIORITY);
+	ioq->msg_handlers[msg_type][priority] = cb;
+	return 0;
+}
+
+TOOLKIT_API int ioqueue_msg_remove_handler(ioqueue_t *ioq, int msg_type, int priority)
+{
+	assert(ioq);
+	assert(msg_type >= 0 && msg_type <= MAX_MSG);
+	assert(priority >= 0 && priority <= MAX_MSG_PRIORITY);
+	ioq->msg_handlers[msg_type][priority] = NULL;
+	return 0;
+}
+
+static void dispatch_acceptor(int err, 
+							  DWORD dwBytesTransfer, 
+							  ioqueue_acceptor_t *acceptor, 
+							  ioqueue_accept_overlapped_t *overlapped)
+{
+	ioqueue_t *ioq = acceptor->owner;
+
+	if (err == 0) {
+		/* only valid for winxp or later, ignore return value */
+		setsockopt(overlapped->client, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, 
+			(char*)&acceptor->u.sock, sizeof(SOCKET));
+	} else {
+		delete_socket(overlapped->client);
+		overlapped->client = INVALID_SOCKET;
+	}
+	{
+	SOCKET s = overlapped->client;
+	int accepted = overlapped->on_accept_callback(acceptor, (ioqueue_overlapped_t*)overlapped, s, 
+		overlapped->base.user_data, err);
+	if (!err && !accepted && s != INVALID_SOCKET)
+		delete_socket(s);
+	}
+}
+
+static void dispatch_pipe_acceptor(int err, 
+								   DWORD dwBytesTransfer, 
+								   ioqueue_pipe_acceptor_t *acceptor, 
+								   ioqueue_connectpipe_overlapped_t *overlapped)
+{
+	ioqueue_t *ioq = acceptor->owner;
+	int accepted;
+
+	CloseHandle(overlapped->hevt);
+	overlapped->hevt = NULL;
+
+	if (!err && overlapped->client == INVALID_HANDLE_VALUE)
+		err = -1;
+
+	if (err) {
+		if (overlapped->client != INVALID_HANDLE_VALUE) {
+			CloseHandle(overlapped->client);
+			overlapped->client = INVALID_HANDLE_VALUE;
+		}
+	}
+
+	accepted = overlapped->on_accept_callback(acceptor, 
+		(ioqueue_overlapped_t*)overlapped, 
+		overlapped->client,
+		overlapped->base.user_data, err);
+	if (!err && !accepted && overlapped->client != INVALID_HANDLE_VALUE) {
+		CloseHandle(overlapped->client);
+	}
+}
+
+static void dispatch_network(BOOL ret, DWORD dwBytesTransfer, ioqueue_overlapped_t *io_ctx)
+{
+	int err = ret ? 0 : -1;
+	ioqueue_base_overlapped_t *base_ov = (ioqueue_base_overlapped_t*)io_ctx;
+	ioqueue_handle_context *handle_ctx = base_ov->handle_ctx;
+
+	fastlock_enter(handle_ctx->ov_pending_list_lock);
+	list_del(&base_ov->pending_entry);
+	fastlock_leave(handle_ctx->ov_pending_list_lock);
+	dec_pending_io(handle_ctx);
+
+	switch (handle_ctx->type) {
+		case HANDLE_TYPE_ACCEPTOR:
+			dispatch_acceptor(err, dwBytesTransfer, handle_ctx, (ioqueue_accept_overlapped_t*)io_ctx);
+			break;
+		case HANDLE_TYPE_PIPEACCEPTOR:
+			dispatch_pipe_acceptor(err, dwBytesTransfer, handle_ctx, (ioqueue_connectpipe_overlapped_t*)io_ctx);
+			break;
+		case HANDLE_TYPE_TCPSOCK:
+		case HANDLE_TYPE_UDPSOCK:
+		case HANDLE_TYPE_FILE:
+			switch (base_ov->type) {
+				case OV_CONNECT: {
+						ioqueue_connect_overlapped_t *overlapped = (ioqueue_connect_overlapped_t*)io_ctx;
+						if (err == 0) {
+							setsockopt(handle_ctx->u.sock, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0);
+						}
+						overlapped->on_connect_callback(handle_ctx, io_ctx, base_ov->user_data, err);
+					}
+					break;
+				case OV_SENDSOME: {
+						ioqueue_sendsome_overlapped_t *overlapped = (ioqueue_sendsome_overlapped_t*)io_ctx;
+						overlapped->on_send_callback(handle_ctx, io_ctx, 
+							overlapped->wsabuf.buf, dwBytesTransfer, base_ov->user_data, err);
+					}
+					break;
+				case OV_SENDN: {
+						ioqueue_sendn_overlapped_t *overlapped = (ioqueue_sendn_overlapped_t*)io_ctx;
+						overlapped->sended_bytes += dwBytesTransfer;
+						if (err == 0 && overlapped->sended_bytes < overlapped->total_bytes) {
+							int rc;
+							DWORD bytesWritten;
+							overlapped->wsabuf.buf += dwBytesTransfer;
+							overlapped->wsabuf.len -= dwBytesTransfer;
+							inc_pending_io(handle_ctx);
+							overlapped->base.ov.Internal = 0;
+							overlapped->base.ov.InternalHigh = 0;
+							overlapped->base.ov.Offset = 0;
+							overlapped->base.ov.OffsetHigh = 0;
+							rc = WSASend(handle_ctx->u.sock, &overlapped->wsabuf, 1, &bytesWritten, 
+								0, &overlapped->base.ov, NULL);
+							if (rc != 0 && WSAGetLastError() != WSA_IO_PENDING) {
+								dec_pending_io(handle_ctx);
+								overlapped->on_send_callback(handle_ctx, io_ctx, overlapped->original_buf, 
+									overlapped->sended_bytes, base_ov->user_data, -1);
+							}
+						} else {
+							overlapped->on_send_callback(handle_ctx, io_ctx, overlapped->original_buf, 
+								overlapped->sended_bytes, base_ov->user_data, err);
+						}
+					}
+					break;
+				case OV_RECVSOME: {
+						ioqueue_recvsome_overlapped_t *overlapped = (ioqueue_recvsome_overlapped_t*)io_ctx;
+						overlapped->on_recv_callback(handle_ctx, io_ctx, overlapped->wsabuf.buf, 
+							dwBytesTransfer, base_ov->user_data, err);
+					}
+					break;
+				case OV_RECVUNTIL: {
+						ioqueue_recvuntil_overlapped_t *overlapped = (ioqueue_recvuntil_overlapped_t*)io_ctx;
+						if (err == 0 && dwBytesTransfer) {
+							const char *pos;
+							overlapped->recved_bytes += dwBytesTransfer;
+							pos = memstr(overlapped->original_buf, overlapped->recved_bytes, overlapped->delimer);
+							if (pos) {
+								free(overlapped->delimer);
+								overlapped->on_recvuntil_callback(handle_ctx, io_ctx, overlapped->original_buf,
+									overlapped->recved_bytes, (int)strlen(overlapped->delimer)+(int)(pos-overlapped->original_buf),
+									base_ov->user_data, err);
+							} else if (overlapped->recved_bytes < overlapped->total_bytes) {
+								DWORD bytesRead;
+								int rc;
+								overlapped->wsabuf.buf += dwBytesTransfer;
+								overlapped->wsabuf.len -= dwBytesTransfer;
+								inc_pending_io(handle_ctx);
+								overlapped->base.ov.Internal = 0;
+								overlapped->base.ov.InternalHigh = 0;
+								overlapped->base.ov.Offset = 0;
+								overlapped->base.ov.OffsetHigh = 0;
+								overlapped->dwFlags = 0;
+								rc = WSARecv(handle_ctx->u.sock, &overlapped->wsabuf, 1, &bytesRead, &overlapped->dwFlags, 
+									&overlapped->base.ov, NULL);
+								if (rc != 0 && WSAGetLastError() != WSA_IO_PENDING) {
+									dec_pending_io(handle_ctx);
+									free(overlapped->delimer);
+									overlapped->on_recvuntil_callback(handle_ctx, io_ctx, overlapped->original_buf,
+										overlapped->recved_bytes, 0, base_ov->user_data, -1);
+								}
+							} else {
+								free(overlapped->delimer);
+								overlapped->on_recvuntil_callback(handle_ctx, io_ctx, overlapped->original_buf,
+										overlapped->recved_bytes, 0, base_ov->user_data, -1);
+							}
+						} else {
+							free(overlapped->delimer);
+							overlapped->on_recvuntil_callback(handle_ctx, io_ctx, overlapped->original_buf,
+								overlapped->recved_bytes, 0, base_ov->user_data, err);
+						}
+					}
+					break;
+				case OV_RECVN: {
+						ioqueue_recvn_overlapped_t *overlapped = (ioqueue_recvn_overlapped_t*)io_ctx;
+						overlapped->recved_bytes += dwBytesTransfer;
+						if (err == 0 && overlapped->recved_bytes < overlapped->total_bytes) {
+							int rc;
+							DWORD bytesRead;
+							overlapped->wsabuf.buf += dwBytesTransfer;
+							overlapped->wsabuf.len -= dwBytesTransfer;
+							inc_pending_io(handle_ctx);
+							overlapped->base.ov.Internal = 0;
+							overlapped->base.ov.InternalHigh = 0;
+							overlapped->base.ov.Offset = 0;
+							overlapped->base.ov.OffsetHigh = 0;
+							overlapped->dwFlags = 0;
+							rc = WSARecv(handle_ctx->u.sock, &overlapped->wsabuf, 1, &bytesRead, &overlapped->dwFlags, 
+								&overlapped->base.ov, NULL);
+							if (rc != 0 && WSAGetLastError() != WSA_IO_PENDING) {
+								dec_pending_io(handle_ctx);
+								overlapped->on_recv_callback(handle_ctx, io_ctx, overlapped->original_buf, 
+									overlapped->recved_bytes, base_ov->user_data, -1);
+							}
+						} else {
+							overlapped->on_recv_callback(handle_ctx, io_ctx, overlapped->original_buf, 
+								overlapped->recved_bytes, base_ov->user_data, err);
+						}
+					}
+					break;
+				case OV_SENDTO: {
+						ioqueue_sendto_overlapped_t *overlapped = (ioqueue_sendto_overlapped_t*)io_ctx;
+						overlapped->on_sendto_callback(handle_ctx, io_ctx, overlapped->wsabuf.buf,
+							dwBytesTransfer, base_ov->user_data, err);
+					}
+					break;
+				case OV_RECVFROM: {
+						ioqueue_recvfrom_overlapped_t *overlapped = (ioqueue_recvfrom_overlapped_t*)io_ctx;
+						overlapped->on_recvfrom_callback(handle_ctx, io_ctx, (struct sockaddr*)&overlapped->peer, 
+							overlapped->addrlen, overlapped->wsabuf.buf, dwBytesTransfer, base_ov->user_data, err);
+					}
+					break;
+				case OV_READFILESOME: {
+						ioqueue_readfilesome_overlapped_t *overlapped = (ioqueue_readfilesome_overlapped_t*)io_ctx;
+						CloseHandle(overlapped->hevt);
+						overlapped->hevt = NULL;
+						overlapped->on_read_callback(handle_ctx, io_ctx, overlapped->buf,
+							dwBytesTransfer, base_ov->user_data, err);
+					}
+					break;
+				case OV_READFILEN: {
+						ioqueue_readfilen_overlapped_t *overlapped = (ioqueue_readfilen_overlapped_t*)io_ctx;
+						CloseHandle(overlapped->hevt);
+						overlapped->hevt = NULL;
+						overlapped->recved_bytes += dwBytesTransfer;
+						if (err == 0 && overlapped->recved_bytes < overlapped->total_bytes) {
+							BOOL ret;
+							DWORD left = overlapped->total_bytes - overlapped->recved_bytes;
+							inc_pending_io(handle_ctx);
+							overlapped->base.ov.Internal = 0;
+							overlapped->base.ov.InternalHigh = 0;
+							overlapped->base.ov.Offset += dwBytesTransfer;
+							if (overlapped->base.ov.Offset < dwBytesTransfer)
+								overlapped->base.ov.OffsetHigh += 1;
+							ret = ReadFile(handle_ctx->u.file, overlapped->buf+overlapped->recved_bytes, left, NULL, &overlapped->base.ov);
+							if (!ret && GetLastError() != ERROR_IO_PENDING) {
+								dec_pending_io(handle_ctx);
+								overlapped->on_read_callback(handle_ctx, io_ctx, overlapped->buf, 
+									overlapped->recved_bytes, base_ov->user_data, -1);
+							}
+						} else {
+							overlapped->on_read_callback(handle_ctx, io_ctx, overlapped->buf, 
+								overlapped->recved_bytes, base_ov->user_data, err);
+						}
+					}
+					break;
+				case OV_WRITEFILESOME: {
+						ioqueue_writefilesome_overlapped_t *overlapped = (ioqueue_writefilesome_overlapped_t*)io_ctx;
+						CloseHandle(overlapped->hevt);
+						overlapped->hevt = NULL;
+						overlapped->on_write_callback(handle_ctx, io_ctx, overlapped->buf, dwBytesTransfer, 
+							base_ov->user_data, err);
+					}
+					break;
+				case OV_WRITEFILEN: {
+						ioqueue_writefilen_overlapped_t *overlapped = (ioqueue_writefilen_overlapped_t*)io_ctx;
+						CloseHandle(overlapped->hevt);
+						overlapped->hevt = NULL;
+						overlapped->sended_bytes += dwBytesTransfer;
+						if (err == 0 && overlapped->sended_bytes < overlapped->total_bytes) {
+							BOOL ret;
+							DWORD left = overlapped->total_bytes - overlapped->sended_bytes;
+							inc_pending_io(handle_ctx);
+							overlapped->base.ov.Internal = 0;
+							overlapped->base.ov.InternalHigh = 0;
+							overlapped->base.ov.Offset += dwBytesTransfer;
+							if (overlapped->base.ov.Offset < dwBytesTransfer)
+								overlapped->base.ov.OffsetHigh += 1;
+							ret = WriteFile(handle_ctx->u.file, overlapped->buf+overlapped->sended_bytes, left, NULL, &overlapped->base.ov);
+							if (!ret && GetLastError() != ERROR_IO_PENDING) {
+								dec_pending_io(handle_ctx);
+								overlapped->on_write_callback(handle_ctx, io_ctx, overlapped->buf, 
+									overlapped->sended_bytes, base_ov->user_data, -1);
+							}
+						} else {
+							overlapped->on_write_callback(handle_ctx, io_ctx, overlapped->buf, 
+								overlapped->sended_bytes, base_ov->user_data, err);
+						}
+					}
+					break;
+				default:
+					assert(0);
+					break;
+			}
+			break;
+		default:
+			assert(0);
+			break;
+	}
+}
+
+static void dispatch_msg(ioqueue_t *ioq, int msg_type, int param1, int param2, HANDLE evt)
+{
+	int chain = 1, i;
+	for (i = 0; chain && i < MAX_MSG_PRIORITY; ++i) {
+		ioqueue_on_msg_callback cb = ioq->msg_handlers[msg_type][i];
+		if (cb) 
+			chain = cb(msg_type, param1, param2);
+	}
+	if (evt) 
+		SetEvent(evt);
+}
+
+TOOLKIT_API int ioqueue_post_message(ioqueue_t *ioq, int msg_type, int param1, int param2)
+{
+	ioqueue_msg *msg;
+	assert(ioq);
+	msg = MALLOC_T(ioqueue_msg);
+	msg->msg_type = msg_type;
+	msg->param1 = param1;
+	msg->param2 = param2;
+	msg->evt = NULL;
+	inc_msg_cnt(ioq);
+	if (!PostQueuedCompletionStatus(ioq->iocp, 0, (ULONG_PTR)msg, NULL)) {
+		dec_msg_cnt(ioq);
+		free(msg);
+		return -1;
+	}
+	return 0;
+}
+
+TOOLKIT_API int ioqueue_send_message(ioqueue_t *ioq, int msg_type, int param1, int param2)
+{
+	ioqueue_msg msg = {msg_type, param1, param2};
+	assert(ioq);
+	msg.evt = CreateEventA(NULL, TRUE, FALSE, NULL);
+	inc_msg_cnt(ioq);
+	if (!PostQueuedCompletionStatus(ioq->iocp, 0, (ULONG_PTR)&msg, NULL)) {
+		CloseHandle(msg.evt);
+		dec_msg_cnt(ioq);
+		return -1;
+	}
+	WaitForSingleObject(msg.evt, INFINITE);
+	CloseHandle(msg.evt);
+	dec_msg_cnt(ioq);
+	return 0;
+}
+
+static int poll_all_events(ioqueue_t *ioq, HANDLE *hevts, ioqueue_connect_overlapped_t **ovs, int i)
+{
+	int count = 0;
+	int left = i;
+	int pos = 0;
+	while (left > 0) {
+		DWORD idx = WaitForMultipleObjects(left, &hevts[pos], FALSE, 0) - WAIT_OBJECT_0;
+		if (idx <= (DWORD)left && idx >= 0) {
+			WSANETWORKEVENTS net_events;
+			ioqueue_connect_overlapped_t *triggered = ovs[idx+pos];
+			list_del(&triggered->node);
+			WSAEnumNetworkEvents(triggered->base.handle_ctx->u.sock, hevts[idx+pos], &net_events);
+			WSAEventSelect(triggered->base.handle_ctx->u.sock, hevts[idx+pos], 0);
+			CloseHandle(hevts[idx+pos]);
+			triggered->on_connect_callback(triggered->base.handle_ctx, (ioqueue_overlapped_t*)triggered, 
+				triggered->base.user_data, net_events.iErrorCode[FD_CONNECT_BIT] == 0 ? 0 : -1);
+			left -= (int)idx + 1;
+			pos += idx+1;
+			count ++;
+		} else {
+			break;
+		}
+	}
+	return count;
+}
+
+static int poll_connect_list(ioqueue_t *ioq)
+{
+	int count = 0, i = 0;
+	HANDLE hevts[MAXIMUM_WAIT_OBJECTS];
+	ioqueue_connect_overlapped_t *ovs[MAXIMUM_WAIT_OBJECTS];
+	ioqueue_connect_overlapped_t *pos, *n;
+
+	list_for_each_entry_safe(pos, n, &ioq->connect_list, ioqueue_connect_overlapped_t, node) {
+		hevts[i] = pos->hevt;
+		ovs[i] = pos;
+		i++;
+		if (i == MAXIMUM_WAIT_OBJECTS) {
+			count += poll_all_events(ioq, hevts, ovs, i);
+			i = 0;
+		}
+	}
+	if (i > 0) {
+		count += poll_all_events(ioq, hevts, ovs, i);
+	}
+	return count;
+}
+
+TOOLKIT_API int ioqueue_poll(ioqueue_t* q, int timeout)
+{
+	ioqueue_t *ioq = (ioqueue_t*)q;
+	int count = 0, t = 0;
+
+	/* network and msg, dispatch until no events */
+	do 
+	{
+		BOOL ret;
+		ULONG_PTR iocp_key = 0;
+		LPOVERLAPPED iocp_pov = 0;
+		DWORD dwBytesTransfer = 0;
+		ret = GetQueuedCompletionStatus(ioq->iocp, &dwBytesTransfer, 
+			&iocp_key, &iocp_pov, t ? 0 : (DWORD)timeout);
+		if (iocp_pov) { /* network io */
+			ioqueue_overlapped_t *io_ctx = (ioqueue_overlapped_t*)iocp_pov;
+			dispatch_network(ret, dwBytesTransfer, io_ctx);
+			t++;
+			count ++;
+		} else if (ret && iocp_key && !iocp_pov) { /* msg */
+			ioqueue_msg *msg = (ioqueue_msg *)iocp_key;
+			int msg_type = msg->msg_type;
+			int param1 = msg->param1;
+			int param2 = msg->param2;
+			HANDLE evt = msg->evt;
+			if (!evt)
+				free(msg);
+			dispatch_msg(ioq, msg_type, param1, param2, evt);
+			dec_msg_cnt(ioq);
+			t++;
+			count ++;
+		} else {
+			t = 0;
+		}
+	} while (t > 0);
+
+	/* win2k connect event */
+	if (!is_os_gte_xp()) {
+		spinlock_enter(&ioq->connect_list_lock, -1);
+		poll_connect_list(ioq);
+		spinlock_leave(&ioq->connect_list_lock);
+	}
+
+	/* timer heap */
+	spinlock_enter(&ioq->tm_queue_lock, -1);
+	count += timer_queue_poll(ioq->tm_queue, NULL); /* dispatch timer heap */
+	spinlock_leave(&ioq->tm_queue_lock);
+
+	if (ioq->stop == -1) {
+		if (InterlockedCompareExchange(&ioq->stop, -2, -1) == -1) { /* close all handler */
+			ioqueue_handle_context *pos, *n;
+			spinlock_enter(&ioq->handler_list_lock, -1);
+			list_for_each_entry_safe(pos, n, &ioq->handler_list, ioqueue_handle_context, node) {
+				if (pos->type != HANDLE_TYPE_FILE) {
+					closesocket(pos->u.sock);
+					pos->u.sock = INVALID_SOCKET;
+				} else {
+					CloseHandle(pos->u.file);
+					pos->u.file = INVALID_HANDLE_VALUE;
+				}
+			}
+			spinlock_leave(&ioq->handler_list_lock);
+		}
+	}
+
+	return count;
+}
+
+TOOLKIT_API void ioqueue_stop(ioqueue_t *ioq)
+{
+	assert(ioq);
+	ioq->stop = -1;
+}
+
+/* timer */
+
+TOOLKIT_API int ioqueue_timer_schedule(ioqueue_t *ioq, timer_entry *entry, unsigned int delay)
+{
+	int err;
+	
+	assert(ioq);
+	assert(entry);
+
+	if (ioq->stop)
+		return -1;
+
+	spinlock_enter(&ioq->tm_queue_lock, -1);
+	err = timer_queue_schedule(ioq->tm_queue, entry, delay);
+	spinlock_leave(&ioq->tm_queue_lock);
+	return err;
+}
+
+TOOLKIT_API int ioqueue_timer_cancel(ioqueue_t *ioq, timer_entry *entry, int cancel)
+{
+	int err;
+	assert(ioq);
+	assert(entry);
+	spinlock_enter(&ioq->tm_queue_lock, -1);
+	err = timer_queue_cancel(ioq->tm_queue, entry, cancel);
+	spinlock_leave(&ioq->tm_queue_lock);
+	return err;
+}
+
+/* acceptor */
+
+TOOLKIT_API int ioqueue_acceptor_create(ioqueue_t *ioq, 
+							const char *ip, 
+							unsigned short port, 
+							ioqueue_acceptor_t* acceptor)
+{
+	struct sockaddr_in service = {0};
+
+	assert(ioq);
+	assert(acceptor);
+	assert(port);
+
+	if (ioq->stop)
+		return -1;
+
+	memset(acceptor, 0, sizeof(ioqueue_acceptor_t));
+	acceptor->u.sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
+	if (acceptor->u.sock == INVALID_SOCKET)
+		goto on_error;
+	nonblock_sock(acceptor->u.sock);
+	service.sin_family = AF_INET;
+	service.sin_port = htons(port);
+	service.sin_addr.s_addr = ip ? inet_addr(ip) : htonl(INADDR_ANY);
+	if (bind(acceptor->u.sock, (struct sockaddr*)&service, sizeof(struct sockaddr)) != 0)
+		goto on_error;
+	if (!CreateIoCompletionPort((HANDLE)acceptor->u.sock, ioq->iocp, 0, 0))
+		goto on_error;
+	acceptor->type = HANDLE_TYPE_ACCEPTOR;
+	acceptor->owner = ioq;
+	fastlock_init(acceptor->ov_pending_list_lock);
+	INIT_LIST_HEAD(&acceptor->ov_pending_list);
+	add_handler_list(acceptor, ioq);
+	inc_ref(ioqueue_handle_context, acceptor);
+
+	return 0;
+
+on_error:
+	if (acceptor->u.sock != INVALID_SOCKET)
+		closesocket(acceptor->u.sock);
+	return -1;
+}
+
+TOOLKIT_API int ioqueue_acceptor_listen(ioqueue_acceptor_t* acceptor, int backlog)
+{
+	assert(acceptor);
+	return listen(acceptor->u.sock, backlog);
+}
+
+TOOLKIT_API void ioqueue_acceptor_destroy(ioqueue_acceptor_t* acceptor)
+{
+	assert(acceptor);
+	dec_ref(ioqueue_handle_context, acceptor);
+}
+
+TOOLKIT_API void ioqueue_acceptor_close(ioqueue_acceptor_t* acceptor)
+{
+	SOCKET s;
+	assert(acceptor);
+	s = acceptor->u.sock;
+	if (s != INVALID_SOCKET) {
+		acceptor->u.sock = INVALID_SOCKET;
+		closesocket(s);
+	}
+}
+
+TOOLKIT_API int ioqueue_acceptor_async_accept(ioqueue_acceptor_t* acceptor, 
+								  ioqueue_overlapped_t *ov, 
+								  ioqueue_on_accept_callback on_accept_callback,
+								  void *user_data)
+{
+	ioqueue_t *ioq;
+	ioqueue_accept_overlapped_t *overlapped;
+	DWORD bytesTransfer;
+	BOOL ret;
+
+	assert(acceptor);
+	assert(ov);
+	assert(acceptor->type == HANDLE_TYPE_ACCEPTOR);
+	assert(on_accept_callback);
+
+	ioq = acceptor->owner;
+	if (ioq->stop)
+		return -1;
+
+	overlapped = (ioqueue_accept_overlapped_t*)ov;
+	memset(overlapped, 0, sizeof(ioqueue_accept_overlapped_t));
+	overlapped->client = new_socket();
+	if (overlapped->client == INVALID_SOCKET)
+		return -1;
+	fastlock_enter(acceptor->ov_pending_list_lock);
+	list_add_tail(&overlapped->base.pending_entry, &acceptor->ov_pending_list);
+	fastlock_leave(acceptor->ov_pending_list_lock);
+	overlapped->base.type = OV_ACCEPT;
+	overlapped->base.user_data = user_data;
+	overlapped->base.handle_ctx = acceptor;
+	inc_pending_io(acceptor);
+	overlapped->on_accept_callback = on_accept_callback;
+	ret = AcceptEx(acceptor->u.sock, overlapped->client, overlapped->accept_buf,
+		0, ACCEPT_ADDR_LEN, ACCEPT_ADDR_LEN, &bytesTransfer, &overlapped->base.ov);
+	if (ret || WSAGetLastError() == WSA_IO_PENDING)
+		return 0;
+#if 0
+	{
+		DWORD dwError = WSAGetLastError();
+		printf("dwError = %d\n", dwError);
+	}
+#endif
+	fastlock_enter(acceptor->ov_pending_list_lock);
+	list_del(&overlapped->base.pending_entry);
+	fastlock_leave(acceptor->ov_pending_list_lock);
+	dec_pending_io(acceptor);
+	delete_socket(overlapped->client);
+	return -1;
+}
+
+TOOLKIT_API int ioqueue_acceptor_accept(ioqueue_acceptor_t* acceptor, SOCKET *s, struct sockaddr *addr, int *addrlen, int timeout)
+{
+	struct timeval tm;
+	fd_set set;
+	fd_set ex_set;
+	int rc;
+
+	FD_ZERO(&set);
+	FD_ZERO(&ex_set);
+	FD_SET(acceptor->u.sock, &set);
+	FD_SET(acceptor->u.sock, &ex_set);
+
+	tm.tv_sec = timeout / 1000;
+	tm.tv_usec = 1000 * (timeout % 1000);
+
+	rc = select(acceptor->u.sock+1, &set, NULL, &ex_set, &tm);
+	if (rc > 0) {
+		if (FD_ISSET(acceptor->u.sock, &ex_set))
+			return -1;
+		if (FD_ISSET(acceptor->u.sock, &set)) {
+			SOCKET fd = accept(acceptor->u.sock, addr, addrlen);
+			if (fd != INVALID_SOCKET) {
+				*s = fd;
+				return 0;
+			}
+		}
+	}
+
+	return -1;
+}
+
+TOOLKIT_API int ioqueue_acceptor_create_client(ioqueue_acceptor_t* acceptor, SOCKET s, ioqueue_tcpsock_t *tcpsock)
+{
+	ioqueue_t *ioq;
+	assert(acceptor);
+	assert(tcpsock);
+	assert(s != INVALID_SOCKET);
+	ioq = acceptor->owner;
+	if (ioq->stop)
+		return -1;
+	memset(tcpsock, 0, sizeof(ioqueue_tcpsock_t));
+	tcpsock->type = HANDLE_TYPE_TCPSOCK;
+	tcpsock->u.sock = s;
+	tcpsock->owner = ioq;
+	tcpsock->user_data = NULL;
+	fastlock_init(tcpsock->ov_pending_list_lock);
+	INIT_LIST_HEAD(&tcpsock->ov_pending_list);
+	if (!CreateIoCompletionPort((HANDLE)s, ioq->iocp, 0, 0)) /* bind to iocp */
+		return -1;
+	add_handler_list(tcpsock, ioq);
+	inc_ref(ioqueue_handle_context, tcpsock);
+	return 0;
+}
+
+TOOLKIT_API SOCKET ioqueue_acceptor_get_raw_socket(ioqueue_acceptor_t* acceptor)
+{
+	assert(acceptor);
+	assert(acceptor->type == HANDLE_TYPE_ACCEPTOR);
+	assert(acceptor->u.sock != INVALID_SOCKET);
+	return acceptor->u.sock;
+}
+
+TOOLKIT_API ioqueue_t* ioqueue_acceptor_get_owned_ioqueue(ioqueue_acceptor_t* acceptor)
+{
+	assert(acceptor);
+	assert(acceptor->type == HANDLE_TYPE_ACCEPTOR);
+	return acceptor->owner;
+}
+
+TOOLKIT_API void *ioqueue_acceptor_set_user_data(ioqueue_acceptor_t* acceptor, void *user_data)
+{
+	void *old;
+	assert(acceptor);
+	assert(acceptor->type == HANDLE_TYPE_ACCEPTOR);
+	old = acceptor->user_data;
+	acceptor->user_data = user_data;
+	return old;
+}
+
+TOOLKIT_API void *ioqueue_acceptor_get_user_data(ioqueue_acceptor_t* acceptor)
+{
+	assert(acceptor);
+	assert(acceptor->type == HANDLE_TYPE_ACCEPTOR);
+	return acceptor->user_data;
+}
+
+TOOLKIT_API int ioqueue_acceptor_cancel(ioqueue_acceptor_t* acceptor)
+{
+	assert(acceptor);
+	return CancelIo(acceptor->u.file) ? 0 : -1;
+}
+
+/* tcpsock */
+
+TOOLKIT_API int ioqueue_tcpsock_create(ioqueue_t *ioq, ioqueue_tcpsock_t *tcpsock)
+{
+	SOCKET s;
+
+	assert(ioq);
+	assert(tcpsock);
+
+	if (ioq->stop)
+		return -1;
+
+	s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
+	if (s == INVALID_SOCKET)
+		return -1;
+	if (ioqueue_tcpsock_create_from_handle(ioq, s, tcpsock) != 0) {
+		closesocket(s);
+		return -1;
+	}
+	return 0;
+}
+
+TOOLKIT_API int ioqueue_tcpsock_create_from_handle(ioqueue_t *ioq, SOCKET s, ioqueue_tcpsock_t *tcpsock)
+{
+	assert(ioq);
+	assert(s != INVALID_SOCKET);
+	assert(tcpsock);
+
+	if (ioq->stop)
+		return -1;
+
+	memset(tcpsock, 0, sizeof(ioqueue_tcpsock_t));
+	tcpsock->u.sock = s;
+	reuse_addr(tcpsock->u.sock);
+	nonblock_sock(tcpsock->u.sock);
+	/* winxp or more we use ConnectEx, this funtion need bind at first */
+	if (is_os_gte_xp()) {
+		struct sockaddr_in local = {0};
+		local.sin_family = AF_INET;
+		local.sin_port = htons(0);
+		local.sin_addr.s_addr = INADDR_ANY;
+		if (bind(tcpsock->u.sock, (struct sockaddr*)&local, sizeof(struct sockaddr)) != 0)
+			return -1;
+	} else {
+		/* for win2k we use connect, set socket to non-block mode */
+		//u_long ul_onoff = 1;
+		//if (ioctlsocket(tcpsock->u.sock, FIONBIO, &ul_onoff) != 0)
+		//	goto on_error;
+	}
+	if (!CreateIoCompletionPort((HANDLE)tcpsock->u.sock, ioq->iocp, 0, 0))
+		return -1;
+	fastlock_init(tcpsock->ov_pending_list_lock);
+	INIT_LIST_HEAD(&tcpsock->ov_pending_list);
+	tcpsock->type = HANDLE_TYPE_TCPSOCK;
+	tcpsock->owner = ioq;
+	add_handler_list(tcpsock, ioq);
+	inc_ref(ioqueue_handle_context, tcpsock);
+
+	return 0;
+}
+
+TOOLKIT_API int ioqueue_tcpsock_async_connect(ioqueue_tcpsock_t *tcpsock, 
+								  ioqueue_overlapped_t *ov,
+								  const char *ip, 
+								  unsigned short port, 
+								  ioqueue_on_connect_callback on_connect_callback,
+								  void* user_data)
+{
+	ioqueue_t *ioq;
+	ioqueue_connect_overlapped_t *overlapped;
+	struct sockaddr_in service;
+
+	assert(tcpsock);
+	assert(ov);
+	assert(ip);
+	assert(port);
+	assert(on_connect_callback);
+
+	ioq = tcpsock->owner;
+	if (ioq->stop)
+		return -1;
+
+	ioq = tcpsock->owner;
+	overlapped = (ioqueue_connect_overlapped_t*)ov;
+	memset(overlapped, 0, sizeof(ioqueue_connect_overlapped_t));
+	fastlock_enter(tcpsock->ov_pending_list_lock);
+	list_add_tail(&overlapped->base.pending_entry, &tcpsock->ov_pending_list);
+	fastlock_leave(tcpsock->ov_pending_list_lock);
+	overlapped->base.type = OV_CONNECT;
+	overlapped->base.handle_ctx = (ioqueue_handle_context*)tcpsock;
+	overlapped->base.user_data = user_data;
+	overlapped->on_connect_callback = on_connect_callback;
+	inc_pending_io(tcpsock);
+
+	if (is_os_gte_xp()) { /* use ConnectEx */
+		DWORD dwBytes;
+		BOOL ret;
+		BOOL (PASCAL FAR * lpfnConnectEx) (IN SOCKET s,
+			IN const struct sockaddr FAR *name,
+			IN int namelen,
+			IN PVOID lpSendBuffer OPTIONAL,
+			IN DWORD dwSendDataLength,
+			OUT LPDWORD lpdwBytesSent,
+			IN LPOVERLAPPED lpOverlapped
+			);
+//		LPFN_CONNECTEX   lpfnConnectEx;
+		GUID GuidConnectEx = WSAID_CONNECTEX;
+		if (WSAIoctl(tcpsock->u.sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidConnectEx, 
+			sizeof(GuidConnectEx), &lpfnConnectEx, sizeof(lpfnConnectEx), &dwBytes, NULL, NULL) != 0) {
+				fastlock_enter(tcpsock->ov_pending_list_lock);
+				list_del(&overlapped->base.pending_entry);
+				fastlock_leave(tcpsock->ov_pending_list_lock);
+				dec_pending_io(tcpsock);
+				return -1;
+		}
+		memset(&service, 0, sizeof(service));
+		service.sin_family = AF_INET;
+		service.sin_port = htons(port);
+		service.sin_addr.s_addr = inet_addr(ip);
+		{
+			struct sockaddr_in local_addr = {0}; // bind to a INADDR_ANY and port 0 to let OS choose an local address
+			local_addr.sin_family = AF_INET;
+			local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+			local_addr.sin_port = htons(0);
+			ret = bind(tcpsock->u.sock, (SOCKADDR*)&local_addr, sizeof(local_addr)); // caution: ConnectEx need socket to be bounded at first
+		}
+		if (ret == 0) {
+			ret = lpfnConnectEx(tcpsock->u.sock, (struct sockaddr*)&service, sizeof(service), NULL, 
+				0, NULL, &overlapped->base.ov);
+			if (ret || WSAGetLastError() == WSA_IO_PENDING)
+				return 0;
+		}
+	} else { /* use non-blocking connect */
+		overlapped->hevt = WSACreateEvent();
+		if (WSAEventSelect(tcpsock->u.sock, overlapped->hevt, FD_CONNECT) == 0) {
+			spinlock_enter(&ioq->connect_list_lock, -1);
+			list_add_tail(&overlapped->node, &ioq->connect_list);
+			spinlock_leave(&ioq->connect_list_lock);
+			if (connect(tcpsock->u.sock, (struct sockaddr*)&service, sizeof(service)) == 0) {
+				return 0;
+			} else {
+				spinlock_enter(&ioq->connect_list_lock, -1);
+				list_del(&overlapped->node);
+				spinlock_leave(&ioq->connect_list_lock);
+			}
+		}
+		WSACloseEvent(overlapped->hevt);
+	}
+	fastlock_enter(tcpsock->ov_pending_list_lock);
+	list_del(&overlapped->base.pending_entry);
+	fastlock_leave(tcpsock->ov_pending_list_lock);
+	dec_pending_io(tcpsock);
+
+	return -1;
+}
+
+TOOLKIT_API int ioqueue_tcpsock_conect(ioqueue_tcpsock_t *tcpsock, 
+						   const char *ip, 
+						   unsigned short port, 
+						   int timeout)
+{
+	fd_set wr_set;
+	fd_set ex_set;
+	struct timeval tm;
+
+	assert(tcpsock);
+	assert(ip);
+	assert(port > 0);
+
+	FD_ZERO(&wr_set);
+	FD_ZERO(&ex_set);
+
+	FD_SET(tcpsock->u.sock, &wr_set);
+	FD_SET(tcpsock->u.sock, &ex_set);
+
+	tm.tv_sec = timeout / 1000;
+	tm.tv_usec = 1000 * (timeout % 1000);
+
+	if (select(tcpsock->u.sock+1, NULL, &wr_set, &ex_set, &tm) > 0) {
+		if (FD_ISSET(tcpsock->u.sock, &ex_set))
+			return -1;
+		if (FD_ISSET(tcpsock->u.sock, &wr_set))
+			return 0;
+	}
+
+	return -1;
+}
+
+
+TOOLKIT_API int ioqueue_tcpsock_async_sendsome(ioqueue_tcpsock_t *tcpsock, 
+								   ioqueue_overlapped_t *ov,
+								   void *buf, 
+								   unsigned int len, 
+								   ioqueue_on_send_callback on_send_callback,
+								   void *user_data)
+{
+	ioqueue_sendsome_overlapped_t *overlapped;
+	DWORD bytesWritten;
+	int rc;
+	ioqueue_t *ioq;
+
+	assert(tcpsock);
+	assert(ov);
+	assert(buf);
+	assert(on_send_callback);
+
+	ioq = ioqueue_tcpsock_get_owned_ioqueue(tcpsock);
+	if (ioq->stop)
+		return -1;
+
+	overlapped = (ioqueue_sendsome_overlapped_t*)ov;
+	memset(overlapped, 0, sizeof(ioqueue_sendsome_overlapped_t));
+	fastlock_enter(tcpsock->ov_pending_list_lock);
+	list_add_tail(&overlapped->base.pending_entry, &tcpsock->ov_pending_list);
+	fastlock_leave(tcpsock->ov_pending_list_lock);
+	overlapped->base.type = OV_SENDSOME;
+	overlapped->base.handle_ctx = (ioqueue_handle_context*)tcpsock;
+	overlapped->base.user_data = user_data;
+	overlapped->on_send_callback = on_send_callback;
+	overlapped->wsabuf.len = len;
+	overlapped->wsabuf.buf = buf;
+	inc_pending_io(tcpsock);
+	rc = WSASend(tcpsock->u.sock, &overlapped->wsabuf, 1, &bytesWritten, 
+		0, &overlapped->base.ov, NULL);
+	if (rc == 0 || WSAGetLastError() == WSA_IO_PENDING)
+		return 0;
+	fastlock_enter(tcpsock->ov_pending_list_lock);
+	list_del(&overlapped->base.pending_entry);
+	fastlock_leave(tcpsock->ov_pending_list_lock);
+	dec_pending_io(tcpsock);
+	return -1;
+}
+
+TOOLKIT_API int ioqueue_tcpsock_async_sendn(ioqueue_tcpsock_t *tcpsock, 
+								ioqueue_overlapped_t *ov,
+								void *buf, 
+								unsigned int len,
+								ioqueue_on_send_callback on_send_callback,
+								void* user_data)
+{
+	ioqueue_sendn_overlapped_t *overlapped;
+	DWORD bytesWritten;
+	int rc;
+	ioqueue_t *ioq;
+
+	assert(tcpsock);
+	assert(ov);
+	assert(buf);
+	assert(on_send_callback);
+
+	ioq = ioqueue_tcpsock_get_owned_ioqueue(tcpsock);
+	if (ioq->stop)
+		return -1;
+
+	overlapped = (ioqueue_sendn_overlapped_t*)ov;
+	memset(overlapped, 0, sizeof(ioqueue_sendn_overlapped_t));
+	fastlock_enter(tcpsock->ov_pending_list_lock);
+	list_add_tail(&overlapped->base.pending_entry, &tcpsock->ov_pending_list);
+	fastlock_leave(tcpsock->ov_pending_list_lock);
+	overlapped->base.type = OV_SENDN;
+	overlapped->base.handle_ctx = (ioqueue_handle_context*)tcpsock;
+	overlapped->base.user_data = user_data;
+	overlapped->on_send_callback = on_send_callback;
+	overlapped->wsabuf.len = len;
+	overlapped->wsabuf.buf = buf;
+	overlapped->original_buf = buf;
+	overlapped->sended_bytes = 0;
+	overlapped->total_bytes = len;
+	inc_pending_io(tcpsock);
+	rc = WSASend(tcpsock->u.sock, &overlapped->wsabuf, 1, &bytesWritten, 
+		0, &overlapped->base.ov, NULL);
+	if (rc == 0 || WSAGetLastError() == WSA_IO_PENDING)
+		return 0;
+	bytesWritten = WSAGetLastError();
+	fastlock_enter(tcpsock->ov_pending_list_lock);
+	list_del(&overlapped->base.pending_entry);
+	fastlock_leave(tcpsock->ov_pending_list_lock);
+	dec_pending_io(tcpsock);
+	return -1;
+}
+
+TOOLKIT_API int ioqueue_tcpsock_async_senduntil(ioqueue_tcpsock_t *tcpsock, 
+									ioqueue_overlapped_t *ov,
+									void *buf, 
+									unsigned int len,
+									const char *delimer,
+									ioqueue_on_send_callback on_send_cb,
+									void* user_data)
+{
+	const char *p;
+
+	assert(tcpsock);
+	assert(ov);
+	assert(buf);
+	assert(on_send_cb);
+	assert(delimer);
+	
+	p = memstr(buf, len, delimer);
+	if (!p)
+		return -1;
+	p += strlen(delimer);
+
+	return ioqueue_tcpsock_async_sendn(tcpsock, ov, buf, p - (char*)buf, on_send_cb, user_data);
+}
+
+TOOLKIT_API int ioqueue_tcpsock_sendsome(ioqueue_tcpsock_t *tcpsock, 
+							 void *buf, 
+							 unsigned int len,
+							 int timeout)
+{
+	assert(tcpsock);
+	return send(tcpsock->u.sock, buf, len, 0);
+}
+
+TOOLKIT_API int ioqueue_tcpsock_sendn(ioqueue_tcpsock_t *tcpsock, 
+						  void *buf, 
+						  unsigned int len,
+						  int timeout)
+{
+	return tsend_n(tcpsock->u.sock, buf, len, timeout);
+}
+
+TOOLKIT_API int ioqueue_tcpsock_senduntil(ioqueue_tcpsock_t *tcpsock, 
+							  void *buf, 
+							  unsigned int len, 
+							  const char *delimer, 
+							  int timeout)
+{
+	return tsend_until(tcpsock->u.sock, buf, len, delimer, timeout);
+}
+
+TOOLKIT_API int ioqueue_tcpsock_async_recvsome(ioqueue_tcpsock_t *tcpsock, 
+								   ioqueue_overlapped_t *ov,
+								   void *buf, 
+								   unsigned int len, 
+								   ioqueue_on_recv_callback on_recv_callback,
+								   void *user_data)
+{
+	ioqueue_recvsome_overlapped_t *overlapped;
+	DWORD bytesRead;
+	int rc;
+	ioqueue_t *ioq;
+
+	assert(tcpsock);
+	assert(ov);
+	assert(buf);
+	assert(on_recv_callback);
+
+	ioq = tcpsock->owner;
+	if (ioq->stop)
+		return -1;
+
+	overlapped = (ioqueue_recvsome_overlapped_t*)ov;
+	memset(overlapped, 0, sizeof(ioqueue_recvsome_overlapped_t));
+	fastlock_enter(tcpsock->ov_pending_list_lock);
+	list_add_tail(&overlapped->base.pending_entry, &tcpsock->ov_pending_list);
+	fastlock_leave(tcpsock->ov_pending_list_lock);
+	overlapped->base.type = OV_RECVSOME;
+	overlapped->base.handle_ctx = (ioqueue_handle_context*)tcpsock;
+	overlapped->base.user_data = user_data;
+	overlapped->on_recv_callback = on_recv_callback;
+	overlapped->wsabuf.len = len;
+	overlapped->wsabuf.buf = buf;
+	overlapped->dwFlags = 0;
+	inc_pending_io(tcpsock);
+	rc = WSARecv(tcpsock->u.sock, &overlapped->wsabuf, 1, &bytesRead, &overlapped->dwFlags, 
+			&overlapped->base.ov, NULL);
+	if (rc == 0 || WSAGetLastError() == WSA_IO_PENDING)
+		return 0;
+	fastlock_enter(tcpsock->ov_pending_list_lock);
+	list_del(&overlapped->base.pending_entry);
+	fastlock_leave(tcpsock->ov_pending_list_lock);
+	dec_pending_io(tcpsock);
+	return -1;
+}
+
+TOOLKIT_API int ioqueue_tcpsock_async_recvn(ioqueue_tcpsock_t *tcpsock, 
+								ioqueue_overlapped_t *ov,
+								void *buf, 
+								unsigned int len, 
+								ioqueue_on_recv_callback on_recv_callback,
+								void *user_data)
+{
+	ioqueue_recvn_overlapped_t *overlapped;
+	DWORD bytesRead;
+	int rc;
+	ioqueue_t *ioq;
+
+	assert(tcpsock);
+	assert(ov);
+	assert(buf);
+	assert(on_recv_callback);
+
+	ioq = tcpsock->owner;
+	if (ioq->stop)
+		return -1;
+
+	overlapped = (ioqueue_recvn_overlapped_t*)ov;
+	memset(overlapped, 0, sizeof(ioqueue_recvn_overlapped_t));
+	fastlock_enter(tcpsock->ov_pending_list_lock);
+	list_add_tail(&overlapped->base.pending_entry, &tcpsock->ov_pending_list);
+	fastlock_leave(tcpsock->ov_pending_list_lock);
+	overlapped->base.type = OV_RECVN;
+	overlapped->base.handle_ctx = (ioqueue_handle_context*)tcpsock;
+	overlapped->base.user_data = user_data;
+	overlapped->on_recv_callback = on_recv_callback;
+	overlapped->wsabuf.len = len;
+	overlapped->wsabuf.buf = buf;
+	overlapped->original_buf = buf;
+	overlapped->recved_bytes = 0;
+	overlapped->total_bytes = len;
+	overlapped->dwFlags = 0;
+	inc_pending_io(tcpsock);
+	rc = WSARecv(tcpsock->u.sock, &overlapped->wsabuf, 1, &bytesRead, &overlapped->dwFlags, 
+			&overlapped->base.ov, NULL);
+	if (rc == 0 || WSAGetLastError() == WSA_IO_PENDING)
+		return 0;
+	fastlock_enter(tcpsock->ov_pending_list_lock);
+	list_del(&overlapped->base.pending_entry);
+	fastlock_leave(tcpsock->ov_pending_list_lock);
+	dec_pending_io(tcpsock);
+	return -1;
+}
+
+TOOLKIT_API int ioqueue_tcpsock_async_recvuntil(ioqueue_tcpsock_t *tcpsock, 
+									ioqueue_overlapped_t *ov,
+									void *buf, 
+									unsigned int len,
+									const char *delimer,
+									ioqueue_on_recvuntil_callback on_recvuntil_callback,
+									void *user_data)
+{
+	ioqueue_recvuntil_overlapped_t *overlapped;
+	DWORD bytesRead;
+	int rc;
+	ioqueue_t *ioq;
+
+	assert(tcpsock);
+	assert(ov);
+	assert(buf);
+	assert(delimer);
+	assert(on_recvuntil_callback);
+
+	ioq = tcpsock->owner;
+	if (ioq->stop)
+		return -1;
+
+	overlapped = (ioqueue_recvuntil_overlapped_t*)ov;
+	memset(overlapped, 0, sizeof(ioqueue_recvuntil_overlapped_t));
+	fastlock_enter(tcpsock->ov_pending_list_lock);
+	list_add_tail(&overlapped->base.pending_entry, &tcpsock->ov_pending_list);
+	fastlock_leave(tcpsock->ov_pending_list_lock);
+	overlapped->base.type = OV_RECVUNTIL;
+	overlapped->base.handle_ctx = (ioqueue_handle_context*)tcpsock;
+	overlapped->base.user_data = user_data;
+	overlapped->on_recvuntil_callback = on_recvuntil_callback;
+	overlapped->wsabuf.len = len;
+	overlapped->wsabuf.buf = buf;
+	overlapped->original_buf = buf;
+	overlapped->recved_bytes = 0;
+	overlapped->total_bytes = len;
+	overlapped->delimer = _strdup(delimer);
+	overlapped->dwFlags = 0;
+	inc_pending_io(tcpsock);
+	rc = WSARecv(tcpsock->u.sock, &overlapped->wsabuf, 1, &bytesRead, &overlapped->dwFlags, 
+			&overlapped->base.ov, NULL);
+	if (rc == 0 || WSAGetLastError() == WSA_IO_PENDING)
+		return 0;
+	fastlock_enter(tcpsock->ov_pending_list_lock);
+	list_del(&overlapped->base.pending_entry);
+	fastlock_leave(tcpsock->ov_pending_list_lock);
+	dec_pending_io(tcpsock);
+	return -1;
+}
+
+TOOLKIT_API int ioqueue_tcpsock_recvsome(ioqueue_tcpsock_t *tcpsock, 
+							 void *buf, 
+							 unsigned int len, 
+							 int timeout)
+{
+	return recv(tcpsock->u.sock, buf, len, 0);
+}
+
+TOOLKIT_API int ioqueue_tcpsock_recvn(ioqueue_tcpsock_t *tcpsock, 
+						  void *buf, 
+						  unsigned int len, 
+						  int timeout)
+{
+	return trecv_n(tcpsock->u.sock, buf, len, timeout);
+}
+
+TOOLKIT_API int ioqueue_tcpsock_recvuntil(ioqueue_tcpsock_t *tcpsock, 
+							  void *buf, 
+							  unsigned int len, 
+							  const char *delimer, 
+							  unsigned int *header_len,
+							  int timeout)
+{
+	return trecv_until(tcpsock->u.sock, buf, len, delimer, header_len, timeout);
+}
+
+TOOLKIT_API void ioqueue_tcpsock_close(ioqueue_tcpsock_t *tcpsock)
+{
+	SOCKET s;
+	assert(tcpsock);
+	s = tcpsock->u.sock;
+	if (s != INVALID_SOCKET) {
+		tcpsock->u.sock = INVALID_SOCKET;
+		closesocket(s);
+	}
+}
+
+TOOLKIT_API void ioqueue_tcpsock_destroy(ioqueue_tcpsock_t *tcpsock)
+{
+	assert(tcpsock);
+	dec_ref(ioqueue_handle_context, tcpsock);
+}
+
+TOOLKIT_API int ioqueue_tcpsock_shutdown(ioqueue_tcpsock_t *tcpsock, int how)
+{
+	assert(tcpsock);
+	return shutdown(tcpsock->u.sock, how);
+}
+
+TOOLKIT_API SOCKET ioqueue_tcpsock_get_raw_socket(ioqueue_tcpsock_t *tcpsock)
+{
+	assert(tcpsock);
+	return tcpsock->u.sock;
+}
+
+TOOLKIT_API ioqueue_t* ioqueue_tcpsock_get_owned_ioqueue(ioqueue_tcpsock_t *tcpsock)
+{
+	assert(tcpsock);
+	return tcpsock->owner;
+}
+
+TOOLKIT_API void *ioqueue_tcpsock_set_user_data(ioqueue_tcpsock_t *tcpsock, void *user_data)
+{
+	void *old;
+	assert(tcpsock);
+	old = tcpsock->user_data;
+	tcpsock->user_data = user_data;
+	return old;
+}
+
+TOOLKIT_API void *ioqueue_tcpsock_get_user_data(ioqueue_tcpsock_t *tcpsock)
+{
+	assert(tcpsock);
+	return tcpsock->user_data;
+}
+
+TOOLKIT_API int ioqueue_tcpsock_cancel(ioqueue_tcpsock_t* tcpsock)
+{
+	assert(tcpsock);
+	return CancelIo(tcpsock->u.file) ? 0 : -1;
+}
+
+/* udpsock */
+
+TOOLKIT_API int ioqueue_udpsock_create(ioqueue_t *ioq, ioqueue_udpsock_t *udpsock)
+{
+	SOCKET s;
+	
+	assert(ioq);
+	assert(udpsock);
+
+	if (ioq->stop)
+		return -1;
+
+	s = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_OVERLAPPED);
+	if (s == INVALID_SOCKET) 
+		return -1;
+	if (ioqueue_udpsock_create_from_handle(ioq, s, udpsock) != 0) {
+		closesocket(s);
+		return -1;
+	}
+	return 0;
+}
+
+TOOLKIT_API int ioqueue_udpsock_create_from_handle(ioqueue_t *ioq, SOCKET s, ioqueue_udpsock_t *udpsock)
+{
+	assert(ioq);
+	assert(udpsock);
+	assert(s != INVALID_SOCKET);
+
+	if (ioq->stop)
+		return -1;
+
+	memset(udpsock, 0, sizeof(ioqueue_udpsock_t));
+	udpsock->u.sock = s;
+	nonblock_sock(udpsock->u.sock);
+	if (!CreateIoCompletionPort((HANDLE)udpsock->u.sock, ioq->iocp, 0, 0))
+		return -1;
+	fastlock_init(udpsock->ov_pending_list_lock);
+	INIT_LIST_HEAD(&udpsock->ov_pending_list);
+	udpsock->type = HANDLE_TYPE_UDPSOCK;
+	udpsock->owner = ioq;
+	add_handler_list(udpsock, ioq);
+	inc_ref(ioqueue_handle_context, udpsock);
+
+	return 0;
+}
+
+TOOLKIT_API void ioqueue_udpsock_close(ioqueue_udpsock_t *udpsock)
+{
+	SOCKET s;
+	assert(udpsock);
+	s = udpsock->u.sock;
+	if (s != INVALID_SOCKET) {
+		udpsock->u.sock = INVALID_SOCKET;
+		closesocket(s);
+	}
+}
+
+TOOLKIT_API void ioqueue_udpsock_destroy(ioqueue_udpsock_t *udpsock)
+{
+	assert(udpsock);
+	dec_ref(ioqueue_handle_context, udpsock);
+}
+
+TOOLKIT_API int ioqueue_udpsock_async_sendto(ioqueue_udpsock_t* udpsock, 
+								 ioqueue_overlapped_t *ov,
+								 void *buf, 
+								 int len, 
+								 const struct sockaddr* to, 
+								 int tolen,
+								 ioqueue_on_sendto_callback on_sendto_callback,
+								 void *user_data)
+{
+	ioqueue_sendto_overlapped_t *overlapped;
+	int rc;
+	DWORD bytesWritten;
+	ioqueue_t *ioq;
+
+	assert(udpsock);
+	assert(ov);
+	assert(buf);
+	assert(to);
+	assert(on_sendto_callback);
+	assert(ov);
+
+	ioq = udpsock->owner;
+	if (ioq->stop)
+		return -1;
+
+	overlapped = (ioqueue_sendto_overlapped_t*)ov;
+	memset(overlapped, 0, sizeof(ioqueue_sendto_overlapped_t));
+	fastlock_enter(udpsock->ov_pending_list_lock);
+	list_add_tail(&overlapped->base.pending_entry, &udpsock->ov_pending_list);
+	fastlock_leave(udpsock->ov_pending_list_lock);
+	overlapped->base.type = OV_SENDTO;
+	overlapped->base.user_data = user_data;
+	overlapped->base.handle_ctx = (ioqueue_handle_context*)udpsock;
+	overlapped->on_sendto_callback = on_sendto_callback;
+	overlapped->wsabuf.len = len;
+	overlapped->wsabuf.buf = buf;
+	inc_pending_io(udpsock);
+	rc = WSASendTo(udpsock->u.sock, &overlapped->wsabuf, 1, &bytesWritten, 0,
+			to, tolen, &overlapped->base.ov, NULL);
+	if (rc == 0 || WSAGetLastError() == WSA_IO_PENDING)
+		return 0;
+	fastlock_enter(udpsock->ov_pending_list_lock);
+	list_del(&overlapped->base.pending_entry);
+	fastlock_leave(udpsock->ov_pending_list_lock);
+	dec_pending_io(udpsock);
+	return -1;
+}
+
+TOOLKIT_API int ioqueue_udpsock_sendto(ioqueue_udpsock_t *udpsock, 
+						   void *buf, 
+						   int len, 
+						   const struct sockaddr* to, 
+						   int tolen,
+						   int timeout)
+{
+	return tsendto(udpsock->u.sock, buf, len, to, tolen, timeout);
+}
+
+TOOLKIT_API int ioqueue_udpsock_async_recvfrom(ioqueue_udpsock_t* udpsock, 
+								   ioqueue_overlapped_t *ov,
+								   void* buf, 
+								   int len, 
+								   ioqueue_on_recvfrom_callback on_recvfrom_callback,
+								   void *user_data)
+{
+	ioqueue_recvfrom_overlapped_t *overlapped;
+	int rc;
+	DWORD bytesRead;
+	ioqueue_t *ioq;
+
+	assert(udpsock);
+	assert(ov);
+	assert(buf);
+	assert(on_recvfrom_callback);
+
+	ioq = udpsock->owner;
+	if (ioq->stop)
+		return -1;
+
+	overlapped = (ioqueue_recvfrom_overlapped_t*)ov;
+	memset(overlapped, 0, sizeof(ioqueue_recvfrom_overlapped_t));
+	fastlock_enter(udpsock->ov_pending_list_lock);
+	list_add_tail(&overlapped->base.pending_entry, &udpsock->ov_pending_list);
+	fastlock_leave(udpsock->ov_pending_list_lock);
+	overlapped->base.type = OV_RECVFROM;
+	overlapped->base.user_data = user_data;
+	overlapped->base.handle_ctx = (ioqueue_handle_context*)udpsock;
+	overlapped->on_recvfrom_callback = on_recvfrom_callback;
+	overlapped->wsabuf.len = len;
+	overlapped->wsabuf.buf = buf;
+	overlapped->dwFlags = 0;
+	inc_pending_io(udpsock);
+	rc = WSARecvFrom(udpsock->u.sock, &overlapped->wsabuf, 1, &bytesRead, &overlapped->dwFlags,
+			(struct sockaddr*)&overlapped->peer, &overlapped->addrlen, &overlapped->base.ov, NULL);
+	if (rc == 0 || WSAGetLastError() == WSA_IO_PENDING)
+		return 0;
+	fastlock_enter(udpsock->ov_pending_list_lock);
+	list_del(&overlapped->base.pending_entry);
+	fastlock_leave(udpsock->ov_pending_list_lock);
+	dec_pending_io(udpsock);
+	return -1;
+}
+
+TOOLKIT_API int ioqueue_udpsock_recvfrom(ioqueue_udpsock_t* udpsock, 
+							 ioqueue_overlapped_t *overlapped,
+							 void* buf, 
+							 int len,
+							 struct sockaddr *fromaddr,
+							 int *addrlen,
+							 int timeout)
+{
+	return trecvfrom(udpsock->u.sock, buf, len, fromaddr, addrlen, timeout);
+}
+
+TOOLKIT_API SOCKET ioqueue_udpsock_get_raw_socket(ioqueue_udpsock_t *udpsock)
+{
+	assert(udpsock);
+	return udpsock->u.sock;
+}
+
+TOOLKIT_API ioqueue_t* ioqueue_udpsock_get_owned_ioqueue(ioqueue_udpsock_t *udpsock)
+{
+	assert(udpsock);
+	return udpsock->owner;
+}
+
+TOOLKIT_API void *ioqueue_udpsock_set_user_data(ioqueue_udpsock_t *udpsock, void *user_data)
+{
+	void *old;
+	assert(udpsock);
+	old = udpsock->user_data;
+	udpsock->user_data = user_data;
+	return old;
+}
+
+TOOLKIT_API void *ioqueue_udpsock_get_user_data(ioqueue_udpsock_t *udpsock)
+{
+	assert(udpsock);
+	return udpsock->user_data;
+}
+
+TOOLKIT_API int ioqueue_udpsock_cancel(ioqueue_udpsock_t *udpsock)
+{
+	assert(udpsock);
+	return CancelIo(udpsock->u.file) ? 0 : -1;
+}
+
+/* file */
+
+TOOLKIT_API int ioqueue_file_create(ioqueue_t *ioq,
+						const char *path, 
+						DWORD dwDesiredAccess,
+						DWORD dwShareMode,
+						DWORD dwCreationDisposition, 
+						DWORD dwFlagsAndAttributes,
+						ioqueue_file_t *file)
+{
+	HANDLE hFile;
+
+	assert(ioq);
+	assert(path);
+	assert(file);
+
+	if (ioq->stop)
+		return -1;
+
+	hFile = CreateFileA(path, dwDesiredAccess, dwShareMode, 
+		NULL, dwCreationDisposition, 
+		dwFlagsAndAttributes|FILE_FLAG_OVERLAPPED, NULL);
+
+	return ioqueue_file_create_from_handle(ioq, hFile, file);
+}
+
+TOOLKIT_API int ioqueue_file_create_from_handle(ioqueue_t *ioq, HANDLE h, ioqueue_file_t *file)
+{
+	assert(ioq);
+	assert(file);
+
+	if (ioq->stop)
+		return -1;
+
+	memset(file, 0, sizeof(ioqueue_file_t));
+	file->u.file = h;
+	if (file->u.file == INVALID_HANDLE_VALUE)
+		return -1;
+	if (!CreateIoCompletionPort(file->u.file, ioq->iocp, 0, 0)) {
+		CloseHandle(file->u.file);
+		file->u.file = INVALID_HANDLE_VALUE;
+		return -1;
+	}
+	fastlock_init(file->ov_pending_list_lock);
+	INIT_LIST_HEAD(&file->ov_pending_list);
+	file->type = HANDLE_TYPE_FILE;
+	file->owner = ioq;
+	add_handler_list(file, ioq);
+	inc_ref(ioqueue_handle_context, file);
+	return 0;
+}
+
+TOOLKIT_API void ioqueue_file_close(ioqueue_file_t* file)
+{
+	HANDLE s;
+	assert(file);
+	s = file->u.file;
+	if (s != INVALID_HANDLE_VALUE) {
+		file->u.file = INVALID_HANDLE_VALUE;
+		CloseHandle(s);
+	}
+}
+
+TOOLKIT_API void ioqueue_file_destroy(ioqueue_file_t* file)
+{
+	assert(file);
+	dec_ref(ioqueue_handle_context, file);
+}
+
+TOOLKIT_API int ioqueue_file_async_readsome(ioqueue_file_t* file, 
+								ioqueue_overlapped_t *ov,
+								void *buf, 
+								unsigned int len, 
+								ioqueue_on_read_callback on_read_callback,
+								void *user_data)
+{
+	return ioqueue_file_async_readsome_at(file, ov, buf, len, 0, 0, on_read_callback, user_data);
+}
+
+TOOLKIT_API int ioqueue_file_async_readn(ioqueue_file_t* file, 
+							 ioqueue_overlapped_t *overlapped,
+							 void *buf, 
+							 unsigned int len, 
+							 ioqueue_on_read_callback on_read_cb,
+							 void *user_data)
+{
+	return ioqueue_file_async_readn_at(file, overlapped, buf, len, 0, 0, on_read_cb, user_data);
+}
+
+TOOLKIT_API int ioqueue_file_readsome(ioqueue_file_t *file, void *buf, unsigned int len)
+{
+	return ioqueue_file_readsome_at(file, buf, len, 0, 0);
+}
+
+TOOLKIT_API int ioqueue_file_readn(ioqueue_file_t *file, void *buf, unsigned int len)
+{
+	return ioqueue_file_readn_at(file, buf, len, 0, 0);
+}
+
+TOOLKIT_API int ioqueue_file_async_readsome_at(ioqueue_file_t* file, 
+								   ioqueue_overlapped_t *ov,
+								   void *buf, 
+								   unsigned int len, 
+								   DWORD posLow, 
+								   DWORD posHigh, 
+								   ioqueue_on_read_callback on_read_callback,
+								   void *user_data)
+{
+	ioqueue_readfilesome_overlapped_t *overlapped;
+	BOOL rc;
+	ioqueue_t *ioq;
+
+	assert(file);
+	assert(ov);
+	assert(buf);
+	assert(on_read_callback); 
+
+	ioq = file->owner;
+	if (ioq->stop)
+		return -1;
+
+	overlapped = (ioqueue_readfilesome_overlapped_t*)ov;
+	memset(overlapped, 0, sizeof(ioqueue_readfilesome_overlapped_t));
+	fastlock_enter(file->ov_pending_list_lock);
+	list_add_tail(&overlapped->base.pending_entry, &file->ov_pending_list);
+	fastlock_leave(file->ov_pending_list_lock);
+	overlapped->hevt = CreateEventA(NULL, TRUE, FALSE, NULL);
+	overlapped->base.type = OV_READFILESOME;
+	overlapped->base.user_data = user_data;
+	overlapped->base.handle_ctx = (ioqueue_handle_context*)file;
+	overlapped->base.ov.Offset = posLow;
+	overlapped->base.ov.OffsetHigh = posHigh;
+	overlapped->on_read_callback = on_read_callback;
+	overlapped->buf = buf;
+	inc_pending_io(file);
+	rc = ReadFile(file->u.file, buf, (DWORD)len, NULL, &overlapped->base.ov);
+	if (rc || GetLastError() == ERROR_IO_PENDING)
+		return 0;
+	fastlock_enter(file->ov_pending_list_lock);
+	list_del(&overlapped->base.pending_entry);
+	fastlock_leave(file->ov_pending_list_lock);
+	dec_pending_io(file);
+	CloseHandle(overlapped->hevt);
+	return -1;
+}
+
+TOOLKIT_API int ioqueue_file_async_readn_at(ioqueue_file_t* file, 
+								ioqueue_overlapped_t *ov,
+								void *buf, 
+								unsigned int len, 
+								DWORD posLow, 
+								DWORD posHigh, 
+								ioqueue_on_read_callback on_read_cb,
+								void *user_data)
+{
+	ioqueue_readfilen_overlapped_t *overlapped;
+	BOOL rc;
+	ioqueue_t *ioq;
+
+	assert(file);
+	assert(ov);
+	assert(buf);
+	assert(on_read_cb); 
+
+	ioq = file->owner;
+	if (ioq->stop)
+		return -1;
+
+	overlapped = (ioqueue_readfilen_overlapped_t*)ov;
+	memset(overlapped, 0, sizeof(ioqueue_readfilen_overlapped_t));
+	fastlock_enter(file->ov_pending_list_lock);
+	list_add_tail(&overlapped->base.pending_entry, &file->ov_pending_list);
+	fastlock_leave(file->ov_pending_list_lock);
+	overlapped->hevt = CreateEventA(NULL, TRUE, FALSE, NULL);
+	overlapped->base.type = OV_READFILEN;
+	overlapped->base.user_data = user_data;
+	overlapped->base.handle_ctx = (ioqueue_handle_context*)file;
+	overlapped->base.ov.Offset = posLow;
+	overlapped->base.ov.OffsetHigh = posHigh;
+	overlapped->on_read_callback = on_read_cb;
+	overlapped->buf = buf;
+	overlapped->recved_bytes = 0;
+	overlapped->total_bytes = len;
+	inc_pending_io(file);
+	rc = ReadFile(file->u.file, buf, (DWORD)len, NULL, &overlapped->base.ov);
+	if (rc || GetLastError() == ERROR_IO_PENDING)
+		return 0;
+	fastlock_enter(file->ov_pending_list_lock);
+	list_del(&overlapped->base.pending_entry);
+	fastlock_leave(file->ov_pending_list_lock);
+	dec_pending_io(file);
+	return -1;
+}
+
+TOOLKIT_API int ioqueue_file_readsome_at(ioqueue_file_t *file, 
+							 void *buf, 
+							 unsigned int len, 
+							 DWORD posLow, 
+							 DWORD posHigh)
+{
+	OVERLAPPED ov;
+	BOOL ret;
+	DWORD dwTransferBytes;
+	int rc = -1;
+
+	/* (MSDN)
+	Even if you have passed the function a file handle associated with a completion port and 
+	a valid OVERLAPPED structure, an application can prevent completion port notification. 
+	This is done by specifying a valid event handle for the hEvent member of the OVERLAPPED  structure, 
+	and setting its low-order bit. A valid event handle whose low-order bit is set keeps I/O completion 
+	from being queued to the completion port.
+	*/
+
+	memset(&ov, 0, sizeof(ov));
+	ov.Offset = posLow;
+	ov.OffsetHigh = posHigh;
+	ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
+	ov.hEvent = (HANDLE)((DWORD)ov.hEvent & 0x1);
+
+	ret = ReadFile(file->u.file, buf, len, &dwTransferBytes, &ov);
+
+	if (!ret && GetLastError() == ERROR_IO_PENDING) {
+		ret = GetOverlappedResult(file->u.file, &ov, &dwTransferBytes, TRUE);
+	}
+
+	CloseHandle((HANDLE)((DWORD)ov.hEvent & ~1));
+
+	if (ret && dwTransferBytes > 0)
+		rc = dwTransferBytes;
+
+	return rc;
+}
+
+TOOLKIT_API int ioqueue_file_readn_at(ioqueue_file_t *file, 
+						  void *buf, 
+						  unsigned int len, 
+						  DWORD posLow, 
+						  DWORD posHigh)
+{
+	OVERLAPPED ov;
+	int rc = 0;
+	DWORD left = len;
+	DWORD offset = 0;
+
+	memset(&ov, 0, sizeof(ov));
+	ov.Offset = posLow;
+	ov.OffsetHigh = posHigh;
+	ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
+	ov.hEvent = (HANDLE)((DWORD)ov.hEvent & 0x1);
+
+	while (left > 0) {
+		BOOL ret;
+		DWORD dwTransferBytes;
+		ret = ReadFile(file->u.file, (char*)buf+offset, len, &dwTransferBytes, &ov);
+		if (!ret && GetLastError() == ERROR_IO_PENDING) {
+			ret = GetOverlappedResult(file->u.file, &ov, &dwTransferBytes, TRUE);
+		}
+		if (rc && dwTransferBytes) {
+			offset += dwTransferBytes;
+			left -= dwTransferBytes;
+			ov.Internal = 0;
+			ov.InternalHigh = 0;
+			ov.Offset += dwTransferBytes;
+			if (ov.Offset < dwTransferBytes)
+				ov.OffsetHigh++;
+		} else {
+			rc = -1;
+			break;
+		}
+	}
+
+	CloseHandle((HANDLE)((DWORD)ov.hEvent & ~1));
+
+	return rc;
+}
+
+TOOLKIT_API int ioqueue_file_async_writesome(ioqueue_file_t* file, 
+								 ioqueue_overlapped_t *ov,
+								 void* buf, 
+								 unsigned int len, 
+								 ioqueue_on_write_callback on_write_callback,
+								 void *user_data)
+{
+	return ioqueue_file_async_writesome_at(file, ov, buf, len, 0, 0, on_write_callback, user_data);
+}
+
+TOOLKIT_API int ioqueue_file_async_writen(ioqueue_file_t* file, 
+							  ioqueue_overlapped_t *overlapped,
+							  void* buf, 
+							  unsigned int len, 
+							  ioqueue_on_write_callback on_write_cb,
+							  void *user_data)
+{
+	return ioqueue_file_async_writen_at(file, overlapped, buf, len, 0, 0, on_write_cb, user_data);
+}
+
+TOOLKIT_API int ioqueue_file_writesome(ioqueue_file_t* file, const void *buf, unsigned int len)
+{
+	return ioqueue_file_writesome_at(file, buf, len, 0, 0);
+}
+
+TOOLKIT_API int ioqueue_file_writen(ioqueue_file_t* file, const void *buf, unsigned int len)
+{
+	return ioqueue_file_writen_at(file, buf, len, 0, 0);
+}
+
+TOOLKIT_API int ioqueue_file_async_writesome_at(ioqueue_file_t* file, 
+									ioqueue_overlapped_t *ov,
+									void* buf, 
+									unsigned int len, 
+									DWORD posLow, 
+									DWORD posHigh, 
+									ioqueue_on_write_callback on_write_callback,
+									void *user_data)
+{
+	ioqueue_writefilesome_overlapped_t *overlapped;
+	BOOL rc;
+	ioqueue_t *ioq;
+
+	assert(file);
+	assert(ov);
+	assert(buf);
+	assert(on_write_callback);
+
+	ioq = file->owner;
+	if (ioq->stop)
+		return -1;
+
+	overlapped = (ioqueue_writefilesome_overlapped_t*)ov;
+	memset(overlapped, 0, sizeof(ioqueue_writefilesome_overlapped_t));
+	fastlock_enter(file->ov_pending_list_lock);
+	list_add_tail(&overlapped->base.pending_entry, &file->ov_pending_list);
+	fastlock_leave(file->ov_pending_list_lock);
+	overlapped->hevt = CreateEventA(NULL, TRUE, FALSE, NULL);
+	overlapped->base.type = OV_WRITEFILESOME;
+	overlapped->base.user_data = user_data;
+	overlapped->base.handle_ctx = (ioqueue_handle_context*)file;
+	overlapped->base.ov.Offset = posLow;
+	overlapped->base.ov.OffsetHigh = posHigh;
+	overlapped->on_write_callback = on_write_callback;
+	overlapped->buf = buf;
+	inc_pending_io(file);
+	rc = WriteFile(file->u.file, buf, (DWORD)len, NULL, &overlapped->base.ov);
+	if (rc || GetLastError() == ERROR_IO_PENDING)
+		return 0;
+	fastlock_enter(file->ov_pending_list_lock);
+	list_del(&overlapped->base.pending_entry);
+	fastlock_leave(file->ov_pending_list_lock);
+	dec_pending_io(file);
+	CloseHandle(overlapped->hevt);
+	return -1;
+}
+
+TOOLKIT_API int ioqueue_file_async_writen_at(ioqueue_file_t* file, 
+								 ioqueue_overlapped_t *ov,
+								 void* buf, 
+								 unsigned int len, 
+								 DWORD posLow, 
+								 DWORD posHigh, 
+								 ioqueue_on_write_callback on_write_cb,
+								 void *user_data)
+{
+	ioqueue_writefilen_overlapped_t *overlapped;
+	BOOL rc;
+	ioqueue_t *ioq;
+
+	assert(file);
+	assert(ov);
+	assert(buf);
+	assert(on_write_cb);
+
+	ioq = file->owner;
+	if (ioq->stop)
+		return -1;
+
+	overlapped = (ioqueue_writefilen_overlapped_t*)ov;
+	memset(overlapped, 0, sizeof(ioqueue_writefilen_overlapped_t));
+	fastlock_enter(file->ov_pending_list_lock);
+	list_add_tail(&overlapped->base.pending_entry, &file->ov_pending_list);
+	fastlock_leave(file->ov_pending_list_lock);
+	overlapped->hevt = CreateEventA(NULL, TRUE, FALSE, NULL);
+	overlapped->base.type = OV_WRITEFILEN;
+	overlapped->base.user_data = user_data;
+	overlapped->base.handle_ctx = (ioqueue_handle_context*)file;
+	overlapped->base.ov.Offset = posLow;
+	overlapped->base.ov.OffsetHigh = posHigh;
+	overlapped->on_write_callback = on_write_cb;
+	overlapped->buf = buf;
+	overlapped->sended_bytes = 0;
+	overlapped->total_bytes = len;
+	inc_pending_io(file);
+	rc = WriteFile(file->u.file, buf, (DWORD)len, NULL, &overlapped->base.ov);
+	if (rc || GetLastError() == ERROR_IO_PENDING)
+		return 0;
+	fastlock_enter(file->ov_pending_list_lock);
+	list_del(&overlapped->base.pending_entry);
+	fastlock_leave(file->ov_pending_list_lock);
+	dec_pending_io(file);
+	CloseHandle(overlapped->hevt);
+	return -1;
+}
+
+TOOLKIT_API int ioqueue_file_writesome_at(ioqueue_file_t* file,
+							  const void *buf, 
+							  unsigned int len, 
+							  DWORD posLow, 
+							  DWORD posHigh)
+{
+	OVERLAPPED ov;
+	BOOL ret;
+	DWORD dwTransferBytes;
+	int rc = -1;
+
+	memset(&ov, 0, sizeof(ov));
+	ov.Offset = posLow;
+	ov.OffsetHigh = posHigh;
+	ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
+	ov.hEvent = (HANDLE)((DWORD)ov.hEvent & 0x1);
+
+	ret = WriteFile(file->u.file, buf, len, &dwTransferBytes, &ov);
+
+	if (!ret && GetLastError() == ERROR_IO_PENDING) {
+		ret = GetOverlappedResult(file->u.file, &ov, &dwTransferBytes, TRUE);
+	}
+
+	CloseHandle((HANDLE)((DWORD)ov.hEvent & ~1));
+
+	if (ret && dwTransferBytes > 0)
+		rc = dwTransferBytes;
+
+	return rc;
+}
+
+TOOLKIT_API int ioqueue_file_writen_at(ioqueue_file_t* file, 
+						   const void *buf, 
+						   unsigned int len, 
+						   DWORD posLow, 
+						   DWORD posHigh)
+{
+	OVERLAPPED ov;
+	int rc = 0;
+	DWORD offset = 0;
+	DWORD left = len;
+
+	memset(&ov, 0, sizeof(ov));
+	ov.Offset = posLow;
+	ov.OffsetHigh = posHigh;
+	ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
+	ov.hEvent = (HANDLE)((DWORD)ov.hEvent & 0x1);
+
+	while (left > 0) {
+		BOOL ret;
+		DWORD dwTransferBytes;
+		ret = WriteFile(file->u.file, (char*)buf+offset, left, &dwTransferBytes, &ov);
+		if (!ret && GetLastError() == ERROR_IO_PENDING) {
+			ret = GetOverlappedResult(file->u.file, &ov, &dwTransferBytes, TRUE);
+		}
+		if (ret && dwTransferBytes > 0) {
+			offset += dwTransferBytes;
+			left -= dwTransferBytes;
+			ov.Internal = 0;
+			ov.InternalHigh = 0;
+			ov.Offset += dwTransferBytes;
+			if (ov.Offset < dwTransferBytes)
+				ov.OffsetHigh ++;
+		} else {
+			rc = -1;
+			break;
+		}
+	}
+
+	CloseHandle((HANDLE)((DWORD)ov.hEvent & ~1));
+
+	return rc;
+}
+
+TOOLKIT_API ioqueue_t* ioqueue_file_get_owned_ioqueue(ioqueue_file_t* file)
+{
+	assert(file);
+	return file->owner;
+}
+
+TOOLKIT_API HANDLE ioqueue_file_get_raw_handle(ioqueue_file_t* file)
+{
+	assert(file);
+	return file->u.file;
+}
+
+TOOLKIT_API void *ioqueue_file_set_user_data(ioqueue_file_t* file, void* user_data)
+{
+	void *old;
+	assert(file);
+	old = file->user_data;
+	file->user_data = user_data;
+	return old;
+}
+
+TOOLKIT_API void *ioqueue_file_get_user_data(ioqueue_file_t* file)
+{
+	assert(file);
+	return file->user_data;
+}
+
+TOOLKIT_API int ioqueue_file_cancel(ioqueue_file_t* file)
+{
+	assert(file);
+	return CancelIo(file->u.file) ? 0 : -1;
+}
+
+/* pipe acceptor */
+
+TOOLKIT_API int ioqueue_pipe_acceptor_create(ioqueue_t *ioq, 
+								 const char *name, 
+								 ioqueue_pipe_acceptor_t *acceptor)
+{
+	assert(ioq);
+	assert(name);
+	assert(acceptor);
+
+	memset(acceptor, 0, sizeof(ioqueue_pipe_acceptor_t));
+	acceptor->u.pipe_name = strdup_printf("\\\\.\\pipe\\%s", name);
+	acceptor->type = HANDLE_TYPE_PIPEACCEPTOR;
+	acceptor->owner = ioq;
+	fastlock_init(acceptor->ov_pending_list_lock);
+	INIT_LIST_HEAD(&acceptor->ov_pending_list);
+	add_handler_list(acceptor, ioq);
+	inc_ref(ioqueue_handle_context, acceptor);
+
+	return 0;
+}
+
+TOOLKIT_API void ioqueue_pipe_acceptor_destroy(ioqueue_pipe_acceptor_t *acceptor)
+{
+	assert(acceptor);
+	dec_ref(ioqueue_handle_context, acceptor);
+}
+
+TOOLKIT_API ioqueue_t* ioqueue_pipe_acceptor_get_owned_ioqueue(ioqueue_pipe_acceptor_t *acceptor)
+{
+	assert(acceptor);
+	assert(acceptor->type == HANDLE_TYPE_PIPEACCEPTOR);
+	return acceptor->owner;
+}
+
+TOOLKIT_API void *ioqueue_pipe_acceptor_set_user_data(ioqueue_pipe_acceptor_t *acceptor, void *user_data)
+{
+	void *old;
+	assert(acceptor);
+	assert(acceptor->type == HANDLE_TYPE_PIPEACCEPTOR);
+	old = acceptor->user_data;
+	acceptor->user_data = user_data;
+	return old;
+}
+
+TOOLKIT_API void *ioqueue_pipe_acceptor_get_user_data(ioqueue_pipe_acceptor_t *acceptor)
+{
+	assert(acceptor);
+	assert(acceptor->type == HANDLE_TYPE_ACCEPTOR);
+	return acceptor->user_data;
+}
+
+TOOLKIT_API int ioqueue_pipe_acceptor_async_accept(ioqueue_pipe_acceptor_t *acceptor,
+									   ioqueue_overlapped_t *ov,
+									   ioqueue_on_pipe_accept_callback on_accept_callback,
+									   void *user_data)
+{
+	ioqueue_t *ioq;
+	ioqueue_connectpipe_overlapped_t *overlapped;
+	BOOL ret;
+
+	assert(acceptor);
+	assert(ov);
+	assert(acceptor->type == HANDLE_TYPE_PIPEACCEPTOR);
+	assert(on_accept_callback);
+
+	ioq = acceptor->owner;
+	if (ioq->stop)
+		return -1;
+
+	overlapped = (ioqueue_connectpipe_overlapped_t*)ov;
+	memset(overlapped, 0, sizeof(ioqueue_connectpipe_overlapped_t));
+	overlapped->client = CreateNamedPipeA(acceptor->u.pipe_name, 
+		PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, 
+		PIPE_UNLIMITED_INSTANCES, 3072, 3072, NMPWAIT_WAIT_FOREVER, NULL);
+	if (overlapped->client == INVALID_HANDLE_VALUE)
+		return -1;
+	if (!CreateIoCompletionPort(overlapped->client, ioq->iocp, 0, 0)) {
+		CloseHandle(overlapped->client);
+		return -1;
+	}
+	overlapped->hevt = CreateEventA(NULL, TRUE, FALSE, NULL); // must be use event, from MSDN
+	overlapped->base.type = OV_CONNECTPIPE;
+	overlapped->base.user_data = user_data;
+	overlapped->base.handle_ctx = acceptor;
+	overlapped->base.ov.hEvent = overlapped->hevt;
+	fastlock_enter(acceptor->ov_pending_list_lock);
+	list_add_tail(&overlapped->base.pending_entry, &acceptor->ov_pending_list);
+	fastlock_leave(acceptor->ov_pending_list_lock);
+	inc_pending_io(acceptor);
+	overlapped->on_accept_callback = on_accept_callback;
+	ret = ConnectNamedPipe(overlapped->client, &overlapped->base.ov);
+	if (ret || GetLastError() == ERROR_IO_PENDING)
+		return 0;
+	fastlock_enter(acceptor->ov_pending_list_lock);
+	list_del(&overlapped->base.pending_entry);
+	fastlock_leave(acceptor->ov_pending_list_lock);
+	dec_pending_io(acceptor);
+	CloseHandle(overlapped->client);
+	CloseHandle(overlapped->hevt);
+	return -1;
+}
+
+TOOLKIT_API int ioqueue_pipe_acceptor_accept(ioqueue_pipe_acceptor_t *acceptor, HANDLE *p_pipe, int timeout)
+{
+	ioqueue_t *ioq;
+	HANDLE pipe;
+	OVERLAPPED ov;
+	BOOL ret;
+
+	assert(acceptor);
+	assert(p_pipe);
+	assert(acceptor->type == HANDLE_TYPE_PIPEACCEPTOR);
+
+	ioq = acceptor->owner;
+	if (ioq->stop)
+		return -1;
+	pipe = CreateNamedPipeA(acceptor->u.pipe_name, 
+		PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, 
+		PIPE_UNLIMITED_INSTANCES, 3072, 3072, (DWORD)timeout, NULL);
+	if (pipe == INVALID_HANDLE_VALUE)
+		return -1;
+	memset(&ov, 0, sizeof(ov));
+	ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
+	ret = ConnectNamedPipe(pipe, &ov);
+	CloseHandle(ov.hEvent);
+	if (ret && CreateIoCompletionPort(pipe, ioq->iocp, 0, 0)) {
+		*p_pipe = pipe;
+		return 0;
+	} else {
+		CloseHandle(pipe);
+	}
+	return -1;
+}
+
+TOOLKIT_API int ioqueue_pipe_acceptor_create_client(ioqueue_pipe_acceptor_t *acceptor, 
+										HANDLE h, 
+										ioqueue_file_t *pipe)
+{
+	ioqueue_t *ioq;
+	assert(acceptor);
+	assert(pipe);
+	assert(h != INVALID_HANDLE_VALUE);
+	ioq = acceptor->owner;
+	if (ioq->stop)
+		return -1;
+	memset(pipe, 0, sizeof(ioqueue_tcpsock_t));
+	pipe->type = HANDLE_TYPE_FILE;
+	pipe->u.file = h;
+	pipe->owner = ioq;
+	INIT_LIST_HEAD(&pipe->ov_pending_list);
+	add_handler_list(pipe, ioq);
+	inc_ref(ioqueue_handle_context, pipe);
+	return 0;
+}
+
+TOOLKIT_API int ioqueue_pipe_acceptor_cancel(ioqueue_pipe_acceptor_t *acceptor)
+{
+	//.....
+	assert(0);
+
+
+
+	return 0;
+}
+
+TOOLKIT_API int ioqueue_pipe_acceptor_close_pending_handle(ioqueue_pipe_acceptor_t *acceptor)
+{
+	assert(acceptor);
+
+	fastlock_enter(acceptor->ov_pending_list_lock);
+	{
+		ioqueue_base_overlapped_t *pos;
+		list_for_each_entry(pos, &acceptor->ov_pending_list, ioqueue_base_overlapped_t, pending_entry) {
+			ioqueue_connectpipe_overlapped_t *overlapped = (ioqueue_connectpipe_overlapped_t *)pos;
+			if (overlapped->client != INVALID_HANDLE_VALUE) {
+				CloseHandle(overlapped->client);
+				overlapped->client = INVALID_HANDLE_VALUE;
+			}
+		}
+	}
+	fastlock_leave(acceptor->ov_pending_list_lock);
+
+	return 0;
+}

+ 0 - 0
libtoolkit/ioqueue.c → libtoolkit/ioqueue-win.c


+ 0 - 3
libtoolkit/ioqueue.h

@@ -774,9 +774,6 @@ TOOLKIT_API int ioqueue_pipe_acceptor_create_client(ioqueue_pipe_acceptor_t *acc
 TOOLKIT_API int ioqueue_pipe_acceptor_cancel(ioqueue_pipe_acceptor_t *acceptor);
 TOOLKIT_API int ioqueue_pipe_acceptor_close_pending_handle(ioqueue_pipe_acceptor_t *acceptor);
 
-#pragma comment(lib, "ws2_32.lib")
-#pragma comment(lib, "Mswsock.lib")
-
 #ifdef __cplusplus
 } // extern "C" {
 #endif

+ 4 - 1
libtoolkit/log_periodic.c

@@ -8,6 +8,9 @@
 #include "fileutil.h"
 #include <winpr/string.h>
 
+#include <winpr/wlog.h>
+#define TAG TOOLKIT_TAG("log")
+
 typedef struct periodiclogfactory_t periodiclogfactory_t;
 
 extern int fastlock_tryenter(lock_t l);
@@ -274,7 +277,7 @@ static int periodicfilefactory_record_log(void *self,
 		_fwrite_nolock(tmp, n, 1, plog->fp);
 		_fwrite_nolock(s, sn, 1, plog->fp);
 		// Only for debugging. [3/24/2020 16:58 Gifur]
-		printf("%s%s", tmp, s);
+		WLog_DBG(TAG, s);
 		if (plog->flush)
 			_fflush_nolock(plog->fp);
 	} else {

+ 0 - 7
libtoolkit/toolkit.h

@@ -11,13 +11,6 @@
 
 #include "config.h"
 #include <stdint.h>
-#include <winpr/wtypes.h>
-
-#ifndef TOOLKIT_TAG
-#include <winpr/wlog.h>
-#define RVC_TAG(tag) "rvc." tag
-#define TOOLKIT_TAG(tag) RVC_TAG("libtoolkit.") tag
-#endif //TOOLKIT_TAG
 
 #ifdef __cplusplus
 extern "C" {

+ 2 - 2
spbase/sp_iom.c

@@ -346,9 +346,9 @@ int sp_iom_remove_sys_handler(sp_iom_t *iom, int key)
 static int sp_iom_poll(sp_iom_t *iom, int *timeout)
 {
 	int rc;	
-	if (iom->poll_thread_id == 0)
+	if (iom->poll_thread_id == 0) {
 		iom->poll_thread_id = (int)GetCurrentThreadId();
-
+	}
 	rc = bus_endpt_poll(iom->endpt, *timeout);
 
 	for (;;) {

+ 1 - 0
spbase/sp_mod.h

@@ -144,6 +144,7 @@ void sp_mod_mgr_set_instance(sp_mod_mgr_t *);
 
 SPBASE_API int sp_mod_mgr_create(sp_mod_mgr_t **p_mgr);
 SPBASE_API void sp_mod_mgr_destroy(sp_mod_mgr_t *mgr);
+/*bind svc to mgr, just set the value.*/
 SPBASE_API void sp_mod_mgr_bind_shell_svc(sp_mod_mgr_t *mgr, sp_svc_t *svc);
 int sp_mod_mgr_add_module(sp_mod_mgr_t *mgr, sp_cfg_shell_module_t *mod);
 int sp_mod_mgr_add_entity(sp_mod_mgr_t *mgr, sp_cfg_shell_entity_t *ent);

+ 1 - 4
spshell/app.cpp

@@ -256,7 +256,6 @@ int app_init()
 			}
 		}
 	}
-
 	sp_pst_recover(env->dir->obj_path);
 
 #ifdef _WIN32
@@ -298,7 +297,7 @@ int app_init()
 	}
 	sp_dbg_info("start svc ok!");
 
-	sp_mod_mgr_bind_shell_svc(env->mod_mgr, g_app.svc);	//°ó¶¨£¬¸³Öµ
+	sp_mod_mgr_bind_shell_svc(env->mod_mgr, g_app.svc);	
 	sp_dbg_info("bind svc ok!");
 
 	rc = sp_mod_mgr_init(env->mod_mgr);
@@ -418,9 +417,7 @@ int app_term()
 int app_run()
 {
 	int rc;
-
 	rc = sp_iom_run(g_app.iom);
-
 	return rc;
 }
 

+ 0 - 1
spshell/spshell.cpp

@@ -847,7 +847,6 @@ int main(int argc, char **argv)
 #endif //_WIN32
 	
 	auto rc = app_init();
-
 	if (rc == 0) 
 	{
 #ifdef _WIN32