Files

272 lines
7.5 KiB
C

#include "../inc/mq.h"
#include <string.h> //for memcpy
#include <stdlib.h> //for malloc free
#include <sys/time.h> //for struct timeval gettimeofday
#include <signal.h> //for signal sigset_t sigfillset pthread_sigmask SIG*
#include <sys/msg.h> //for msgget msgsnd msgrcv
#include <unistd.h> //for pipe write read close
#include <pthread.h> //for pthread*
#include <errno.h> //for errno E*
#include "../inc/rbtree.h"
#include <stdio.h>
#define QUE_MSGTYPE 1l
static int timer_alive = 0;
static pthread_t timer_tid;
static pthread_mutex_t timer_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t timer_cond = PTHREAD_COND_INITIALIZER;
static rbtree_t timer_root;
typedef struct timer_node_s timer_node_t;
struct timer_node_s{
pthread_t tid;
uint64_t expire;
int status;//0 default, 1 timeout
rbtree_node_t rbtree_node;
};
int is_thread_alive(pthread_t tid){
int kill_rc = pthread_kill(tid,0);//0 success, ESRCH 线程不存在, EINVAL 信号不合法
int ret = 1;//alive
if(kill_rc == ESRCH){
ret = 0;
}
return ret;
}
static void* timer_loop(void*arg);
static int timer_create_new(){
if(timer_alive == 1){
return 0;
}
pthread_mutex_lock(&timer_mutex);
if(timer_alive == 1){
pthread_mutex_unlock(&timer_mutex);
return 0;
}
rbtree_init(&timer_root);
pthread_create(&timer_tid,NULL,timer_loop,NULL);
timer_alive = 1;
pthread_detach(timer_tid);//分离线程
pthread_mutex_unlock(&timer_mutex);
return 0;
}
static void* timer_loop(void*arg){
sigset_t sigset;
sigfillset(&sigset);
pthread_sigmask(SIG_SETMASK,&sigset,NULL);//当前线程屏蔽所有信号
uint64_t now;
struct timespec now_ts;
struct timespec timeout_ts;
rbtree_node_t *rbtree_node;
timer_node_t *timer_node=NULL,*timer_node_will_expire=NULL;
for(;;){
//加锁
pthread_mutex_lock(&timer_mutex);
clock_gettime(CLOCK_REALTIME, &now_ts);
now = (uint64_t)((uint64_t)now_ts.tv_sec*1000) + (uint64_t)((uint64_t)now_ts.tv_nsec/1000000);
timer_node = NULL;
timer_node_will_expire = NULL;
for(rbtree_node = rbtree_min(&timer_root,timer_root.root);
rbtree_node!=NULL;
rbtree_node = rbtree_next(&timer_root,rbtree_node)
){
timer_node = rbtree_data(rbtree_node,timer_node_t,rbtree_node);
//printf("[tim]loop now=%llu,exp=%llu\n",now,timer_node->expire);
if(timer_node->expire <= now){
printf("[tim]kill loop now=%llu,exp=%llu\n",now,timer_node->expire);
//超时, 则信号通知
timer_node->status = 1;
pthread_kill(timer_node->tid,SIGUSR1);
continue;
}
timer_node_will_expire = timer_node;
printf("[tim]loop now=%llu,exp=%llu\n",now,timer_node->expire);
break;
}
//条件等待
if(timer_node_will_expire == NULL){
pthread_cond_wait(&timer_cond,&timer_mutex);
}else{
//printf("[tim].will=%llu\n",timer_node_will_expire->expire);
timeout_ts.tv_sec = (uint64_t)((uint64_t)timer_node_will_expire->expire/1000);
timeout_ts.tv_nsec = (uint64_t)((uint64_t)timer_node_will_expire->expire%1000)*1000000;
printf("[tim]con sec=%llu,nsec=%llu\n",(uint64_t)timeout_ts.tv_sec, (uint64_t)timeout_ts.tv_nsec);
pthread_cond_timedwait(&timer_cond,&timer_mutex,&timeout_ts);//注意, 时间不能是NULL
}
//解锁
pthread_mutex_unlock(&timer_mutex);
}
timer_alive = 0;
pthread_exit(NULL);
}
int add_timer(timer_node_t *node,size_t timeout){//单位ms
node->tid = pthread_self();
node->status = 0;
struct timespec ts;
uint64_t lldata;
clock_gettime(CLOCK_REALTIME, &ts);
node->expire = (uint64_t)(((uint64_t)ts.tv_sec*1000)+ (uint64_t)timeout + (uint64_t)((uint64_t)ts.tv_nsec/1000000));
node->rbtree_node.key = node->expire;
lldata = (uint64_t)(((uint64_t)ts.tv_sec*1000) + (uint64_t)((uint64_t)ts.tv_nsec/1000000));
printf("[rec].time_exp=%llu\n",node->expire);
printf("[rec].time_cur=%llu\n",lldata);
pthread_mutex_lock(&timer_mutex);
rbtree_insert(&timer_root,&node->rbtree_node);
pthread_cond_signal(&timer_cond);
pthread_mutex_unlock(&timer_mutex);
//pthread_cond_signal(&timer_cond);
return 0;
}
int delete_timer(timer_node_t *node){
pthread_mutex_lock(&timer_mutex);
rbtree_delete(&timer_root,&node->rbtree_node);
pthread_mutex_unlock(&timer_mutex);
return 0;
}
int _signal(int signo, void (*sighandler)(int))
{
struct sigaction sa;
sigset_t set;
//sigemptyset(&set);//当进入sighandler时,不屏蔽所有信号,可被其他信号中断
sigfillset(&set);//当进入sighandler时,屏蔽所有信号
sa.sa_mask = set;
sa.sa_handler = sighandler;
sa.sa_restorer = NULL;
sa.sa_flags = 0;
return sigaction(signo, &sa, NULL);
}
void timer_sighandler(int signo){
return;
}
typedef struct {
long msgtype;
unsigned char buffer[0];
} MSGBUF;
ssize_t send_que_timedwait(key_t qid,void* pdata,size_t len,int timeout){
int msgqid,retu;
timer_node_t node = {0};
unsigned char* buf = NULL;
MSGBUF* que_msgbuf;
//https://man7.org/linux/man-pages/man2/msgget.2.html
msgqid=msgget(qid,0666);
if(msgqid == -1){
if(errno == ENOENT){
msgqid=msgget(qid,IPC_CREAT|0666);
}
if(msgqid == -1)
return -1;
}
buf = malloc(sizeof(MSGBUF) + len);
if(buf == NULL)
return -1;
que_msgbuf = (MSGBUF*)buf;
que_msgbuf->msgtype = QUE_MSGTYPE;
memcpy(que_msgbuf->buffer,pdata,len);
// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
// msgflag
// 0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列
// IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回
// IPC_NOERROR:若发送的消息大于size字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程。
// 返回值
// 成功:0
// 出错:-1,错误原因存于error中
// EAGAIN:参数msgflg设为IPC_NOWAIT,而消息队列已满
// EIDRM:标识符为msqid的消息队列已被删除
// EACCESS:无权限写入消息队列
// EFAULT:参数msgp指向无效的内存地址
// EINTR:队列已满而处于等待情况下被信号中断
// EINVAL:无效的参数msqid、msgsz或参数消息类型type小于0
if(timeout == -1){
retu=msgsnd(msgqid,buf,len,0);
//printf("");
}
else if(timeout == 0){
retu=msgsnd(msgqid,buf,len,IPC_NOWAIT);
}else{
timer_create_new();
_signal(SIGUSR1,timer_sighandler);
add_timer(&node,timeout);
retu=msgsnd(msgqid,buf,len,0);
delete_timer(&node);
if(errno == EINTR && node.status == 1){
errno = ETIMEDOUT;
}
}
free(buf);
return retu;
}
ssize_t read_que_timedwait(key_t qid,void* pdata,size_t len,int timeout){
int msgqid,retu;
timer_node_t node = {0};
unsigned char* buf = NULL;
MSGBUF* que_msgbuf = NULL;
msgqid=msgget(qid,0666);//https://man7.org/linux/man-pages/man2/msgget.2.html
if(msgqid == -1){
if(errno == ENOENT){
msgqid=msgget(qid,IPC_CREAT|0666);
}
if(msgqid == -1)
printf("msgget err.\n");
return -1;
}
buf = malloc(sizeof(MSGBUF) + len);
if(buf == NULL){
printf("malloc err.\n");
return -1;
}
if(timeout == -1){
retu=msgrcv(msgqid,buf,len,0,0);
}
else if(timeout == 0){
retu=msgrcv(msgqid,buf,len,0,IPC_NOWAIT);
}else{
timer_create_new();
_signal(SIGUSR1,timer_sighandler);
add_timer(&node,timeout);
retu=msgrcv(msgqid,buf,len,0,0);
delete_timer(&node);
if(errno == EINTR && node.status == 1){
errno = ETIMEDOUT;
}
}
que_msgbuf = (MSGBUF*)buf;
if(retu > 0){
memcpy(pdata,que_msgbuf->buffer,retu);
}
free(buf);
return retu;
}