Libfiber
The high performance c/c++ coroutine/fiber library for Linux/FreeBSD/MacOS/Windows, supporting select/poll/epoll/kqueue/iouring/iocp/windows GUI.
Install / Use
/learn @iqiyi/LibfiberREADME
The high performance coroutine library, supporting Linux/BSD/Mac/Windows
<!-- vim-markdown-toc GFM -->- 1 About
- 2 Which IO events are supported?
- 3 SAMPLES
- 3.1 One server sample with C API
- 3.2 One client sample with C API
- 3.3 Resolve domain address in coroutine
- 3.4 Create fiber with standard C++ API
- 3.5 Create fiber with C++1x API
- 3.6 Create shared stack fiber
- 3.7 Sync between fibers and threads
- 3.8 Transfer objects through box
- 3.9 Using wait_group to wait for the others done
- 3.10 Using fiber_pool to execute different tasks
- 3.11 Wait for the result from a thread
- 3.12 Http server supporting http url route
- 3.13 Windows GUI sample
- 3.14 More SAMPLES
- 4 BUILDING
- 5 Benchmark
- 6 API support
- 7 About API Hook
- 8 FAQ
1 About
The libfiber project comes from the coroutine module of the acl project in lib_fiber directory of which. It can be used on OS platforms including Linux, FreeBSD, macOS, and Windows, which supports select, poll, epoll, kqueue, iocp, and even Windows GUI messages for different platform. With libfiber, you can write network application services having the high performance and large concurrent more easily than the traditional asynchronous framework with event-driven model. <b>What's more</b>, with the help of libfiber, you can even write network module of the Windows GUI application written by MFC, wtl or other GUI framework on Windows in coroutine way. That's really amazing.
2 Which IO events are supported ?
The libfiber supports many events including select/poll/epoll/kqueue/iocp, and Windows GUI messages.
| Platform | Event type | |----------------|---------------------------------| | <b>Linux</b> | select, poll, epoll, io-uring | | <b>BSD</b> | select, poll, kqueue | | <b>Mac</b> | select, poll, kqueue | | <b>Windows</b> | select, poll, iocp, GUI Message |
3 SAMPLES
3.1 One server sample with C API
// fiber_server.c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "fiber/lib_fiber.h"
#include "patch.h" // in the samples' path
static size_t __stack_size = 128000;
static const char *__listen_ip = "127.0.0.1";
static int __listen_port = 9001;
// Set read/write timeout with setsockopt API.
static int set_timeout(SOCKET fd, int opt, int timeo) {
# if defined(_WIN32) || defined(_WIN64)
timeout *= 1000; // From seconds to millisecond.
if (setsockopt(fd, SOL_SOCKET, opt, (const char*) &timeo, sizeof(timeo)) < 0) {
printf("setsockopt error=%s, timeout=%d, opt=%d, fd=%d\r\n",
strerror(errno), timeo, opt, (int) fd);
return -1;
}
# else // Must be Linux or __APPLE__.
struct timeval tm;
tm.tv_sec = timeo;
tm.tv_usec = 0;
if (setsockopt(fd, SOL_SOCKET, opt, &tm, sizeof(tm)) < 0) {
printf("setsockopt error=%s, timeout=%d, opt=%d, fd=%d\r\n",
strerror(errno), timeo, opt, (int) fd);
return -1;
}
# endif
return 0;
}
static int set_rw_timeout(SOCKET fd, int timeo) {
if (set_timeout(fd, SO_RCVTIMEO, timeo) == -1
|| set_timeout(fd, SO_SNDTIMEO, timeo) == -1) {
return -1;
}
return 0;
}
static void fiber_client(ACL_FIBER *fb, void *ctx) {
SOCKET *pfd = (SOCKET *) ctx;
char buf[8192];
// Set the socket's read/write timeout.
set_rw_timeout(*pfd, 10);
while (1) {
#ifdef _WIN32
int ret = acl_fiber_recv(*pfd, buf, sizeof(buf), 0);
#else
int ret = recv(*pfd, buf, sizeof(buf), 0);
#endif
if (ret == 0) {
break;
} else if (ret < 0) {
if (acl_fiber_last_error() == FIBER_EINTR) {
continue;
}
break;
}
#ifdef _WIN32
if (acl_fiber_send(*pfd, buf, ret, 0) < 0) {
#else
if (send(*pfd, buf, ret, 0) < 0) {
#endif
break;
}
}
socket_close(*pfd);
free(pfd);
}
static void fiber_accept(ACL_FIBER *fb, void *ctx) {
const char *addr = (const char *) ctx;
SOCKET lfd = socket_listen(__listen_ip, __listen_port);
assert(lfd >= 0);
for (;;) {
SOCKET *pfd, cfd = socket_accept(lfd);
if (cfd == INVALID_SOCKET) {
printf("accept error %s\r\n", acl_fiber_last_serror());
break;
}
pfd = (SOCKET *) malloc(sizeof(SOCKET));
*pfd = cfd;
// create and start one fiber to handle the client socket IO
acl_fiber_create(fiber_client, pfd, __stack_size);
}
socket_close(lfd);
exit (0);
}
// FIBER_EVENT_KERNEL represents the event type on
// Linux(epoll), BSD(kqueue), Mac(kqueue), Windows(iocp)
// FIBER_EVENT_POLL: poll on Linux/BSD/Mac/Windows
// FIBER_EVENT_SELECT: select on Linux/BSD/Mac/Windows
// FIBER_EVENT_WMSG: Win GUI message on Windows
// acl_fiber_create/acl_fiber_schedule_with are in `lib_fiber.h`.
// socket_listen/socket_accept/socket_close are in patch.c of the samples' path.
int main(void) {
int event_mode = FIBER_EVENT_KERNEL;
#ifdef _WIN32
socket_init();
#endif
// create one fiber to accept connections
acl_fiber_create(fiber_accept, NULL, __stack_size);
// start the fiber schedule process
acl_fiber_schedule_with(event_mode);
#ifdef _WIN32
socket_end();
#endif
return 0;
}
3.2 One client sample with C API
// fiber_client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "fiber/lib_fiber.h"
#include "patch.h" // in the samples' path
static const char *__server_ip = "127.0.0.1";
static int __server_port = 9001;
// socket_init/socket_end/socket_connect/socket_close are in patch.c of the samples path
static void fiber_client(ACL_FIBER *fb, void *ctx) {
SOCKET cfd = socket_connect(__server_ip, __server_port);
const char *s = "hello world\r\n";
char buf[8192];
int i, ret;
if (cfd == INVALID_SOCKET) {
return;
}
for (i = 0; i < 1024; i++) {
#ifdef _WIN32
if (acl_fiber_send(cfd, s, strlen(s), 0) <= 0) {
#else
if (send(cfd, s, strlen(s), 0) <= 0) {
#endif
printf("send error %s\r\n", acl_fiber_last_serror());
break;
}
#ifdef _WIN32
ret = acl_fiber_recv(cfd, buf, sizeof(buf), 0);
#else
ret = recv(cfd, buf, sizeof(buf), 0);
#endif
if (ret <= 0) {
break;
}
}
#ifdef _WIN32
acl_fiber_close(cfd);
#else
close(cfd);
#endif
}
int main(void) {
int event_mode = FIBER_EVENT_KERNEL;
size_t stack_size = 128000;
int i;
#ifdef _WIN32
socket_init();
#endif
for (i = 0; i < 100; i++) {
acl_fiber_create(fiber_client, NULL, stack_size);
}
acl_fiber_schedule_with(event_mode);
#ifdef _WIN32
socket_end();
#endif
return 0;
}
3.3 Resolve domain address in coroutine
The rfc1035 for DNS has been implement in libfiber, so you can call gethostbyname or getaddrinfo to get the givent domain's IP addresses in coroutine.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include "fiber/lib_fiber.h"
static void lookup(ACL_FIBER *fiber, void *ctx) {
char *name = (char *) ctx;
struct addrinfo hints, *res0;
int ret;
(void) fiber; // avoid compiler warning
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
ret = getaddrinfo(name, "80", &hints, &res0);
free(name);
if (ret != 0) {
printf("getaddrinfo error %s\r\n", gai_strerror(ret));
} else {
printf("getaddrinfo ok\r\n");
freeaddrinfo(res0);
}
}
int main(void) {
char *name1 = strdup("www.iqiyi.com");
char *name2 = strdup("www.baidu.com");
acl_fiber_create(lookup, name1, 128000);
acl_fiber_create(lookup, name2, 128000);
acl_fiber_schedule();
return 0;
}
3.4 Create fiber with standard C++ API
You can create one coroutine with standard C++ API in libfiber:
#include <cstdio>
#include "fiber/libfiber.hpp"
class myfiber : public acl::fiber {
public:
myfiber() {}
private:
~myfiber() {}
protected:
// @override from acl::fiber
void run() {
printf("hello world!\r\n");
delete this;
}
};
int main() {
for (int i = 0; i < 10; i++) {
acl::fiber* fb = new myfiber();
fb->start();
}
acl::fiber::schedule();
return 0;
}
3.5 Create fiber with C++1x API
You can also create one coroutine with c++11 API in libfiber:
#include <cstdio>
#include "fiber/libfiber.hpp"
#include "fiber/go_fiber.hpp"
static void fiber_routine(int i) {
printf("hi, i=%d, curr fiber=%u\r\n", i, acl::fiber::self());
}
int main() {
for (int i = 0; i < 10; i++) {
go[=] {
fiber_routine(i);
};
}
acl::fiber::schedule
