Compare commits

...

11 Commits
V0.3 ... master

Author SHA1 Message Date
AaronKonishi 1e7ef0dd95 add igeore rules for mutl_timer
Signed-off-by: AaronKonishi <konishi5202@163.com>

 Changes to be committed:

	new file:   .gitignore
2019-10-16 17:50:31 +08:00
AaronKonishi 01fe162ecf optimize deal speed
Signed-off-by: AaronKonishi <konishi5202@163.com>

 Changes to be committed:

	modified:   source/mt_timer.c
	modified:   source/mt_timer.h
2019-10-16 17:49:38 +08:00
AaronKonishi 6c8d47b9bb close epoll_fd in timer_deinit() function
Signed-off-by: AaronKonishi <konishi5202@163.com>

 Changes to be committed:

	modified:   source/mt_timer.c
2019-10-16 15:00:19 +08:00
极简美 f6286f4885 更新 README.md 2019-06-01 10:38:37 +08:00
AaronKonishi a9ac521d91 change line end CR/LF(Windows) to LF(Unix)
Signed-off-by: AaronKonishi <konishi5202@163.com>
2019-05-31 17:43:25 +08:00
AaronKonishi da4e0bf67e add release callback for userdata
Signed-off-by: AaronKonishi <konishi5202@163.com>
2019-05-31 17:41:28 +08:00
极简美 07764643ac 更新 README.md 2019-05-12 09:42:35 +08:00
极简美 78d1ac1ea6 更新 README.md 2019-05-10 14:48:39 +08:00
极简美 27d2717eab 更新 README.md 2019-05-10 14:48:02 +08:00
极简美 f34faaf2de 更新 README.md
添加作者说明
2019-05-10 14:41:28 +08:00
AaronKonishi 0492ef5479 add cmake support for timer
Signed-off-by: AaronKonishi <konishi5202@163.com>
2019-05-07 10:11:12 +08:00
9 changed files with 535 additions and 404 deletions

12
.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
build

26
CMakeLists.txt Normal file
View File

@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.0)
project(timer)
set(CMAKE_BUILD_TYPE Release)
#set(CMAKE_C_FLAGS_RELEASE "$ENV${CFLAGS} -O3 -Wall")
include_directories(source)
add_subdirectory(source)
add_subdirectory(examples)
# uninstall target
if(NOT TARGET uninstall)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
endif()
include (CTest)
add_test(example examples/example)

View File

@ -1,10 +1,10 @@
# MT_TimerMT译为Multiple或Multi # MT_TimerMT译为Multiple或Multi
#### 介绍 #### 一、介绍
一个Linux下的超级简洁的定时器*利用epoll机制和timerfd新特性实现的多重、多用、多个定时实现*。只需要使用TIMER_CREATE()接口创建一个定时器实体,即**可向其添加成千上万个定时任务,定时任务可达到纳秒级别的精度,且可在同一时间点添加不同的定时任务!**。 一个Linux下的超级简洁的定时器*利用epoll机制和timerfd新特性实现的多重、多用、多个定时任务实现*。只需要使用TIMER_CREATE()接口创建一个定时器实体,即**可向其添加成千上万个定时任务,定时任务可达到纳秒级别的精度,且可在同一时间点添加不同的定时任务!**。
#### 软件接口 #### 二、软件接口
整个定时器包含如下几类接口。 整个定时器包含如下几类接口。
@ -22,12 +22,12 @@ TIMER_DEINIT(name);
3. **添加和删除定时任务** 3. **添加和删除定时任务**
``` ```
TIMER_ADD(name, itimespec, repeat, cb, data); TIMER_ADD(name, itimespec, repeat, cb, data, rb);
TIMER_DEL(name, timerfd); TIMER_DEL(name, timerfd);
``` ```
TIMER_ADD()用于向定时器实例name中添加一个定时,其参数描述如下: TIMER_ADD()用于向定时器实例name中添加一个定时任务,其参数描述如下:
- ittimespec是定时器的定时时间和循环事件,其结构体类型如下: - ittimespec是定时任务的定时时间和循环时间,其结构体类型如下:
``` ```
struct timespec { struct timespec {
time_t tv_sec; // seconds time_t tv_sec; // seconds
@ -39,15 +39,15 @@ struct itimerspec {
}; };
``` ```
其中it_value即是超时时间若想定义周期定时任务则设置it_interval成员若不想定义周期定时则需设置it_interval成员都为0。因此第一次超时和后面周期定时任务是可以使用不同时间的。 其中it_value即是超时时间(相对时间)若想定义周期定时任务则设置it_interval成员若不想定义周期定时任务则需设置it_interval成员都为0。因此第一次超时和后面周期定时任务是可以使用不同时间的。
- repeat是周期定时的重复次数,若设置为**-1代表永远重复0代表一次都不执行**因此repeat应至少为1或者使用-1 - repeat是周期定时任务的重复次数,若设置为**-1代表永远重复0代表一次都不执行**因此repeat应至少为1或者使用-1
- cb为定时任务超时回调函数其类型为void (*timer_callback_t)(void *data) - cb为定时任务超时回调函数其类型为void (*timer_callback_t)(void *data)
- data为定时任务回调函数的参数为void *类型,用户可指定为自己定义的结构体; - data为定时任务回调函数的参数为void *类型,用户可指定为自己定义的结构体;
TIMER_ADD()添加定时任务成功返回新定时任务的文件描述符,失败返回 < 0TIMER_DEL() TIMER_ADD()添加定时任务成功返回新定时任务的文件描述符,失败返回 < 0TIMER_DEL()
4. **查询和清空定时** 4. **查询和清空定时任务**
``` ```
TIMER_COUNT(name); TIMER_COUNT(name);
TIMER_CLEAR(name); TIMER_CLEAR(name);
@ -55,9 +55,9 @@ TIMER_CLEAR(name);
TIMER_COUNT(name)用于查询定时器实例name中现存的定时任务个数TIMER_CLEAR(name)用于清空定时器实例name中的所有定时任务。 TIMER_COUNT(name)用于查询定时器实例name中现存的定时任务个数TIMER_CLEAR(name)用于清空定时器实例name中的所有定时任务。
#### 使用实例 #### 三、使用实例
下面是一个非常简单的使用示例:共创建了两个定时每个第一次超时都是3S后面每隔1S超时一次但第一个定时器频次为8第二个定时器频次为3当所有定时器都超时后,输入回车即可退出: 下面是一个非常简单的使用示例:共创建了两个定时任务每个第一次超时都是3S后面每隔1S超时一次但第一个定时任务频次为8第二个定时任务频次为3当所有定时任务都超时后,输入回车即可退出:
``` ```
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -105,7 +105,11 @@ int main(void)
} }
``` ```
#### 参与贡献 #### 四、赞赏作者
![image](https://images.gitee.com/uploads/images/2019/0510/144101_bc93efba_3026819.jpeg)
#### 五、参与贡献
1. Fork 本仓库 1. Fork 本仓库
2. 新建 Feat_xxx 分支 2. 新建 Feat_xxx 分支
@ -113,7 +117,7 @@ int main(void)
4. 新建 Pull Request 4. 新建 Pull Request
#### 码云特技 #### 六、码云特技
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md 1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com) 2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com)

22
cmake_uninstall.cmake.in Normal file
View File

@ -0,0 +1,22 @@
if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
endif(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
string(REGEX REPLACE "\n" ";" files "${files}")
foreach(file ${files})
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
exec_program(
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval
)
if(NOT "${rm_retval}" STREQUAL 0)
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
endif(NOT "${rm_retval}" STREQUAL 0)
else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
endforeach(file)

10
examples/CMakeLists.txt Normal file
View File

@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.0)
project(examples)
link_libraries(pthread)
aux_source_directory(. SRCS)
add_executable(example ${SRCS})
target_link_libraries(example mttimer)

View File

@ -1,69 +1,69 @@
/* /*
* MIT License * MIT License
* *
* Copyright (c) 2019 @ konishi5202@163.com * Copyright (c) 2019 @ konishi5202@163.com
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include "mt_timer.h" #include "mt_timer.h"
void timeout_handle(void *arg) void timeout_handle(void *arg)
{ {
printf("[%ld]:timeout1\n", time(NULL)); printf("[%ld]:timeout1\n", time(NULL));
} }
void timeout_handler(void *arg) void timeout_handler(void *arg)
{ {
printf("[%ld]:timeout2\n", time(NULL)); printf("[%ld]:timeout2\n", time(NULL));
} }
TIMER_CREATE(test); TIMER_CREATE(test);
int main(void) int main(void)
{ {
int timer; int timer;
struct itimerspec itimespec; struct itimerspec itimespec;
TIMER_INIT(test, 10); TIMER_INIT(test, 10);
itimespec.it_value.tv_sec = 3; itimespec.it_value.tv_sec = 3;
itimespec.it_value.tv_nsec = 0; itimespec.it_value.tv_nsec = 0;
itimespec.it_interval.tv_sec = 1; itimespec.it_interval.tv_sec = 1;
itimespec.it_interval.tv_nsec = 0; itimespec.it_interval.tv_nsec = 0;
timer = TIMER_ADD(test, &itimespec, 8, timeout_handle, NULL); timer = TIMER_ADD(test, &itimespec, 8, timeout_handle, NULL, NULL);
TIMER_ADD(test, &itimespec, 3, timeout_handler, NULL); TIMER_ADD(test, &itimespec, 3, timeout_handler, NULL, NULL);
printf("[%ld]:timer_add : %d\n", time(NULL), TIMER_COUNT(test)); printf("[%ld]:timer_add : %d\n", time(NULL), TIMER_COUNT(test));
sleep(4);//getchar(); sleep(4);//getchar();
TIMER_DEL(test, timer); TIMER_DEL(test, timer);
printf("[%ld]:timer_del : %d\n", time(NULL), TIMER_COUNT(test)); printf("[%ld]:timer_del : %d\n", time(NULL), TIMER_COUNT(test));
TIMER_CLEAR(test); TIMER_CLEAR(test);
printf("[%ld]:timer_clear : %d\n", time(NULL), TIMER_COUNT(test)); printf("[%ld]:timer_clear : %d\n", time(NULL), TIMER_COUNT(test));
getchar(); getchar();
TIMER_DEINIT(test); TIMER_DEINIT(test);
return 0; return 0;
} }

9
source/CMakeLists.txt Normal file
View File

@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.0)
project(mttimer)
add_library(mttimer SHARED mt_timer.c)
set_target_properties(mttimer PROPERTIES VERSION 0.4 SOVERSION 1)
install (TARGETS mttimer DESTINATION lib)
install (FILES mt_timer.h DESTINATION include)
install (FILES uthash.h DESTINATION include)

View File

@ -1,172 +1,211 @@
/* /*
* MIT License * MIT License
* *
* Copyright (c) 2019 @ konishi5202@163.com * Copyright (c) 2019 @ konishi5202@163.com
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
#include <stdio.h> #include <stdio.h>
#include <errno.h> #include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <assert.h>
#include "mt_timer.h" #include <sys/eventfd.h>
int timer_init(MT_TIMER_OBJECT *object, void *(*thread_handler)(void *arg), int max_num) #include "mt_timer.h"
{
object->timer_max = max_num; int timer_init(MT_TIMER_OBJECT *object, void *(*thread_handler)(void *arg), int max_num)
object->timer_active_flag = true; {
object->timer_epoll_fd = epoll_create(max_num); struct epoll_event event;
if(object->timer_epoll_fd < 0) assert(NULL != object);
{ if(true == object->timer_active_flag)
MT_TIMER_PRINT_INL("MT-Timer error: epoll_create failed %s.\n", strerror(errno)); return 0;
return -1;
} object->timer_event_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
if(-1 == object->timer_event_fd)
if(pthread_create(&object->timer_thread_id, NULL, thread_handler, NULL) != 0) {
{ MT_TIMER_PRINT_INL("MT-Timer error: eventfd() failed, %s!\n", strerror(errno));
MT_TIMER_PRINT_INL("MT-Timer error: pthread_create failed %s.\n", strerror(errno)); return -1;
return -1; }
} object->timer_max = max_num;
object->timer_active_flag = true;
return 0; object->timer_epoll_fd = epoll_create(max_num);
} if(object->timer_epoll_fd < 0)
{
void timer_deinit(MT_TIMER_OBJECT *object) MT_TIMER_PRINT_INL("MT-Timer error: epoll_create failed %s.\n", strerror(errno));
{ close(object->timer_event_fd);
struct itimerspec itimespec; object->timer_event_fd = -1;
return -1;
object->timer_active_flag = false; }
itimespec.it_value.tv_sec = 0; event.data.ptr = NULL;
itimespec.it_value.tv_nsec = 50; event.events = EPOLLIN | EPOLLET;
itimespec.it_interval.tv_sec = 0; if(epoll_ctl(object->timer_epoll_fd, EPOLL_CTL_ADD, object->timer_event_fd, &event) < 0)
itimespec.it_interval.tv_nsec = 0; {
fprintf(stderr, "epoll: epoll_ctl ADD failed, %s\n", strerror(errno));
timer_add(object, &itimespec, 0, NULL, NULL); close(object->timer_epoll_fd);
pthread_join(object->timer_thread_id, NULL); close(object->timer_event_fd);
timer_clear(object); object->timer_epoll_fd = -1;
} object->timer_event_fd = -1;
return -1;
int timer_add(MT_TIMER_OBJECT *object, struct itimerspec *itimespec, }
int repeat, timer_callback_t cb, void *data)
{ if(pthread_create(&object->timer_thread_id, NULL, thread_handler, NULL) != 0)
struct epoll_event event; {
MT_TIMER_NODE *handler = NULL; MT_TIMER_PRINT_INL("MT-Timer error: pthread_create failed %s.\n", strerror(errno));
close(object->timer_epoll_fd);
handler = (MT_TIMER_NODE *)malloc(sizeof(MT_TIMER_NODE)); close(object->timer_event_fd);
if(NULL == handler) object->timer_epoll_fd = -1;
{ object->timer_event_fd = -1;
MT_TIMER_PRINT_INL("MT-Timer error: malloc failed %s.\n", strerror(errno)); return -1;
return -1; }
}
return 0;
handler->timer_cb = cb; }
handler->timer_cnt = repeat;
handler->timer_data = data; void timer_deinit(MT_TIMER_OBJECT *object)
handler->timer_fd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC|TFD_NONBLOCK); {
if(handler->timer_fd < 0) uint64_t quit = 0x51;
{ assert(NULL != object);
MT_TIMER_PRINT_INL("MT-Timer error: timerfd_create failed %s.\n", strerror(errno)); if(false == object->timer_active_flag)
free(handler); return ;
return -2; object->timer_active_flag = false;
} usleep(100);
if(timerfd_settime(handler->timer_fd, 0, itimespec, NULL) == -1) write(object->timer_event_fd, &quit, sizeof(uint64_t)); /* wakeup thread */
{ pthread_join(object->timer_thread_id, NULL);
MT_TIMER_PRINT_INL("MT-Timer error: timerfd_settime failed %s.\n", strerror(errno)); timer_clear(object);
close(handler->timer_fd); close(object->timer_epoll_fd);
free(handler); close(object->timer_event_fd);
return -3; object->timer_epoll_fd = -1;
} object->timer_event_fd = -1;
}
event.events = EPOLLIN | EPOLLET;
event.data.ptr = handler; int timer_add(MT_TIMER_OBJECT *object, struct itimerspec *itimespec,
if(epoll_ctl(object->timer_epoll_fd, EPOLL_CTL_ADD, handler->timer_fd, &event) < 0) int repeat, timer_callback_t cb, void *data, timer_release_t rb)
{ {
MT_TIMER_PRINT_INL("MT-Timer error: epoll_ctl ADD failed %s.\n", strerror(errno)); uint64_t quit = 0x51;
close(handler->timer_fd); struct epoll_event event;
free(handler); MT_TIMER_NODE *handler = NULL;
return -4; assert(NULL != object);
} handler = (MT_TIMER_NODE *)malloc(sizeof(MT_TIMER_NODE));
if(NULL == handler)
pthread_rwlock_wrlock(&object->timer_rwlock); {
HASH_ADD_INT(object->timer_head, timer_fd, handler); MT_TIMER_PRINT_INL("MT-Timer error: malloc failed %s.\n", strerror(errno));
pthread_rwlock_unlock(&object->timer_rwlock); return -1;
}
return handler->timer_fd;
} handler->timer_cb = cb;
handler->release_cb = rb;
int timer_del(MT_TIMER_OBJECT *object, int timerfd) handler->timer_cnt = repeat;
{ handler->timer_data = data;
struct epoll_event event; handler->timer_fd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC|TFD_NONBLOCK);
MT_TIMER_NODE *handler = NULL; if(handler->timer_fd < 0)
{
pthread_rwlock_rdlock(&object->timer_rwlock); MT_TIMER_PRINT_INL("MT-Timer error: timerfd_create failed %s.\n", strerror(errno));
HASH_FIND_INT(object->timer_head, &timerfd, handler); free(handler);
pthread_rwlock_unlock(&object->timer_rwlock); return -2;
if(NULL == handler) }
return 0; if(timerfd_settime(handler->timer_fd, 0, itimespec, NULL) == -1)
{
event.data.ptr = (void *)handler; MT_TIMER_PRINT_INL("MT-Timer error: timerfd_settime failed %s.\n", strerror(errno));
event.events = EPOLLIN | EPOLLET; close(handler->timer_fd);
if(epoll_ctl(object->timer_epoll_fd, EPOLL_CTL_DEL, handler->timer_fd, &event) < 0) free(handler);
{ return -3;
MT_TIMER_PRINT_INL("MT-Timer error: epoll_ctl DEL failed %s.\n", strerror(errno)); }
return -1;
} event.events = EPOLLIN | EPOLLET;
event.data.ptr = handler;
pthread_rwlock_wrlock(&object->timer_rwlock); if(epoll_ctl(object->timer_epoll_fd, EPOLL_CTL_ADD, handler->timer_fd, &event) < 0)
HASH_DEL(object->timer_head, handler); {
pthread_rwlock_unlock(&object->timer_rwlock); MT_TIMER_PRINT_INL("MT-Timer error: epoll_ctl ADD failed %s.\n", strerror(errno));
close(handler->timer_fd); close(handler->timer_fd);
free(handler); free(handler);
return -4;
return 0; }
} write(object->timer_event_fd, &quit, sizeof(uint64_t)); /* wakeup thread */
pthread_rwlock_wrlock(&object->timer_rwlock);
int timer_count(MT_TIMER_OBJECT *object) HASH_ADD_INT(object->timer_head, timer_fd, handler);
{ pthread_rwlock_unlock(&object->timer_rwlock);
return HASH_COUNT(object->timer_head);
} return handler->timer_fd;
}
int timer_clear(MT_TIMER_OBJECT *object)
{ int timer_del(MT_TIMER_OBJECT *object, int timerfd)
struct epoll_event event; {
MT_TIMER_NODE *handler = NULL; struct epoll_event event;
MT_TIMER_NODE *handler = NULL;
event.events = EPOLLIN | EPOLLET; assert(NULL != object);
for(handler = object->timer_head; handler != NULL; handler = handler->hh.next) pthread_rwlock_rdlock(&object->timer_rwlock);
{ HASH_FIND_INT(object->timer_head, &timerfd, handler);
event.data.ptr = (void *)handler; pthread_rwlock_unlock(&object->timer_rwlock);
if(epoll_ctl(object->timer_epoll_fd, EPOLL_CTL_DEL, handler->timer_fd, &event) < 0) if(NULL == handler)
{ return 0;
MT_TIMER_PRINT_INL("MT-Timer error: epoll_ctl CLEAR failed %s.\n", strerror(errno));
return -1; event.data.ptr = (void *)handler;
} event.events = EPOLLIN | EPOLLET;
pthread_rwlock_wrlock(&object->timer_rwlock); if(epoll_ctl(object->timer_epoll_fd, EPOLL_CTL_DEL, handler->timer_fd, &event) < 0)
HASH_DEL(object->timer_head, handler); {
pthread_rwlock_unlock(&object->timer_rwlock); MT_TIMER_PRINT_INL("MT-Timer error: epoll_ctl DEL failed %s.\n", strerror(errno));
close(handler->timer_fd); return -1;
free(handler); }
}
return 0; pthread_rwlock_wrlock(&object->timer_rwlock);
} HASH_DEL(object->timer_head, handler);
pthread_rwlock_unlock(&object->timer_rwlock);
close(handler->timer_fd);
if(handler->release_cb)
handler->release_cb(handler->timer_data);
free(handler);
return 0;
}
int timer_count(MT_TIMER_OBJECT *object)
{
assert(NULL != object);
return HASH_COUNT(object->timer_head);
}
int timer_clear(MT_TIMER_OBJECT *object)
{
struct epoll_event event;
MT_TIMER_NODE *handler = NULL;
assert(NULL != object);
event.events = EPOLLIN | EPOLLET;
for(handler = object->timer_head; handler != NULL; handler = handler->hh.next)
{
event.data.ptr = (void *)handler;
if(epoll_ctl(object->timer_epoll_fd, EPOLL_CTL_DEL, handler->timer_fd, &event) < 0)
{
MT_TIMER_PRINT_INL("MT-Timer error: epoll_ctl CLEAR failed %s.\n", strerror(errno));
return -1;
}
pthread_rwlock_wrlock(&object->timer_rwlock);
HASH_DEL(object->timer_head, handler);
pthread_rwlock_unlock(&object->timer_rwlock);
close(handler->timer_fd);
if(handler->release_cb)
handler->release_cb(handler->timer_data);
free(handler);
}
return 0;
}

View File

@ -1,172 +1,181 @@
/* /*
* MIT License * MIT License
* *
* Copyright (c) 2019 @ konishi5202@163.com * Copyright (c) 2019 @ konishi5202@163.com
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
#ifndef _MT_TIMER_H__ #ifndef _MT_TIMER_H__
#define _MT_TIMER_H__ #define _MT_TIMER_H__
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#include <pthread.h> #include <pthread.h>
#include <sys/epoll.h> #include <sys/epoll.h>
#include <sys/timerfd.h> #include <sys/timerfd.h>
#include "uthash.h" #include "uthash.h"
#define DEBUG_ENABLE #define DEBUG_ENABLE
/************************** Debug Config Start **************************/ /************************** Debug Config Start **************************/
#ifdef DEBUG_ENABLE #ifdef DEBUG_ENABLE
#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L #if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
#ifdef EFSM_PRINT #ifdef EFSM_PRINT
#define MT_TIMER_PRINT_INL(...) EFSM_PRINT(__VA_ARGS__) #define MT_TIMER_PRINT_INL(...) EFSM_PRINT(__VA_ARGS__)
#else #else
#define MT_TIMER_PRINT_INL(...) printf(__VA_ARGS__) #define MT_TIMER_PRINT_INL(...) printf(__VA_ARGS__)
#endif #endif
#else #else
#ifdef EFSM_PRINT #ifdef EFSM_PRINT
#define MT_TIMER_PRINT_INL(format, args...) EFSM_PRINT(format, ##args) #define MT_TIMER_PRINT_INL(format, args...) EFSM_PRINT(format, ##args)
#else #else
#define MT_TIMER_PRINT_INL(format, args...) printf(format, ##args) #define MT_TIMER_PRINT_INL(format, args...) printf(format, ##args)
#endif #endif
#endif #endif
#else #else
#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L #if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
#define MT_TIMER_PRINT_INL(...) #define MT_TIMER_PRINT_INL(...)
#else #else
#define MT_TIMER_PRINT_INL(format, args...) #define MT_TIMER_PRINT_INL(format, args...)
#endif #endif
#endif #endif
/************************** Debug Config End **************************/ /************************** Debug Config End **************************/
#ifndef bool #ifndef bool
typedef enum{false, true} bool; typedef enum{false, true} bool;
#define bool bool #define bool bool
#endif #endif
typedef void (*timer_callback_t)(void *data); typedef void (*timer_release_t)(void *data);
typedef void (*timer_callback_t)(void *data);
typedef struct {
int timer_fd; typedef struct {
int timer_cnt; int timer_fd;
void *timer_data; int timer_cnt;
UT_hash_handle hh; void *timer_data;
timer_callback_t timer_cb; UT_hash_handle hh;
}MT_TIMER_NODE; /* MT mean multiple */ timer_callback_t timer_cb;
timer_release_t release_cb;
typedef struct { }MT_TIMER_NODE; /* MT mean multiple */
int timer_max;
int timer_epoll_fd; typedef struct {
bool timer_active_flag; int timer_max;
MT_TIMER_NODE *timer_head; int timer_epoll_fd;
pthread_t timer_thread_id; int timer_event_fd;
pthread_rwlock_t timer_rwlock; bool timer_active_flag;
}MT_TIMER_OBJECT; MT_TIMER_NODE *timer_head;
pthread_t timer_thread_id;
#define TIMER_THREAD_CREATE(name) \ pthread_rwlock_t timer_rwlock;
static void *mt_timer_thread_##name(void *arg) \ }MT_TIMER_OBJECT;
{ \
int nfds, i; \ #define TIMER_THREAD_CREATE(name) \
char buf[128]; \ static void *mt_timer_thread_##name(void *arg) \
struct epoll_event event; \ { \
MT_TIMER_NODE *timer_node = NULL; \ int nfds, i; \
struct epoll_event events[mt_timer_##name.timer_max]; \ uint64_t u64; \
/*pthread_detach(pthread_self());*/ \ char buf[128]; \
event.events = EPOLLIN | EPOLLET; \ struct epoll_event event; \
MT_TIMER_PRINT_INL("MT-Timer Info: %s thread is running.\n", #name); \ MT_TIMER_NODE *timer_node = NULL; \
while(mt_timer_##name.timer_active_flag) \ struct epoll_event events[mt_timer_##name.timer_max]; \
{ \ /*pthread_detach(pthread_self());*/ \
nfds = epoll_wait(mt_timer_##name.timer_epoll_fd, events, mt_timer_##name.timer_max, -1); \ event.events = EPOLLIN | EPOLLET; \
if(nfds <= 0) \ MT_TIMER_PRINT_INL("MT-Timer Info: %s thread is running.\n", #name); \
continue; \ while(mt_timer_##name.timer_active_flag) \
for(i = 0; i < nfds; i++) \ { \
{ \ nfds = epoll_wait(mt_timer_##name.timer_epoll_fd, events, mt_timer_##name.timer_max, -1); \
timer_node = (MT_TIMER_NODE *)events[i].data.ptr; \ if(nfds <= 0) \
if(NULL == timer_node) \ continue; \
continue; \ for(i = 0; i < nfds; i++) \
while(read(timer_node->timer_fd, buf, sizeof(buf)) > 0); \ { \
if(NULL == timer_node->timer_cb) \ timer_node = (MT_TIMER_NODE *)events[i].data.ptr; \
continue; \ if(NULL == timer_node) \
if(-1 == timer_node->timer_cnt) \ { \
timer_node->timer_cb(timer_node->timer_data); \ read(mt_timer_##name.timer_event_fd, &u64, sizeof(uint64_t)); \
else \ continue; \
{ \ } \
if(timer_node->timer_cnt) \ while(read(timer_node->timer_fd, buf, sizeof(buf)) > 0); \
{ \ if(NULL == timer_node->timer_cb) \
timer_node->timer_cb(timer_node->timer_data); \ continue; \
timer_node->timer_cnt--; \ if(-1 == timer_node->timer_cnt) \
} \ timer_node->timer_cb(timer_node->timer_data); \
else \ else \
{ \ { \
event.data.ptr = (void *)timer_node; \ if(timer_node->timer_cnt) \
epoll_ctl(mt_timer_##name.timer_epoll_fd, EPOLL_CTL_DEL, timer_node->timer_fd, &event); \ { \
pthread_rwlock_wrlock(&mt_timer_##name.timer_rwlock); \ timer_node->timer_cb(timer_node->timer_data); \
HASH_DEL(mt_timer_##name.timer_head, timer_node); \ timer_node->timer_cnt--; \
pthread_rwlock_unlock(&mt_timer_##name.timer_rwlock); \ } \
close(timer_node->timer_fd); \ else \
free(timer_node); \ { \
} \ event.data.ptr = (void *)timer_node; \
} \ epoll_ctl(mt_timer_##name.timer_epoll_fd, EPOLL_CTL_DEL, timer_node->timer_fd, &event); \
} \ pthread_rwlock_wrlock(&mt_timer_##name.timer_rwlock); \
} \ HASH_DEL(mt_timer_##name.timer_head, timer_node); \
MT_TIMER_PRINT_INL("MT-Timer Info: %s thread is exit.\n", #name); \ pthread_rwlock_unlock(&mt_timer_##name.timer_rwlock); \
pthread_exit(NULL); \ close(timer_node->timer_fd); \
} if(timer_node->release_cb) \
timer_node->release_cb(timer_node->timer_data); \
extern int timer_init(MT_TIMER_OBJECT *object, void *(*thread_handler)(void *arg), int max_num); free(timer_node); \
extern void timer_deinit(MT_TIMER_OBJECT *object); } \
extern int timer_add(MT_TIMER_OBJECT *object, struct itimerspec *itimespec, } \
int repeat, timer_callback_t cb, void *data); } \
extern int timer_del(MT_TIMER_OBJECT *object, int timerfd); } \
extern int timer_count(MT_TIMER_OBJECT *object); MT_TIMER_PRINT_INL("MT-Timer Info: %s thread is exit.\n", #name); \
extern int timer_clear(MT_TIMER_OBJECT *object); pthread_exit(NULL); \
}
/************************** API Interface **************************/
#define TIMER_CREATE(name) \ extern int timer_init(MT_TIMER_OBJECT *object, void *(*thread_handler)(void *arg), int max_num);
MT_TIMER_OBJECT mt_timer_##name = {0, -1, false, NULL, 0, PTHREAD_RWLOCK_INITIALIZER}; \ extern void timer_deinit(MT_TIMER_OBJECT *object);
TIMER_THREAD_CREATE(name) extern int timer_add(MT_TIMER_OBJECT *object, struct itimerspec *itimespec,
#define TIMER_DECLEAR(name) \ int repeat, timer_callback_t cb, void *data, timer_release_t rb);
extern MT_TIMER_OBJECT mt_timer_##name extern int timer_del(MT_TIMER_OBJECT *object, int timerfd);
#define TIMER_INIT(name, max) \ extern int timer_count(MT_TIMER_OBJECT *object);
timer_init(&mt_timer_##name, mt_timer_thread_##name, max) extern int timer_clear(MT_TIMER_OBJECT *object);
#define TIMER_DEINIT(name) \
timer_deinit(&mt_timer_##name) /************************** API Interface **************************/
#define TIMER_ADD(name, itimespec, repeat, cb, data) \ #define TIMER_CREATE(name) \
timer_add(&mt_timer_##name, itimespec, repeat, cb, data) MT_TIMER_OBJECT mt_timer_##name = {0, -1, -1, false, NULL, 0, PTHREAD_RWLOCK_INITIALIZER}; \
#define TIMER_DEL(name, timerfd) \ TIMER_THREAD_CREATE(name)
timer_del(&mt_timer_##name, timerfd) #define TIMER_DECLEAR(name) \
#define TIMER_COUNT(name) timer_count(&mt_timer_##name) extern MT_TIMER_OBJECT mt_timer_##name
#define TIMER_CLEAR(name) timer_clear(&mt_timer_##name) #define TIMER_INIT(name, max) \
timer_init(&mt_timer_##name, mt_timer_thread_##name, max)
#ifdef __cplusplus #define TIMER_DEINIT(name) \
} timer_deinit(&mt_timer_##name)
#endif #define TIMER_ADD(name, itimespec, repeat, cb, data, rb) \
timer_add(&mt_timer_##name, itimespec, repeat, cb, data, rb)
#endif // _MT_TIMER_H__ #define TIMER_DEL(name, timerfd) \
timer_del(&mt_timer_##name, timerfd)
#define TIMER_COUNT(name) timer_count(&mt_timer_##name)
#define TIMER_CLEAR(name) timer_clear(&mt_timer_##name)
#ifdef __cplusplus
}
#endif
#endif // _MT_TIMER_H__