#ifndef LIB_ACL_FIBER_INCLUDE_H
#define LIB_ACL_FIBER_INCLUDE_H

#ifdef __cplusplus
extern "C" {
#endif

#include <sys/types.h>
#include <sys/socket.h>

//typedef struct ACL_VSTREAM ACL_VSTREAM;

/**
 * 协程结构类型
 */
typedef struct ACL_FIBER ACL_FIBER;

/**
 * 设置是否需要 hook 系统中的 IO 相关的 API，内部缺省值为 1
 * @param onoff {int} 是否需要 hook
 */
void acl_fiber_hook_api(int onoff);

/**
 * 创建一个协程
 * @param fn {void (*)(ACL_FIBER*, void*)} 协程运行时的回调函数地址
 * @param arg {void*} 回调 fn 函数时的第二个参数
 * @param size {size_t} 所创建协程所占栈空间大小
 * @return {ACL_FIBER*}
 */
ACL_FIBER* acl_fiber_create(void (*fn)(ACL_FIBER*, void*),
	void* arg, size_t size);

/**
 * 返回当前线程中处于消亡状态的协程数
 * @retur {int}
 */
int acl_fiber_ndead(void);

void acl_fiber_check_timer(size_t max);

/**
 * 返回当前正在运行的协程对象
 * @retur {ACL_FIBER*} 返回 NULL 表示当前没有正在运行的协程
 */
ACL_FIBER* acl_fiber_running(void);

/**
 * 获得所给协程的协程 ID 号
 * @param fiber {const ACL_FIBER*} acl_fiber_create 创建的协程对象，必须非空
 * @return {unsigned int} 协程 ID 号
 */
unsigned int acl_fiber_id(const ACL_FIBER* fiber);

/**
 * 获得当前所运行的协程的 ID 号
 * @return {unsigned int} 当前运行协程的 ID 号
 */
unsigned int acl_fiber_self(void);

/**
 * 设置所给协程的错误号
 * @param fiber {ACL_FIBER*} 指定的协程对象，为 NULL 则使用当前运行的协程
 * @param errnum {int} 错误号
 */
void acl_fiber_set_errno(ACL_FIBER* fiber, int errnum);

/**
 * 获得指定协程的错误号
 * @param fiber {ACL_FIBER*} 指定的协程对象，若为 NULL 则使用当前协程对象
 * @return {int} 所给协程错误号
 */
int acl_fiber_errno(ACL_FIBER* fiber);

/**
 * 获得当前系统级的 errno 号
 * @return {int}
 */
int acl_fiber_sys_errno(void);

/**
 * 设置当前系统的 errno 号
 * @param errnum {int}
 */
void acl_fiber_sys_errno_set(int errnum);

/**
 * 是否保持所指定协程的错误号，当设置为“保持”后，则该协程仅保持当前状态下的
 * 错误号，之后该协程的错误号 errno 将不再改变，走到再次调用本函数取消保持
 * @param fiber {ACL_FIBER*} 指定的协程对象，为 NULL 则使用当前运行的协程
 * @param yesno {int} 是否保持
 */
void acl_fiber_keep_errno(ACL_FIBER* fiber, int yesno);

/**
 * 获得指定协程的当前状态
 * @param fiber {const ACL_FIBER*} 指定的协程对象，为 NULL 则使用当前协程
 * @return {int} 协程状态
 */
int acl_fiber_status(const ACL_FIBER* fiber);

/**
 * 通知处于休眠状态的协程退出 
 * @param fiber {const ACL_FIBER*} 指定的协程对象，必须非 NULL
 */
void acl_fiber_kill(ACL_FIBER* fiber);

/**
 * 检查本协程是否被其它协程通知退出
 * @param fiber {const ACL_FIBER*} 指定的协程对象，若为 NULL 则自动使用当前
 *  正在运行的协程
 * @return {int} 返回值为 0 表示没有被通知退出，非 0 表示被通知退出
 */
int acl_fiber_killed(ACL_FIBER* fiber);

/**
 * 唤醒因 IO 等原因处于休眠的协程
 * @param fiber {const ACL_FIBER*} 协程对象，必须非 NULL
 * @param signum {int} SIGINT, SIGKILL, SIGTERM ... 参考系统中 bits/signum.h
 */
void acl_fiber_signal(ACL_FIBER* fiber, int signum);

/**
 * 获得其它协程发送给指定协程的信号值
 * @param fiber {const ACL_FIBER*} 指定的协程对象，为 NULL 时则使用当前协程
 * @retur {int} 返回指定协程收到的信号值
 */
int acl_fiber_signum(ACL_FIBER* fiber);

/**
 * 将当前运行的协程挂起，由调度器选择下一个需要运行的协程
 * @return {int}
 */
int acl_fiber_yield(void);

/**
 * 将指定协程对象置入待运行队列中
 * @param fiber {ACL_FIBER*} 指定协程，必须非 NULL
 */
void acl_fiber_ready(ACL_FIBER* fiber);

/**
 * 将当前运行的协程挂起，同时执行等待队列下一个待运行的协程
 */
void acl_fiber_switch(void);

/**
 * 调用本函数启动协程的调度过程
 */
void acl_fiber_schedule(void);

/**
 * 调用本函数检测当前线程是否处于协程调度状态
 * @return {int} 0 表示非协程状态，非 0 表示处于协程调度状态
 */
int acl_fiber_scheduled(void);

/**
 * 停止协程过程
 */
void acl_fiber_schedule_stop(void);

/**
 * 使当前运行的协程休眠指定毫秒数
 * @param milliseconds {unsigned int} 指定要休眠的毫秒数
 * @return {unsigned int} 本协程休眠后再次被唤醒后剩余的毫秒数
 */
unsigned int acl_fiber_delay(unsigned int milliseconds);

/**
 * 使当前运行的协程休眠指定秒数
 * @param seconds {unsigned int} 指定要休眠的秒数
 * @return {unsigned int} 本协程休眠后再次被唤醒后剩余的秒数
 */
unsigned int acl_fiber_sleep(unsigned int seconds);

/**
 * 创建一个协程用作定时器
 * @param milliseconds {unsigned int} 所创建定时器被唤醒的毫秒数
 * @param size {size_t} 所创建协程的栈空间大小
 * @param fn {void (*)(ACL_FIBER*, void*)} 定时器协程被唤醒时的回调函数
 * @param ctx {void*} 回调 fn 函数时的第二个参数
 * @return {ACL_FIBER*} 新创建的定时器协程
 */
ACL_FIBER* acl_fiber_create_timer(unsigned int milliseconds, size_t size,
	void (*fn)(ACL_FIBER*, void*), void* ctx);

/**
 * 在定时器协程未被唤醒前，可以通过本函数重置该协程被唤醒的时间
 * @param timer {ACL_FIBER*} 由 acl_fiber_create_timer 创建的定时器协程
 * @param milliseconds {unsigned int} 指定该定时器协程被唤醒的毫秒数
 */
void acl_fiber_reset_timer(ACL_FIBER* timer, unsigned int milliseconds);

/**
 * 本函数设置 DNS 服务器的地址
 * @param ip {const char*} DNS 服务器 IP 地址
 * @param port {int} DNS 服务器的端口
 */
void acl_fiber_set_dns(const char* ip, int port);

/* for fiber specific */

/**
 * 设定当前协程的局部变量
 * @param key {int*} 协程局部变量的索引键的地址，初始时该值应 <= 0，内部会自动
 *  分配一个 > 0 的索引键，并给该地址赋值，后面的协程可以复用该值设置各自的
 *  局部变量，该指针必须非 NULL
 * @param ctx {void *} 协程局部变量
 * @param free_fn {void (*)(void*)} 当协程退出时会调用此函数释放协程局部变量
 * @return {int} 返回所设置的协程局部变量的键值，返回 -1 表示当前协程不存在
 */
int acl_fiber_set_specific(int* key, void* ctx, void (*free_fn)(void*));

/**
 * 获得当前协程局部变量
 * @param key {int} 由 acl_fiber_set_specific 返回的键值
 * @retur {void*} 返回 NULL 表示不存在
 */
void* acl_fiber_get_specific(int key);

/* fiber locking */

/**
 * 协程互斥锁，线程非安全，只能用在同一线程内
 */
typedef struct ACL_FIBER_MUTEX ACL_FIBER_MUTEX;

/**
 * 协程读写锁，线程非安全，只能用在同一线程内
 */
typedef struct ACL_FIBER_RWLOCK ACL_FIBER_RWLOCK;

/**
 * 创建协程互斥锁，线程非安全，只能用在同一线程内
 * @return {ACL_FIBER_MUTEX*}
 */
ACL_FIBER_MUTEX* acl_fiber_mutex_create(void);

/**
 * 释放协程互斥锁
 * @param l {ACL_FIBER_MUTEX*} 由 acl_fiber_mutex_create 创建的协程互斥锁
 */
void acl_fiber_mutex_free(ACL_FIBER_MUTEX* l);

/**
 * 对协程互斥锁进行阻塞式加锁，如果加锁成功则返回，否则则阻塞
 * @param l {ACL_FIBER_MUTEX*} 由 acl_fiber_mutex_create 创建的协程互斥锁
 */
void acl_fiber_mutex_lock(ACL_FIBER_MUTEX* l);

/**
 * 对协程互斥锁尝试性进行加锁，无论是否成功加锁都会立即返回
 * @param l {ACL_FIBER_MUTEX*} 由 acl_fiber_mutex_create 创建的协程互斥锁
 * @return {int} 如果加锁成功则返回 0 值，否则返回 -1
 */
int acl_fiber_mutex_trylock(ACL_FIBER_MUTEX* l);

/**
 * 加锁成功的协程调用本函数进行解锁，调用本函数的协程必须是该锁的属主，否则
 * 内部会产生断言
 * @param l {ACL_FIBER_MUTEX*} 由 acl_fiber_mutex_create 创建的协程互斥锁
 */
void acl_fiber_mutex_unlock(ACL_FIBER_MUTEX* l);

/**
 * 创建协程读写锁，线程非安全，只能用在同一线程内
 * @return {ACL_FIBER_RWLOCK*}
 */
ACL_FIBER_RWLOCK* acl_fiber_rwlock_create(void);

/**
 * 释放协程读写锁
 * @param l {ACL_FIBER_RWLOCK*} 由 acl_fiber_rwlock_create 创建的读写锁
 */
void acl_fiber_rwlock_free(ACL_FIBER_RWLOCK* l);

/**
 * 对协程读写锁加读锁，如果该锁当前正被其它协程加了读锁，则本协程依然可以
 * 正常加读锁，如果该锁当前正被其它协程加了写锁，则本协程进入阻塞状态，直至
 * 写锁释放
 * @param l {ACL_FIBER_RWLOCK*} 由 acl_fiber_rwlock_create 创建的读写锁
 */
void acl_fiber_rwlock_rlock(ACL_FIBER_RWLOCK* l);

/**
 * 对协程读写锁尝试性加读锁，加锁无论是否成功都会立即返回
 * @param l {ACL_FIBER_RWLOCK*} 由 acl_fiber_rwlock_create 创建的读写锁
 * @retur {int} 返回 1 表示加锁成功，返回 0 表示加锁失败
 */
int acl_fiber_rwlock_tryrlock(ACL_FIBER_RWLOCK* l);

/**
 * 对协程读写锁加写锁，只有当该锁未被任何协程加读/写锁时才会返回，否则阻塞，
 * 直至该锁可加写锁
 * @param l {ACL_FIBER_RWLOCK*} 由 acl_fiber_rwlock_create 创建的读写锁
 */
void acl_fiber_rwlock_wlock(ACL_FIBER_RWLOCK* l);

/**
 * 对协程读写锁尝试性加写锁，无论是否加锁成功都会立即返回
 * @param l {ACL_FIBER_RWLOCK*} 由 acl_fiber_rwlock_create 创建的读写锁
 * @return {int} 返回 1 表示加写锁成功，返回 0 表示加锁失败
 */
int acl_fiber_rwlock_trywlock(ACL_FIBER_RWLOCK* l);

/**
 * 对协程读写锁成功加读锁的协程调用本函数解读锁，调用者必须是之前已成功加读
 * 锁成功的协程
 * @param l {ACL_FIBER_RWLOCK*} 由 acl_fiber_rwlock_create 创建的读写锁
 */
void acl_fiber_rwlock_runlock(ACL_FIBER_RWLOCK* l);
/**
 * 对协程读写锁成功加写锁的协程调用本函数解写锁，调用者必须是之前已成功加写
 * 锁成功的协程
 * @param l {ACL_FIBER_RWLOCK*} 由 acl_fiber_rwlock_create 创建的读写锁
 */
void acl_fiber_rwlock_wunlock(ACL_FIBER_RWLOCK* l);

/* fiber_event.c */

/* 线程安全的协程锁，可以用在不同线程的协程之间及不同线程之间的互斥 */
typedef struct ACL_FIBER_EVENT ACL_FIBER_EVENT;

/**
 * 创建基于事件的协程/线程混合锁
 * @return {ACL_FIBER_EVENT *}
 */
ACL_FIBER_EVENT *acl_fiber_event_create(void);

/**
 * 释放事件锁
 * @param {ACL_FIBER_EVENT *}
 */
void acl_fiber_event_free(ACL_FIBER_EVENT *event);

/**
 * 等待事件锁可用
 * @param {ACL_FIBER_EVENT *}
 * @return {int} 返回 0 表示成功，-1 表示出错
 */
int acl_fiber_event_wait(ACL_FIBER_EVENT *event);

/**
 * 尝试等待事件锁可用
 * @param {ACL_FIBER_EVENT *}
 * @return {int} 返回 0 表示成功，-1 表示锁被占用
 */
int acl_fiber_event_trywait(ACL_FIBER_EVENT *event);

/**
 * 事件锁拥有者通知等待者事件锁可用，则等待者收到通知后则可获得事件锁
 * @param {ACL_FIBER_EVENT *}
 * @return {int} 返回 0 表示成功，-1 表示出错
 */
int acl_fiber_event_notify(ACL_FIBER_EVENT *event);

/* fiber semaphore */

typedef struct ACL_FIBER_SEM ACL_FIBER_SEM;

/**
 * 创建协程信号量，同时内部会将当前线程与该信号量绑定
 * @param num {int} 信号量初始值（必须 >= 0）
 * @return {ACL_FIBER_SEM *}
 */
ACL_FIBER_SEM* acl_fiber_sem_create(int num);

/**
 * 释放协程信号量
 * @param {ACL_FIBER_SEM *}
 */
void acl_fiber_sem_free(ACL_FIBER_SEM* sem);

/**
 * 获得当前协程信号量所绑定的线程 ID
 * @param sem {ACL_FIBER_SEM*} 协程信号量对象
 * @return {acl_pthread_t}
 */
acl_pthread_t acl_fiber_sem_get_tid(ACL_FIBER_SEM* sem);

/**
 * 设置指定协程信号量的的线程 ID，当改变本协程信号量所属的线程时如果等待的协程
 * 数据非 0 则内部自动 fatal，即当协程信号量上等待协程非空时禁止调用本方法
 * @param sem {ACL_FIBER_SEM*} 协程信号量对象
 * @param {acl_pthread_t} 线程 ID
 */
void acl_fiber_sem_set_tid(ACL_FIBER_SEM* sem, acl_pthread_t tid);

/**
 * 当协程信号量 > 0 时使信号量减 1，否则等待信号量 > 0
 * @param sem {ACL_FIBER_SEM *}
 * @retur {int} 返回信号量当前值，如果返回 -1 表明当前线程与协程信号量所属线程
 *  不是同一线程，此时该方法不等待立即返回
 */
int acl_fiber_sem_wait(ACL_FIBER_SEM* sem);

/**
 * 尝试使协程信号量减 1
 * @param sem {ACL_FIBER_SEM *}
 * @retur {int} 成功减 1 时返回值 >= 0，返回 -1 表示当前信号量不可用，或当前
 *  调用者线程与协程信号量所属线程不是同一线程
 */
int acl_fiber_sem_trywait(ACL_FIBER_SEM* sem);

/**
 * 使协程信号量加 1
 * @param sem {ACL_FIBER_SEM *}
 * @retur {int} 返回信号量当前值，返回 -1 表示当前调用者线程与协程信号量所属
 *  线程不是同一线程
 */
int acl_fiber_sem_post(ACL_FIBER_SEM* sem);

/**
 * 获得指定协程信号量的当前值，该值反映了目前等待该信号量的数量
 * @param sem {ACL_FIBER_SEM*}
 * @retur {int}
 */
int acl_fiber_sem_num(ACL_FIBER_SEM* sem);

/* channel communication */

/**
 * 协程间通信的管道
 */
typedef struct ACL_CHANNEL ACL_CHANNEL;

/**
 * 创建协程通信管道
 * @param elemsize {int} 在 ACL_CHANNEL 进行传输的对象的固定尺寸大小（字节）
 * @param bufsize {int} ACL_CHANNEL 内部缓冲区大小，即可以缓存 elemsize 尺寸大小
 *  对象的个数
 * @return {CHANNNEL*}
 */
ACL_CHANNEL* acl_channel_create(int elemsize, int bufsize);

/**
 * 释放由 acl_channel_create 创建的协程通信管道对象
 * @param c {ACL_CHANNEL*} 由 acl_channel_create 创建的管道对象
 */
void acl_channel_free(ACL_CHANNEL* c);

/**
 * 阻塞式向指定 ACL_CHANNEL 中发送指定的对象地址
 * @param c {ACL_CHANNEL*} 由 acl_channel_create 创建的管道对象
 * @param v {void*} 被发送的对象地址
 * @return {int} 返回值 >= 0
 */
int acl_channel_send(ACL_CHANNEL* c, void* v);

/**
 * 非阻塞式向指定 ACL_CHANNEL 中发送指定的对象，内部会根据 acl_channel_create 中指定
 * 的 elemsize 对象大小进行数据拷贝
 * @param c {ACL_CHANNEL*} 由 acl_channel_create 创建的管道对象
 * @param v {void*} 被发送的对象地址
 */
int acl_channel_send_nb(ACL_CHANNEL* c, void* v);

/**
 * 从指定的 ACL_CHANNEL 中阻塞式读取对象，
 * @param c {ACL_CHANNEL*} 由 acl_channel_create 创建的管道对象
 * @param v {void*} 存放结果内容
 * @return {int} 返回值 >= 0 表示成功读到数据
 */
int acl_channel_recv(ACL_CHANNEL* c, void* v);

/**
 * 从指定的 ACL_CHANNEL 中非阻塞式读取对象，无论是否读到数据都会立即返回
 * @param c {ACL_CHANNEL*} 由 acl_channel_create 创建的管道对象
 * @param v {void*} 存放结果内容
 * @return {int} 返回值 >= 0 表示成功读到数据，否则表示未读到数据
 */
int acl_channel_recv_nb(ACL_CHANNEL* c, void* v);

/**
 * 向指定的 ACL_CHANNEL 中阻塞式发送指定对象的地址
 * @param c {ACL_CHANNEL*} 由 acl_channel_create 创建的管道对象
 * @param v {void*} 被发送对象的地址
 * @return {int} 返回值 >= 0
 */
int acl_channel_sendp(ACL_CHANNEL* c, void* v);

/**
 * 从指定的 CHANNLE 中阻塞式接收由 acl_channel_sendp 发送的对象的地址
 * @param c {ACL_CHANNEL*} 由 acl_channel_create 创建的管道对象
 * @return {void*} 返回非 NULL，指定接收到的对象的地址
 */
void* acl_channel_recvp(ACL_CHANNEL* c);

/**
 * 向指定的 ACL_CHANNEL 中非阻塞式发送指定对象的地址
 * @param c {ACL_CHANNEL*} 由 acl_channel_create 创建的管道对象
 * @param v {void*} 被发送对象的地址
 * @return {int} 返回值 >= 0
 */
int acl_channel_sendp_nb(ACL_CHANNEL* c, void* v);

/**
 * 从指定的 CHANNLE 中阻塞式接收由 acl_channel_sendp 发送的对象的地址
 * @param c {ACL_CHANNEL*} 由 acl_channel_create 创建的管道对象
 * @return {void*} 返回非 NULL，指定接收到的对象的地址，如果返回 NULL 表示
 *  没有读到任何对象
 */
void* acl_channel_recvp_nb(ACL_CHANNEL* c);

/**
 * 向指定的 ACL_CHANNEL 中发送无符号长整形数值
 * @param c {ACL_CHANNEL*} 由 acl_channel_create 创建的管道对象
 * @param val {unsigned long} 要发送的数值
 * @return {int} 返回值 >= 0
 */
int acl_channel_sendul(ACL_CHANNEL* c, unsigned long val);

/**
 * 从指定的 ACL_CHANNEL 中接收无符号长整形数值
 * @param c {ACL_CHANNEL*} 由 acl_channel_create 创建的管道对象
 * @return {unsigned long}
 */
unsigned long acl_channel_recvul(ACL_CHANNEL* c);

/**
 * 向指定的 ACL_CHANNEL 中以非阻塞方式发送无符号长整形数值
 * @param c {ACL_CHANNEL*} 由 acl_channel_create 创建的管道对象
 * @param val {unsigned long} 要发送的数值
 * @return {int} 返回值 >= 0
 */
int acl_channel_sendul_nb(ACL_CHANNEL* c, unsigned long val);

/**
 * 从指定的 ACL_CHANNEL 中以非阻塞方式接收无符号长整形数值
 * @param c {ACL_CHANNEL*} 由 acl_channel_create 创建的管道对象
 * @return {unsigned long}
 */
unsigned long acl_channel_recvul_nb(ACL_CHANNEL* c);

/* master fibers server */

/**
 * 基于协程的服务器主函数入口，该模块可以在 acl_master 服务器控制框架下运行
 * @param argc {int} 使用者传入的参数数组 argv 的大小
 * @param argv {char*[]} 参数数组大小
 * @param service {void (*)(ACL_VSTREAM*, void*)} 接收到一个新客户端连接请求
 *  后创建一个协程回调本函数
 * @param ctx {void*} service 回调函数的第二个参数
 * @param name {int} 控制参数列表中的第一个控制参数
 */
void acl_fiber_server_main(int argc, char* argv[],
	void (*service)(void*, ACL_VSTREAM*), void* ctx, int name, ...);
const char* acl_fiber_server_conf(void);

void acl_fiber_chat_main(int argc, char* argv[],
	int (*service)(ACL_VSTREAM*, void*), void* ctx, int name, ...);

/**************************** fiber iostuff *********************************/

ssize_t fiber_read(int fd, void* buf, size_t count);
ssize_t fiber_readv(int fd, const struct iovec* iov, int iovcnt);
ssize_t fiber_recv(int sockfd, void* buf, size_t len, int flags);
ssize_t fiber_recvfrom(int sockfd, void* buf, size_t len, int flags,
	struct sockaddr* src_addr, socklen_t* addrlen);
ssize_t fiber_recvmsg(int sockfd, struct msghdr* msg, int flags);

ssize_t fiber_write(int fd, const void* buf, size_t count);
ssize_t fiber_writev(int fd, const struct iovec* iov, int iovcnt);
ssize_t fiber_send(int sockfd, const void* buf, size_t len, int flags);
ssize_t fiber_sendto(int sockfd, const void* buf, size_t len, int flags,
	const struct sockaddr* dest_addr, socklen_t addrlen);
ssize_t fiber_sendmsg(int sockfd, const struct msghdr* msg, int flags);

/****************************************************************************/

#ifdef __cplusplus
}
#endif

#endif
