/************************************************************************ * AUTHOR: NiuJiuRu * FILENAME: swsignal.c * DESCRIPTION: 信号量 * NOTE: 主要用于状态通知 * HISTORY: * 1, [2010-12-17] created by NiuJiuRu * 2, [2025-11-24] 优化修改"sw_signal_wait()"函数, 避免依赖系统时间, 导致的 * 超时误差或跳变问题 ***********************************************************************/ #include "swapi.h" #include "swmem.h" #include "swsignal.h" /* 创建信号量 */ void *sw_signal_create() { sem_t *sem = NULL; sem = (sem_t *)sw_heap_malloc(sizeof(sem_t)); if(sem) { memset(sem, 0, sizeof(sem_t)); sem_init(sem, 0, 0); } return sem; } /* 销毁信号量 */ void sw_signal_destroy(void *hSignal) { sem_t *sem = (sem_t *)hSignal; sem_destroy(sem); sw_heap_free(sem); } /* 等待信号量, timeout(ms) = -1时表示无限等待 */ int sw_signal_wait(void *hSignal, int timeout) { sem_t *sem = (sem_t *)hSignal; int ret; if(timeout < 0) { wait_p1: ret = sem_wait(sem); if(ret < 0 && errno == EINTR) goto wait_p1; else return ret; } else { #if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 30) struct timespec ts, now; clock_gettime(CLOCK_MONOTONIC/*单调时钟, 不受系统时间影响*/, &now); ts.tv_sec = now.tv_sec + timeout/1000; ts.tv_nsec = now.tv_nsec + (timeout%1000)*1000*1000; if(ts.tv_nsec >= (1000*1000*1000)) { ts.tv_sec += ts.tv_nsec/(1000*1000*1000); ts.tv_nsec %= (1000*1000*1000); } wait_p2: ret = sem_clockwait(sem, CLOCK_MONOTONIC, &ts); // 非POSIX标准, GNU扩展, 需定义:"_GNU_SOURCE"支持 // , 同时glic库版本需大于等于2.30 if(ret < 0 && errno == EINTR) goto wait_p2; else return ret; #else struct timespec start, now; struct timespec ts = { 0, 1000*1000 }; clock_gettime(CLOCK_MONOTONIC, &start); while((ret = sem_trywait(sem)) < 0 && errno == EAGAIN) { clock_gettime(CLOCK_MONOTONIC, &now); long elapsed = (now.tv_sec - start.tv_sec)*1000 + (now.tv_nsec-start.tv_nsec)/(1000*1000); if(elapsed >= timeout) { errno = ETIMEDOUT; return -1; } nanosleep(&ts, NULL); } return ret; #endif } } /* 点亮信号量 */ void sw_signal_give(void *hSignal) { sem_t *sem = (sem_t *)hSignal; sem_post(sem); }