Merge branch 'systime-dev' into 'dev'
[ADD] systime dbus signal See merge request kysdk/kysdk-system!1
This commit is contained in:
commit
6eb7f6d723
|
@ -0,0 +1,19 @@
|
|||
FILE(GLOB SRC_LIST_C ${PROJECT_SOURCE_DIR}/src/*.c)
|
||||
FILE(GLOB TIMER_LIST_C ${PROJECT_SOURCE_DIR}/src/timer/*.c)
|
||||
include(FindPkgConfig)
|
||||
pkg_check_modules(PKGS REQUIRED
|
||||
dbus-1
|
||||
glib-2.0
|
||||
dbus-glib-1
|
||||
)
|
||||
message("pkg cflags: ${PKGS_CFLAGS}")
|
||||
message ("pkg include dirs: ${PKGS_INCLUDE_DIRS}")
|
||||
find_library(GLIBC_LIB glib-2.0)
|
||||
message("library :${GLIBC_LIB}")
|
||||
find_library(DBUS_LIB dbus-1)
|
||||
find_library(DBUS_GLIB_LIB dbus-glib-1)
|
||||
find_library(THREAD_LIB pthread)
|
||||
include_directories(${PKGS_INCLUDE_DIRS})
|
||||
SET( EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
|
||||
ADD_EXECUTABLE(systime m_systime.c)
|
||||
target_link_libraries(systime ${GLIBC_LIB} ${DBUS_LIB} ${DBUS_GLIB_LIB} ${THREAD_LIB} ${TIMER_LIST_C})
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
FILE=$(file)
|
||||
CFLAGS=$(cflags)
|
||||
CFLAGS_DBUS = $(shell pkg-config --cflags --libs dbus-1)
|
||||
CFLAGS_DBUS_GLIB = $(shell pkg-config --cflags --libs dbus-glib-1)
|
||||
CFLAGS += -g -Wall -Werror -O0 -std=gnu11 -Wall
|
||||
|
||||
|
||||
CLIBS=-lpthread -pthread -lrt
|
||||
CLIBS += $(clibs)
|
||||
|
||||
all: m_systime
|
||||
|
||||
m_systime: m_systime.c ./timer/libkytimer.c
|
||||
gcc m_systime.c ./timer/libkytimer.c -o $@ $(CFLAGS) $(CFLAGS_DBUS) $(CFLAGS_DBUS_GLIB) $(CLIBS)
|
||||
|
||||
|
||||
clean:
|
||||
rm -f m_systime
|
||||
|
||||
.PHONY: all clean
|
Binary file not shown.
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
|
||||
|
||||
<!DOCTYPE busconfig PUBLIC
|
||||
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
<busconfig>
|
||||
|
||||
<!-- Only user root can own the alsa service -->
|
||||
<policy user="root">
|
||||
<allow own="com.kylin.kysdk.TimeServer" />
|
||||
<allow send_destination="com.kylin.kysdk.TimeServer" />
|
||||
<allow receive_sender="com.kylin.kysdk.TimeServer" />
|
||||
</policy>
|
||||
|
||||
<policy context="default">
|
||||
<allow send_destination="com.kylin.kysdk.TimeServer" />
|
||||
<!-- remove the following line for debug -->
|
||||
<!--
|
||||
<allow send_interface="com.jd.ccis"/>
|
||||
-->
|
||||
</policy>
|
||||
|
||||
</busconfig>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
[Unit]
|
||||
Description=KYLIN SDK SYSTIME DBUS
|
||||
|
||||
[Service]
|
||||
Type=dbus
|
||||
Restart=always
|
||||
RestartSec=0
|
||||
BusName=com.kylin.kysdk.TimeServer
|
||||
ExecStart=/usr/bin/systime
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1,479 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <dbus/dbus.h>
|
||||
#include <dbus/dbus-glib-lowlevel.h> /* for glib main loop */
|
||||
|
||||
#include "m_systime.h"
|
||||
#include "./timer/libkytimer.h"
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <semaphore.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <errno.h>
|
||||
|
||||
pthread_mutex_t lock;
|
||||
u_int8_t g_Flag;
|
||||
|
||||
u_int8_t g_Quit;
|
||||
sem_t g_Wait;
|
||||
|
||||
u_int8_t g_TimeChanged;
|
||||
u_int8_t g_TimeSync;
|
||||
|
||||
void sig_Handler(int sig)
|
||||
{
|
||||
g_Quit = 1;
|
||||
}
|
||||
|
||||
static void *printClock(void *ptr)
|
||||
{
|
||||
DBusConnection *conn = ptr;
|
||||
DBusMessage *msg;
|
||||
DBusMessageIter args;
|
||||
// DBusError err;
|
||||
dbus_uint32_t serial = 0;
|
||||
struct tm *now;
|
||||
time_t current;
|
||||
time(¤t);
|
||||
now = localtime(¤t);
|
||||
|
||||
// printf("%04d/%02d/%02d %02d:%02d:%02d\n", now->tm_year + 1900, now->tm_mon, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
|
||||
// printf("g_TimeChanged is %d\n", g_TimeChanged);
|
||||
// printf("%d",now->tm_sec);
|
||||
// 如果时间发生改变发送TimeVhangeSignal信号
|
||||
if (g_TimeChanged == 1)
|
||||
{
|
||||
char *buf = calloc(1, 128);
|
||||
sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d", now->tm_year + 1900, now->tm_mon, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
|
||||
printf("%s\n", buf);
|
||||
|
||||
msg = dbus_message_new_signal("/com/kylin/kysdk/Timer",
|
||||
"com.kylin.kysdk.TimeInterface",
|
||||
"TimeChangeSignal");
|
||||
if (NULL == msg)
|
||||
{
|
||||
fprintf(stderr, "Message Null\n");
|
||||
}
|
||||
// append arguments onto signal
|
||||
dbus_message_iter_init_append(msg, &args);
|
||||
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &buf);
|
||||
dbus_connection_send(conn, msg, &serial);
|
||||
dbus_connection_flush(conn);
|
||||
dbus_message_unref(msg);
|
||||
}
|
||||
// 非整点情况
|
||||
|
||||
if (now->tm_sec != 0)
|
||||
{
|
||||
pthread_mutex_lock(&lock);
|
||||
|
||||
// 如果出现了非整点的报时,且
|
||||
// 之前处于整分钟报时的情况,说明
|
||||
// 出现了唤醒偏差。重新执行一下对时
|
||||
if (g_Flag == 1)
|
||||
{
|
||||
g_Flag = 0;
|
||||
g_TimeSync = 1;
|
||||
}
|
||||
pthread_mutex_unlock(&lock);
|
||||
sem_post(&g_Wait);
|
||||
}
|
||||
// 整点的情况发送TimeSignal
|
||||
else
|
||||
{
|
||||
char *buf = calloc(1, 128);
|
||||
sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d", now->tm_year + 1900, now->tm_mon, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
|
||||
// printf("%s\n", buf);
|
||||
|
||||
msg = dbus_message_new_signal("/com/kylin/kysdk/Timer",
|
||||
"com.kylin.kysdk.TimeInterface",
|
||||
"TimeSignal");
|
||||
if (NULL == msg)
|
||||
{
|
||||
fprintf(stderr, "Message Null\n");
|
||||
}
|
||||
// append arguments onto signal
|
||||
dbus_message_iter_init_append(msg, &args);
|
||||
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &buf);
|
||||
dbus_connection_send(conn, msg, &serial);
|
||||
dbus_connection_flush(conn);
|
||||
dbus_message_unref(msg);
|
||||
if (g_Flag == 0)
|
||||
{
|
||||
pthread_mutex_lock(&lock);
|
||||
g_Flag = (1 & ~g_TimeChanged);
|
||||
pthread_mutex_unlock(&lock);
|
||||
sem_post(&g_Wait);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
void *startBroadcastSystemTimePerMin(void *tmp)
|
||||
{
|
||||
DBusConnection *conn = tmp;
|
||||
size_t timerID = -1;
|
||||
while (!g_Quit)
|
||||
{
|
||||
sem_wait(&g_Wait);
|
||||
|
||||
if (g_TimeChanged || g_TimeSync)
|
||||
{
|
||||
printf("Get Time Changed signal or mis-synced. stop timerID %zd\n", timerID);
|
||||
kyStopTimer(timerID);
|
||||
g_TimeChanged = 0;
|
||||
g_TimeSync = 0;
|
||||
timerID = -1;
|
||||
}
|
||||
|
||||
if (!g_Flag)
|
||||
kyStartTimer(200, printClock, KTIMER_SINGLESHOT, KTIMER_RELATIVE, conn, 0);
|
||||
else
|
||||
{
|
||||
timerID = kyStartTimer(1000 * 60, printClock, KTIMER_PERIODIC, KTIMER_RELATIVE, conn, 0);
|
||||
printf("start periodic timer with ID %zd\n", timerID);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int monitorSystemTimeChange()
|
||||
{
|
||||
#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
|
||||
|
||||
struct itimerspec its = {.it_value.tv_sec = TIME_T_MAX};
|
||||
int fd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC);
|
||||
if (fd < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (timerfd_settime(fd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0)
|
||||
{
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
u_int64_t dep;
|
||||
ssize_t ret = read(fd, &dep, sizeof(u_int64_t));
|
||||
if (ret == -1 && errno == ECANCELED)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *actionTimeChanged(void *ptr)
|
||||
{
|
||||
DBusConnection *conn = ptr;
|
||||
while (!g_Quit)
|
||||
{
|
||||
if (monitorSystemTimeChange() == 1)
|
||||
{
|
||||
printf("System Time Changed.\n");
|
||||
g_TimeChanged = 1;
|
||||
g_Flag = 0;
|
||||
printClock(conn);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *version = "0.1";
|
||||
GMainLoop *mainloop;
|
||||
|
||||
/*
|
||||
* This implements 'Get' method of DBUS_INTERFACE_PROPERTIES so a
|
||||
* client can inspect the properties/attributes of 'TestInterface'.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is the XML string describing the interfaces, methods and
|
||||
* signals implemented by our 'Server' object. It's used by the
|
||||
* 'Introspect' method of 'org.freedesktop.DBus.Introspectable'
|
||||
* interface.
|
||||
*
|
||||
* Currently our tiny server implements only 3 interfaces:
|
||||
*
|
||||
* - org.freedesktop.DBus.Introspectable
|
||||
* - org.freedesktop.DBus.Properties
|
||||
* - org.example.TestInterface
|
||||
*
|
||||
* 'org.example.TestInterface' offers 3 methods:
|
||||
*
|
||||
* - Ping(): makes the server answering the string 'Pong'.
|
||||
* It takes no arguments.
|
||||
*
|
||||
* - Echo(): replies the passed string argument.
|
||||
*
|
||||
* - EmitSignal(): send a signal 'OnEmitSignal'
|
||||
*
|
||||
* - Quit(): makes the server exit. It takes no arguments.
|
||||
*/
|
||||
const char *server_introspection_xml =
|
||||
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
|
||||
"<node>\n"
|
||||
|
||||
" <interface name='org.freedesktop.DBus.Introspectable'>\n"
|
||||
" <method name='Introspect'>\n"
|
||||
" <arg name='data' type='s' direction='out' />\n"
|
||||
" </method>\n"
|
||||
" </interface>\n"
|
||||
|
||||
" <interface name='org.freedesktop.DBus.Properties'>\n"
|
||||
" <method name='Get'>\n"
|
||||
" <arg name='interface' type='s' direction='in' />\n"
|
||||
" <arg name='property' type='s' direction='in' />\n"
|
||||
" <arg name='value' type='s' direction='out' />\n"
|
||||
" </method>\n"
|
||||
" <method name='GetAll'>\n"
|
||||
" <arg name='interface' type='s' direction='in'/>\n"
|
||||
" <arg name='properties' type='a{sv}' direction='out'/>\n"
|
||||
" </method>\n"
|
||||
" </interface>\n"
|
||||
|
||||
" <interface name='com.kylin.kysdk.TimeInterface'>\n"
|
||||
" <signal name='TimeSignal'>\n"
|
||||
" <arg name='tim' type='s' />\n"
|
||||
" </signal>"
|
||||
" <signal name='TimeChangeSignal'>\n"
|
||||
" <arg name='tim' type='s' />\n"
|
||||
" </signal>"
|
||||
" </interface>\n"
|
||||
|
||||
"</node>\n";
|
||||
|
||||
/*
|
||||
* This implements 'Get' method of DBUS_INTERFACE_PROPERTIES so a
|
||||
* client can inspect the properties/attributes of 'TestInterface'.
|
||||
*/
|
||||
DBusHandlerResult server_get_properties_handler(const char *property, DBusConnection *conn, DBusMessage *reply)
|
||||
{
|
||||
if (!strcmp(property, "Version"))
|
||||
{
|
||||
dbus_message_append_args(reply,
|
||||
DBUS_TYPE_STRING, &version,
|
||||
DBUS_TYPE_INVALID);
|
||||
}
|
||||
else
|
||||
/* Unknown property */
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
|
||||
if (!dbus_connection_send(conn, reply, NULL))
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* This implements 'GetAll' method of DBUS_INTERFACE_PROPERTIES. This
|
||||
* one seems required by g_dbus_proxy_get_cached_property().
|
||||
*/
|
||||
DBusHandlerResult server_get_all_properties_handler(DBusConnection *conn, DBusMessage *reply)
|
||||
{
|
||||
DBusHandlerResult result;
|
||||
DBusMessageIter array, dict, iter, variant;
|
||||
const char *property = "Version";
|
||||
|
||||
/*
|
||||
* All dbus functions used below might fail due to out of
|
||||
* memory error. If one of them fails, we assume that all
|
||||
* following functions will fail too, including
|
||||
* dbus_connection_send().
|
||||
*/
|
||||
result = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &array);
|
||||
|
||||
/* Append all properties name/value pairs */
|
||||
property = "Version";
|
||||
dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &dict);
|
||||
dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &property);
|
||||
dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "s", &variant);
|
||||
dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, &version);
|
||||
dbus_message_iter_close_container(&dict, &variant);
|
||||
dbus_message_iter_close_container(&array, &dict);
|
||||
|
||||
dbus_message_iter_close_container(&iter, &array);
|
||||
|
||||
if (dbus_connection_send(conn, reply, NULL))
|
||||
result = DBUS_HANDLER_RESULT_HANDLED;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function implements the 'TestInterface' interface for the
|
||||
* 'Server' DBus object.
|
||||
*
|
||||
* It also implements 'Introspect' method of
|
||||
* 'org.freedesktop.DBus.Introspectable' interface which returns the
|
||||
* XML string describing the interfaces, methods, and signals
|
||||
* implemented by 'Server' object. This also can be used by tools such
|
||||
* as d-feet(1) and can be queried by:
|
||||
*
|
||||
* $ gdbus introspect --session --dest org.example.TestServer --object-path /org/example/TestObject
|
||||
*/
|
||||
DBusHandlerResult server_message_handler(DBusConnection *conn, DBusMessage *message, void *data)
|
||||
{
|
||||
// DBusMessage *msg;
|
||||
// DBusMessageIter args;
|
||||
DBusError err;
|
||||
// dbus_uint32_t serial = 0;
|
||||
// char *time_now = "test";
|
||||
|
||||
DBusHandlerResult result;
|
||||
DBusMessage *reply = NULL;
|
||||
bool quit = false;
|
||||
|
||||
fprintf(stderr, "Got D-Bus request: %s.%s on %s\n",
|
||||
dbus_message_get_interface(message),
|
||||
dbus_message_get_member(message),
|
||||
dbus_message_get_path(message));
|
||||
|
||||
/*
|
||||
* Does not allocate any memory; the error only needs to be
|
||||
* freed if it is set at some point.
|
||||
*/
|
||||
dbus_error_init(&err);
|
||||
|
||||
if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
|
||||
{
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto fail;
|
||||
|
||||
dbus_message_append_args(reply,
|
||||
DBUS_TYPE_STRING, &server_introspection_xml,
|
||||
DBUS_TYPE_INVALID);
|
||||
}
|
||||
else if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Get"))
|
||||
{
|
||||
const char *interface, *property;
|
||||
|
||||
if (!dbus_message_get_args(message, &err,
|
||||
DBUS_TYPE_STRING, &interface,
|
||||
DBUS_TYPE_STRING, &property,
|
||||
DBUS_TYPE_INVALID))
|
||||
goto fail;
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto fail;
|
||||
|
||||
result = server_get_properties_handler(property, conn, reply);
|
||||
dbus_message_unref(reply);
|
||||
return result;
|
||||
}
|
||||
else if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "GetAll"))
|
||||
{
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto fail;
|
||||
|
||||
result = server_get_all_properties_handler(conn, reply);
|
||||
dbus_message_unref(reply);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
|
||||
fail:
|
||||
if (dbus_error_is_set(&err))
|
||||
{
|
||||
if (reply)
|
||||
dbus_message_unref(reply);
|
||||
reply = dbus_message_new_error(message, err.name, err.message);
|
||||
dbus_error_free(&err);
|
||||
}
|
||||
|
||||
/*
|
||||
* In any cases we should have allocated a reply otherwise it
|
||||
* means that we failed to allocate one.
|
||||
*/
|
||||
if (!reply)
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
|
||||
/* Send the reply which might be an error one too. */
|
||||
result = DBUS_HANDLER_RESULT_HANDLED;
|
||||
if (!dbus_connection_send(conn, reply, NULL))
|
||||
result = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
dbus_message_unref(reply);
|
||||
|
||||
if (quit)
|
||||
{
|
||||
fprintf(stderr, "Server exiting...\n");
|
||||
g_main_loop_quit(mainloop);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const DBusObjectPathVTable server_vtable = {
|
||||
.message_function = server_message_handler};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
DBusConnection *conn;
|
||||
DBusError err;
|
||||
int rv;
|
||||
|
||||
conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
|
||||
if (!conn)
|
||||
{
|
||||
fprintf(stderr, "Failed to get a system DBus connection: %s\n", err.message);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rv = dbus_bus_request_name(conn, "com.kylin.kysdk.TimeServer", DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
|
||||
if (rv != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
|
||||
{
|
||||
fprintf(stderr, "Failed to request name on bus: %s\n", err.message);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!dbus_connection_register_object_path(conn, "/com/kylin/kysdk/Timer", &server_vtable, NULL))
|
||||
{
|
||||
fprintf(stderr, "Failed to register a object path for 'TestObject'\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dbus_error_init(&err);
|
||||
|
||||
sem_init(&g_Wait, 0, 1);
|
||||
|
||||
pthread_attr_t attr;
|
||||
pthread_t tid;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&tid, &attr, actionTimeChanged, conn);
|
||||
|
||||
pthread_mutex_init(&lock, NULL);
|
||||
/* connect to the daemon bus */
|
||||
|
||||
|
||||
/*
|
||||
* For the sake of simplicity we're using glib event loop to
|
||||
* handle DBus messages. This is the only place where glib is
|
||||
* used.
|
||||
*/
|
||||
pthread_attr_t tt;
|
||||
pthread_t dd;
|
||||
pthread_attr_init(&tt);
|
||||
pthread_attr_setdetachstate(&tt, PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&dd, &tt, startBroadcastSystemTimePerMin, conn);
|
||||
printf("Starting dbus tiny server v%s\n", version);
|
||||
mainloop = g_main_loop_new(NULL, false);
|
||||
/* Set up the DBus connection to work in a GLib event loop */
|
||||
dbus_connection_setup_with_g_main(conn, NULL);
|
||||
/* Start the glib event loop */
|
||||
g_main_loop_run(mainloop);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
fail:
|
||||
dbus_error_free(&err);
|
||||
return EXIT_FAILURE;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef DBUS_M_SYSTIME_SERVER_H__
|
||||
#define DBUS_M_SYSTIME_SERVER_H__
|
||||
|
||||
// extern void *startBroadcastSystemTimePerMin();
|
||||
|
||||
/**
|
||||
* @brief 当系统时间被修改时,该函数会返回 1
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
extern int monitorSystemTimeChange();
|
||||
|
||||
#endif
|
|
@ -0,0 +1,479 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <dbus/dbus.h>
|
||||
#include <dbus/dbus-glib-lowlevel.h> /* for glib main loop */
|
||||
|
||||
#include "m_systime.h"
|
||||
#include "./timer/libkytimer.h"
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <semaphore.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <errno.h>
|
||||
|
||||
pthread_mutex_t lock;
|
||||
u_int8_t g_Flag;
|
||||
|
||||
u_int8_t g_Quit;
|
||||
sem_t g_Wait;
|
||||
|
||||
u_int8_t g_TimeChanged;
|
||||
u_int8_t g_TimeSync;
|
||||
|
||||
void sig_Handler(int sig)
|
||||
{
|
||||
g_Quit = 1;
|
||||
}
|
||||
|
||||
static void *printClock(void *ptr)
|
||||
{
|
||||
DBusConnection *conn = ptr;
|
||||
DBusMessage *msg;
|
||||
DBusMessageIter args;
|
||||
// DBusError err;
|
||||
dbus_uint32_t serial = 0;
|
||||
struct tm *now;
|
||||
time_t current;
|
||||
time(¤t);
|
||||
now = localtime(¤t);
|
||||
|
||||
// printf("%04d/%02d/%02d %02d:%02d:%02d\n", now->tm_year + 1900, now->tm_mon, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
|
||||
// printf("g_TimeChanged is %d\n", g_TimeChanged);
|
||||
// printf("%d",now->tm_sec);
|
||||
// 如果时间发生改变发送TimeVhangeSignal信号
|
||||
if (g_TimeChanged == 1)
|
||||
{
|
||||
char *buf = calloc(1, 128);
|
||||
sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d", now->tm_year + 1900, now->tm_mon, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
|
||||
printf("%s\n", buf);
|
||||
|
||||
msg = dbus_message_new_signal("/com/kylin/kysdk/Timer",
|
||||
"com.kylin.kysdk.TimeInterface",
|
||||
"TimeChangeSignal");
|
||||
if (NULL == msg)
|
||||
{
|
||||
fprintf(stderr, "Message Null\n");
|
||||
}
|
||||
// append arguments onto signal
|
||||
dbus_message_iter_init_append(msg, &args);
|
||||
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &buf);
|
||||
dbus_connection_send(conn, msg, &serial);
|
||||
dbus_connection_flush(conn);
|
||||
dbus_message_unref(msg);
|
||||
}
|
||||
// 非整点情况
|
||||
|
||||
if (now->tm_sec != 0)
|
||||
{
|
||||
pthread_mutex_lock(&lock);
|
||||
|
||||
// 如果出现了非整点的报时,且
|
||||
// 之前处于整分钟报时的情况,说明
|
||||
// 出现了唤醒偏差。重新执行一下对时
|
||||
if (g_Flag == 1)
|
||||
{
|
||||
g_Flag = 0;
|
||||
g_TimeSync = 1;
|
||||
}
|
||||
pthread_mutex_unlock(&lock);
|
||||
sem_post(&g_Wait);
|
||||
}
|
||||
// 整点的情况发送TimeSignal
|
||||
else
|
||||
{
|
||||
char *buf = calloc(1, 128);
|
||||
sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d", now->tm_year + 1900, now->tm_mon, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
|
||||
// printf("%s\n", buf);
|
||||
|
||||
msg = dbus_message_new_signal("/com/kylin/kysdk/Timer",
|
||||
"com.kylin.kysdk.TimeInterface",
|
||||
"TimeSignal");
|
||||
if (NULL == msg)
|
||||
{
|
||||
fprintf(stderr, "Message Null\n");
|
||||
}
|
||||
// append arguments onto signal
|
||||
dbus_message_iter_init_append(msg, &args);
|
||||
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &buf);
|
||||
dbus_connection_send(conn, msg, &serial);
|
||||
dbus_connection_flush(conn);
|
||||
dbus_message_unref(msg);
|
||||
if (g_Flag == 0)
|
||||
{
|
||||
pthread_mutex_lock(&lock);
|
||||
g_Flag = (1 & ~g_TimeChanged);
|
||||
pthread_mutex_unlock(&lock);
|
||||
sem_post(&g_Wait);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
void *startBroadcastSystemTimePerMin(void *tmp)
|
||||
{
|
||||
DBusConnection *conn = tmp;
|
||||
size_t timerID = -1;
|
||||
while (!g_Quit)
|
||||
{
|
||||
sem_wait(&g_Wait);
|
||||
|
||||
if (g_TimeChanged || g_TimeSync)
|
||||
{
|
||||
printf("Get Time Changed signal or mis-synced. stop timerID %zd\n", timerID);
|
||||
kyStopTimer(timerID);
|
||||
g_TimeChanged = 0;
|
||||
g_TimeSync = 0;
|
||||
timerID = -1;
|
||||
}
|
||||
|
||||
if (!g_Flag)
|
||||
kyStartTimer(200, printClock, KTIMER_SINGLESHOT, KTIMER_RELATIVE, conn, 0);
|
||||
else
|
||||
{
|
||||
timerID = kyStartTimer(1000 * 60, printClock, KTIMER_PERIODIC, KTIMER_RELATIVE, conn, 0);
|
||||
printf("start periodic timer with ID %zd\n", timerID);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int monitorSystemTimeChange()
|
||||
{
|
||||
#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
|
||||
|
||||
struct itimerspec its = {.it_value.tv_sec = TIME_T_MAX};
|
||||
int fd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC);
|
||||
if (fd < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (timerfd_settime(fd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0)
|
||||
{
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
u_int64_t dep;
|
||||
ssize_t ret = read(fd, &dep, sizeof(u_int64_t));
|
||||
if (ret == -1 && errno == ECANCELED)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *actionTimeChanged(void *ptr)
|
||||
{
|
||||
DBusConnection *conn = ptr;
|
||||
while (!g_Quit)
|
||||
{
|
||||
if (monitorSystemTimeChange() == 1)
|
||||
{
|
||||
printf("System Time Changed.\n");
|
||||
g_TimeChanged = 1;
|
||||
g_Flag = 0;
|
||||
printClock(conn);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *version = "0.1";
|
||||
GMainLoop *mainloop;
|
||||
|
||||
/*
|
||||
* This implements 'Get' method of DBUS_INTERFACE_PROPERTIES so a
|
||||
* client can inspect the properties/attributes of 'TestInterface'.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is the XML string describing the interfaces, methods and
|
||||
* signals implemented by our 'Server' object. It's used by the
|
||||
* 'Introspect' method of 'org.freedesktop.DBus.Introspectable'
|
||||
* interface.
|
||||
*
|
||||
* Currently our tiny server implements only 3 interfaces:
|
||||
*
|
||||
* - org.freedesktop.DBus.Introspectable
|
||||
* - org.freedesktop.DBus.Properties
|
||||
* - org.example.TestInterface
|
||||
*
|
||||
* 'org.example.TestInterface' offers 3 methods:
|
||||
*
|
||||
* - Ping(): makes the server answering the string 'Pong'.
|
||||
* It takes no arguments.
|
||||
*
|
||||
* - Echo(): replies the passed string argument.
|
||||
*
|
||||
* - EmitSignal(): send a signal 'OnEmitSignal'
|
||||
*
|
||||
* - Quit(): makes the server exit. It takes no arguments.
|
||||
*/
|
||||
const char *server_introspection_xml =
|
||||
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
|
||||
"<node>\n"
|
||||
|
||||
" <interface name='org.freedesktop.DBus.Introspectable'>\n"
|
||||
" <method name='Introspect'>\n"
|
||||
" <arg name='data' type='s' direction='out' />\n"
|
||||
" </method>\n"
|
||||
" </interface>\n"
|
||||
|
||||
" <interface name='org.freedesktop.DBus.Properties'>\n"
|
||||
" <method name='Get'>\n"
|
||||
" <arg name='interface' type='s' direction='in' />\n"
|
||||
" <arg name='property' type='s' direction='in' />\n"
|
||||
" <arg name='value' type='s' direction='out' />\n"
|
||||
" </method>\n"
|
||||
" <method name='GetAll'>\n"
|
||||
" <arg name='interface' type='s' direction='in'/>\n"
|
||||
" <arg name='properties' type='a{sv}' direction='out'/>\n"
|
||||
" </method>\n"
|
||||
" </interface>\n"
|
||||
|
||||
" <interface name='com.kylin.kysdk.TimeInterface'>\n"
|
||||
" <signal name='TimeSignal'>\n"
|
||||
" <arg name='tim' type='s' />\n"
|
||||
" </signal>"
|
||||
" <signal name='TimeChangeSignal'>\n"
|
||||
" <arg name='tim' type='s' />\n"
|
||||
" </signal>"
|
||||
" </interface>\n"
|
||||
|
||||
"</node>\n";
|
||||
|
||||
/*
|
||||
* This implements 'Get' method of DBUS_INTERFACE_PROPERTIES so a
|
||||
* client can inspect the properties/attributes of 'TestInterface'.
|
||||
*/
|
||||
DBusHandlerResult server_get_properties_handler(const char *property, DBusConnection *conn, DBusMessage *reply)
|
||||
{
|
||||
if (!strcmp(property, "Version"))
|
||||
{
|
||||
dbus_message_append_args(reply,
|
||||
DBUS_TYPE_STRING, &version,
|
||||
DBUS_TYPE_INVALID);
|
||||
}
|
||||
else
|
||||
/* Unknown property */
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
|
||||
if (!dbus_connection_send(conn, reply, NULL))
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* This implements 'GetAll' method of DBUS_INTERFACE_PROPERTIES. This
|
||||
* one seems required by g_dbus_proxy_get_cached_property().
|
||||
*/
|
||||
DBusHandlerResult server_get_all_properties_handler(DBusConnection *conn, DBusMessage *reply)
|
||||
{
|
||||
DBusHandlerResult result;
|
||||
DBusMessageIter array, dict, iter, variant;
|
||||
const char *property = "Version";
|
||||
|
||||
/*
|
||||
* All dbus functions used below might fail due to out of
|
||||
* memory error. If one of them fails, we assume that all
|
||||
* following functions will fail too, including
|
||||
* dbus_connection_send().
|
||||
*/
|
||||
result = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &array);
|
||||
|
||||
/* Append all properties name/value pairs */
|
||||
property = "Version";
|
||||
dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &dict);
|
||||
dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &property);
|
||||
dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "s", &variant);
|
||||
dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, &version);
|
||||
dbus_message_iter_close_container(&dict, &variant);
|
||||
dbus_message_iter_close_container(&array, &dict);
|
||||
|
||||
dbus_message_iter_close_container(&iter, &array);
|
||||
|
||||
if (dbus_connection_send(conn, reply, NULL))
|
||||
result = DBUS_HANDLER_RESULT_HANDLED;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function implements the 'TestInterface' interface for the
|
||||
* 'Server' DBus object.
|
||||
*
|
||||
* It also implements 'Introspect' method of
|
||||
* 'org.freedesktop.DBus.Introspectable' interface which returns the
|
||||
* XML string describing the interfaces, methods, and signals
|
||||
* implemented by 'Server' object. This also can be used by tools such
|
||||
* as d-feet(1) and can be queried by:
|
||||
*
|
||||
* $ gdbus introspect --session --dest org.example.TestServer --object-path /org/example/TestObject
|
||||
*/
|
||||
DBusHandlerResult server_message_handler(DBusConnection *conn, DBusMessage *message, void *data)
|
||||
{
|
||||
// DBusMessage *msg;
|
||||
// DBusMessageIter args;
|
||||
DBusError err;
|
||||
// dbus_uint32_t serial = 0;
|
||||
// char *time_now = "test";
|
||||
|
||||
DBusHandlerResult result;
|
||||
DBusMessage *reply = NULL;
|
||||
bool quit = false;
|
||||
|
||||
fprintf(stderr, "Got D-Bus request: %s.%s on %s\n",
|
||||
dbus_message_get_interface(message),
|
||||
dbus_message_get_member(message),
|
||||
dbus_message_get_path(message));
|
||||
|
||||
/*
|
||||
* Does not allocate any memory; the error only needs to be
|
||||
* freed if it is set at some point.
|
||||
*/
|
||||
dbus_error_init(&err);
|
||||
|
||||
if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
|
||||
{
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto fail;
|
||||
|
||||
dbus_message_append_args(reply,
|
||||
DBUS_TYPE_STRING, &server_introspection_xml,
|
||||
DBUS_TYPE_INVALID);
|
||||
}
|
||||
else if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Get"))
|
||||
{
|
||||
const char *interface, *property;
|
||||
|
||||
if (!dbus_message_get_args(message, &err,
|
||||
DBUS_TYPE_STRING, &interface,
|
||||
DBUS_TYPE_STRING, &property,
|
||||
DBUS_TYPE_INVALID))
|
||||
goto fail;
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto fail;
|
||||
|
||||
result = server_get_properties_handler(property, conn, reply);
|
||||
dbus_message_unref(reply);
|
||||
return result;
|
||||
}
|
||||
else if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "GetAll"))
|
||||
{
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto fail;
|
||||
|
||||
result = server_get_all_properties_handler(conn, reply);
|
||||
dbus_message_unref(reply);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
|
||||
fail:
|
||||
if (dbus_error_is_set(&err))
|
||||
{
|
||||
if (reply)
|
||||
dbus_message_unref(reply);
|
||||
reply = dbus_message_new_error(message, err.name, err.message);
|
||||
dbus_error_free(&err);
|
||||
}
|
||||
|
||||
/*
|
||||
* In any cases we should have allocated a reply otherwise it
|
||||
* means that we failed to allocate one.
|
||||
*/
|
||||
if (!reply)
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
|
||||
/* Send the reply which might be an error one too. */
|
||||
result = DBUS_HANDLER_RESULT_HANDLED;
|
||||
if (!dbus_connection_send(conn, reply, NULL))
|
||||
result = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
dbus_message_unref(reply);
|
||||
|
||||
if (quit)
|
||||
{
|
||||
fprintf(stderr, "Server exiting...\n");
|
||||
g_main_loop_quit(mainloop);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const DBusObjectPathVTable server_vtable = {
|
||||
.message_function = server_message_handler};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
DBusConnection *conn;
|
||||
DBusError err;
|
||||
int rv;
|
||||
|
||||
conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
|
||||
if (!conn)
|
||||
{
|
||||
fprintf(stderr, "Failed to get a system DBus connection: %s\n", err.message);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rv = dbus_bus_request_name(conn, "com.kylin.kysdk.TimeServer", DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
|
||||
if (rv != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
|
||||
{
|
||||
fprintf(stderr, "Failed to request name on bus: %s\n", err.message);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!dbus_connection_register_object_path(conn, "/com/kylin/kysdk/Timer", &server_vtable, NULL))
|
||||
{
|
||||
fprintf(stderr, "Failed to register a object path for 'TestObject'\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dbus_error_init(&err);
|
||||
|
||||
sem_init(&g_Wait, 0, 1);
|
||||
|
||||
pthread_attr_t attr;
|
||||
pthread_t tid;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&tid, &attr, actionTimeChanged, conn);
|
||||
|
||||
pthread_mutex_init(&lock, NULL);
|
||||
/* connect to the daemon bus */
|
||||
|
||||
|
||||
/*
|
||||
* For the sake of simplicity we're using glib event loop to
|
||||
* handle DBus messages. This is the only place where glib is
|
||||
* used.
|
||||
*/
|
||||
pthread_attr_t tt;
|
||||
pthread_t dd;
|
||||
pthread_attr_init(&tt);
|
||||
pthread_attr_setdetachstate(&tt, PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&dd, &tt, startBroadcastSystemTimePerMin, conn);
|
||||
printf("Starting dbus tiny server v%s\n", version);
|
||||
mainloop = g_main_loop_new(NULL, false);
|
||||
/* Set up the DBus connection to work in a GLib event loop */
|
||||
dbus_connection_setup_with_g_main(conn, NULL);
|
||||
/* Start the glib event loop */
|
||||
g_main_loop_run(mainloop);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
fail:
|
||||
dbus_error_free(&err);
|
||||
return EXIT_FAILURE;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef DBUS_M_SYSTIME_SERVER_H__
|
||||
#define DBUS_M_SYSTIME_SERVER_H__
|
||||
|
||||
// extern void *startBroadcastSystemTimePerMin();
|
||||
|
||||
/**
|
||||
* @brief 当系统时间被修改时,该函数会返回 1
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
extern int monitorSystemTimeChange();
|
||||
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
CLIBS=-lpthread -lrt
|
||||
CFLAGS=-g -O0 -Wall -fPIC -shared
|
||||
CC=gcc
|
||||
TARGET=libkytimer.so
|
||||
LIBINSTALLNAME=libkytimer.so.1.0.0
|
||||
LIBINSTALLDIR=/usr/lib/
|
||||
HEADERINSTALLDIR=/usr/include/
|
||||
HEADERS=kytimer.h
|
||||
|
||||
.PHONY:all
|
||||
.PHONY:clean
|
||||
.PHONY:lib
|
||||
|
||||
all:lib
|
||||
|
||||
lib:
|
||||
$(CC) $(CFLAGS) $(CLIBS) -o $(TARGET) libkytimer.c
|
||||
mkdir -p ../lib/
|
||||
mv $(TARGET) ../lib/$(LIBINSTALLNAME)
|
||||
|
||||
clean:
|
||||
-rm ../lib/$(LIBINSTALLNAME)
|
|
@ -0,0 +1,340 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/epoll.h>
|
||||
#include "libkytimer.h"
|
||||
|
||||
static void* timerCoreThread(void* data); //定时器循环核心
|
||||
static KTimerNode* findNodeByFD(unsigned int fd); //根据fd查找节点
|
||||
static int testNodeInList(KTimerNode* node);
|
||||
static void insertIntoList(KTimerNode* node); //插入节点,按照间隔时间的顺序从小到大排序
|
||||
static void deleteFromList(KTimerNode* node , int locked); //从链表中删除节点
|
||||
static pthread_t g_coreThreadID; //核心循环线程
|
||||
static KTimerNode* g_list = NULL;
|
||||
static int epollfd; //事件池句柄
|
||||
static int curTimerCounts; //当前已注册定时器数量
|
||||
|
||||
int kyTimerInitialize(); //初始化定时器核心
|
||||
size_t kyStartTimer(unsigned int intervalms, time_handler callback, KTimerAttribute attr, KTimerType type, void* userdata, int freeOnDelete); //注册并开始一个定时器,返回定时器句柄号
|
||||
void kyStopTimer(size_t timerfd); //停止一个定时器
|
||||
void kyResetTickCount(size_t timerfd , unsigned int intervalms);
|
||||
void kyTimerDestroy(); //销毁定时器核心
|
||||
|
||||
#ifndef TFD_TIMER_CANCEL_ON_SET
|
||||
#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
|
||||
#endif
|
||||
|
||||
int kyTimerInitialize()
|
||||
{
|
||||
if (g_coreThreadID > 0)
|
||||
return 0;
|
||||
|
||||
epollfd = epoll_create1(EPOLL_CLOEXEC);
|
||||
if (epollfd <= 0)
|
||||
{
|
||||
printf("Epoll事件池创建失败!%s\n" , strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
if (pthread_create(&g_coreThreadID , NULL , timerCoreThread , NULL))
|
||||
{
|
||||
printf("kyTimer 定时器核心初始化失败:%s\n" , strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t kyStartTimer(unsigned int intervalms, time_handler callback, KTimerAttribute attr, KTimerType type, void* userdata, int freeOnDelete)
|
||||
{
|
||||
if (g_coreThreadID <= 0 && kyTimerInitialize())
|
||||
{
|
||||
printf("定时器注册失败:定时器全局句柄初始化失败!\n");
|
||||
return 0;
|
||||
}
|
||||
if (curTimerCounts >= KTIMER_MAXTIMERFD)
|
||||
{
|
||||
printf("定时器注册失败:定时器注册数量已超限!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
KTimerNode* node = (KTimerNode*)calloc(1 , sizeof(KTimerNode));
|
||||
if (!node)
|
||||
{
|
||||
printf("定时器注册失败:无法分配内存,%s\n" , strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
pthread_mutex_init(&node->lock , NULL);
|
||||
node->callback = callback;
|
||||
node->userdata = userdata;
|
||||
node->intervalms = intervalms;
|
||||
node->attr = attr;
|
||||
node->type = type;
|
||||
node->freeOnDelete = freeOnDelete;
|
||||
|
||||
if (type == KTIMER_ABSOLUTE)
|
||||
node->fd = timerfd_create(CLOCK_MONOTONIC , TFD_CLOEXEC|TFD_NONBLOCK);
|
||||
else
|
||||
node->fd = timerfd_create(CLOCK_REALTIME , TFD_CLOEXEC|TFD_NONBLOCK);
|
||||
|
||||
if ((int)node->fd <= 0)
|
||||
{
|
||||
printf("定时器注册失败:timerfd_create失败,%s\n" , strerror(errno));
|
||||
free(node);
|
||||
return 0;
|
||||
}
|
||||
struct itimerspec tval;
|
||||
tval.it_value.tv_sec = intervalms / 1000;
|
||||
tval.it_value.tv_nsec = (intervalms % 1000) * 1000000;
|
||||
|
||||
if (attr == KTIMER_PERIODIC) //多次触发时设置interval项
|
||||
{
|
||||
tval.it_interval.tv_sec = tval.it_value.tv_sec;
|
||||
tval.it_interval.tv_nsec = tval.it_value.tv_nsec;
|
||||
}
|
||||
else
|
||||
{
|
||||
tval.it_interval.tv_sec = 0;
|
||||
tval.it_interval.tv_nsec = 0;
|
||||
}
|
||||
|
||||
timerfd_settime(node->fd, 0, &tval, NULL);
|
||||
|
||||
//注册到Epoll池中
|
||||
struct epoll_event ev;
|
||||
memset(&ev , 0 , sizeof(struct epoll_event));
|
||||
if (attr == KTIMER_SINGLESHOT)
|
||||
ev.events = EPOLLIN | EPOLLONESHOT;
|
||||
else
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.ptr = node;
|
||||
if (epoll_ctl(epollfd , EPOLL_CTL_ADD , node->fd , &ev))
|
||||
{
|
||||
printf("定时器注册失败:epoll_ctl错误,%s\n" , strerror(errno));
|
||||
free(node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
insertIntoList(node);
|
||||
|
||||
return node->fd;
|
||||
}
|
||||
|
||||
//停止一个定时器,并从列表中移除
|
||||
void kyStopTimer(size_t timerfd)
|
||||
{
|
||||
if (timerfd <= 0)
|
||||
return;
|
||||
KTimerNode* node = findNodeByFD(timerfd);
|
||||
if (node)
|
||||
{
|
||||
if (epoll_ctl(epollfd , EPOLL_CTL_DEL , timerfd , NULL))
|
||||
{
|
||||
printf("无法注销文件句柄%zd : %s\n" , timerfd , strerror(errno));
|
||||
return;
|
||||
}
|
||||
deleteFromList(node , 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//定时器核心线程,负责循环处理已到期的定时器
|
||||
static void* timerCoreThread(void* data)
|
||||
{
|
||||
pthread_detach(pthread_self());
|
||||
|
||||
KTimerNode* knode = NULL;
|
||||
int read_fds = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE , NULL);
|
||||
pthread_testcancel();
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE , NULL);
|
||||
|
||||
struct epoll_event activeEvs[KTIMER_MAXTIMERFD];
|
||||
read_fds = epoll_wait(epollfd , activeEvs , KTIMER_MAXTIMERFD , -1);
|
||||
if (read_fds < 0)
|
||||
{
|
||||
printf("epoll wait error , %s\n" , strerror(errno));
|
||||
continue;
|
||||
}
|
||||
for (int i = 0 ; i < read_fds ; i ++)
|
||||
{
|
||||
knode = activeEvs[i].data.ptr;
|
||||
pthread_mutex_lock(&knode->lock);
|
||||
if (!testNodeInList(knode))
|
||||
continue;
|
||||
uint64_t dep;
|
||||
read(knode->fd , &dep , sizeof(uint64_t));
|
||||
|
||||
if (knode && knode->attr != KTIMER_NEVER && knode->callback)
|
||||
{
|
||||
#if 1
|
||||
pthread_attr_t attr;
|
||||
pthread_t tid;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr , PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&tid , &attr , knode->callback , knode->userdata);
|
||||
|
||||
knode->freeOnDelete = 0;
|
||||
#else
|
||||
knode->callback(knode->userdata);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (knode->attr == KTIMER_SINGLESHOT) //单次触发的在触发后就从链表中移除
|
||||
{
|
||||
// FixMe: 如果设置了freeOnDelete,并且采用上面的线程执行模式,
|
||||
// 则会发生段错误。因此只能在启用线程执行模式的时候强制禁止
|
||||
// freeOnDelete。
|
||||
deleteFromList(knode , 1);
|
||||
}
|
||||
if (testNodeInList(knode))
|
||||
pthread_mutex_unlock(&knode->lock);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static KTimerNode* findNodeByFD(unsigned int fd)
|
||||
{
|
||||
KTimerNode* tmp = g_list;
|
||||
while (tmp)
|
||||
{
|
||||
if (tmp->fd == fd)
|
||||
return tmp;
|
||||
tmp = tmp->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int testNodeInList(KTimerNode* node)
|
||||
{
|
||||
KTimerNode* pos = g_list;
|
||||
while (pos)
|
||||
{
|
||||
if (pos == node)
|
||||
return 1;
|
||||
pos = pos->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void insertIntoList(KTimerNode* node)
|
||||
{
|
||||
curTimerCounts ++;
|
||||
if (!g_list)
|
||||
{
|
||||
g_list = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_list->intervalms > node->intervalms)
|
||||
{
|
||||
node->next = g_list;
|
||||
g_list = node;
|
||||
return;
|
||||
}
|
||||
|
||||
KTimerNode* cur = g_list->next;
|
||||
KTimerNode* prev = g_list;
|
||||
|
||||
while (cur)
|
||||
{
|
||||
if (cur->intervalms > node->intervalms)
|
||||
{
|
||||
node->next = cur;
|
||||
prev->next = node;
|
||||
return;
|
||||
}
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
prev->next = node;
|
||||
node->next = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void deleteFromList(KTimerNode* node , int locked)
|
||||
{
|
||||
if (!g_list)
|
||||
return;
|
||||
curTimerCounts --;
|
||||
if (!locked)
|
||||
pthread_mutex_lock(&node->lock);
|
||||
if (node == g_list)
|
||||
{
|
||||
g_list = g_list->next;
|
||||
if (node->userdata && node->freeOnDelete)
|
||||
free(node->userdata);
|
||||
free(node);
|
||||
return;
|
||||
}
|
||||
|
||||
KTimerNode* pos = g_list;
|
||||
while (pos)
|
||||
{
|
||||
if (pos->next && pos->next == node)
|
||||
{
|
||||
pos->next = pos->next->next;
|
||||
if (node->userdata && node->freeOnDelete)
|
||||
free(node->userdata);
|
||||
free(node);
|
||||
return;
|
||||
}
|
||||
pos = pos->next;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void kyResetTickCount(size_t timerfd , unsigned int intervalms)
|
||||
{
|
||||
KTimerNode* node = findNodeByFD(timerfd);
|
||||
if (!node)
|
||||
return;
|
||||
node->intervalms = intervalms;
|
||||
struct itimerspec tval;
|
||||
tval.it_value.tv_sec = intervalms / 1000;
|
||||
tval.it_value.tv_nsec = (intervalms % 1000) * 1000000;
|
||||
|
||||
if (node->attr == KTIMER_PERIODIC) //多次触发时设置interval项
|
||||
{
|
||||
tval.it_interval.tv_sec = tval.it_value.tv_sec;
|
||||
tval.it_interval.tv_nsec = tval.it_value.tv_nsec;
|
||||
}
|
||||
else
|
||||
{
|
||||
tval.it_interval.tv_sec = 0;
|
||||
tval.it_interval.tv_nsec = 0;
|
||||
}
|
||||
timerfd_settime(timerfd , 0 , &tval , NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
void kyTimerDestroy() //停止所有定时器,并销毁定时器核心
|
||||
{
|
||||
//终止线程运行
|
||||
if (g_coreThreadID > 0)
|
||||
pthread_cancel(g_coreThreadID);
|
||||
|
||||
//销毁链表
|
||||
KTimerNode* node = g_list;
|
||||
while (node)
|
||||
{
|
||||
deleteFromList(node , 0);
|
||||
node = g_list;
|
||||
}
|
||||
|
||||
//关闭epoll池
|
||||
close(epollfd);
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef __KY_TIMER_H__
|
||||
#define __KY_TIMER_H__
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
KTIMER_SINGLESHOT = 0,
|
||||
KTIMER_PERIODIC = 1,
|
||||
KTIMER_NEVER = 2
|
||||
}KTimerAttribute;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
KTIMER_ABSOLUTE = 0,
|
||||
KTIMER_RELATIVE
|
||||
}KTimerType;
|
||||
|
||||
#define KTIMER_MAXTIMERFD 1000 //最多支持注册1000个定时器
|
||||
|
||||
typedef void* (*time_handler)(void* user_data); //回调函数规则
|
||||
|
||||
typedef struct _KTimerNode{
|
||||
pthread_mutex_t lock;
|
||||
size_t fd; //序列号
|
||||
time_handler callback; //到期后执行的函数
|
||||
int freeOnDelete; //删除定时器时自动释放userdata
|
||||
void* userdata; //callback使用的参数,外部使用alloc分配内存,在delete定时器的时候会根据配置自动释放
|
||||
unsigned int intervalms; //定时器间隔时间,单位毫秒
|
||||
KTimerAttribute attr; //触发类型,单次触发或多次触发
|
||||
KTimerType type; //时钟类型,绝对时间还是相对时间
|
||||
struct _KTimerNode* next;
|
||||
}KTimerNode;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
extern int kyTimerInitialize(); //初始化定时器核心
|
||||
extern size_t kyStartTimer(unsigned int intervalms, time_handler callback, KTimerAttribute attr, KTimerType type, void* userdata, int freeOnDelete); //注册并开始一个定时器,返回定时器句柄号
|
||||
extern void kyStopTimer(size_t timerfd); //停止一个定时器
|
||||
extern void kyResetTickCount(size_t timerfd , unsigned int intervalms); //重置一个定时器的时间
|
||||
extern void kyTimerDestroy(); //销毁定时器核心
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,5 @@
|
|||
all:
|
||||
gcc -g -O0 -o demo main.c -lkytimer -lpthread
|
||||
|
||||
clean:
|
||||
rm demo
|
|
@ -0,0 +1,62 @@
|
|||
#include <libkytimer.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
void func(char* comment)
|
||||
{
|
||||
char date[32] = {0};
|
||||
time_t now;
|
||||
time(&now);
|
||||
if (ctime_r(&now , date))
|
||||
date[strlen(date) - 1] = '\0';
|
||||
printf("[%s]这是%s\n" , date , comment);
|
||||
}
|
||||
|
||||
void stop(size_t* fdpoll)
|
||||
{
|
||||
printf("开始停止定时器\n");
|
||||
for (int i = 0 ; i < 10 ; i ++)
|
||||
{
|
||||
kyStopTimer(fdpoll[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
assert(kyTimerInitialize() == 0);
|
||||
|
||||
//测试1 -- 基本功能
|
||||
size_t fdpoll[10] = {0};
|
||||
for (int i = 1 ; i <= 10 ; i ++)
|
||||
{
|
||||
char* data = (char*)malloc(10);
|
||||
assert(data);
|
||||
sprintf(data , "%d号" , i);
|
||||
fdpoll[i - 1] = kyStartTimer(i * 1000 , (time_handler)func , KTIMER_PERIODIC , (void*)data , 1);
|
||||
assert(fdpoll[i - 1]);
|
||||
}
|
||||
kyStartTimer(10000 , (time_handler)stop , KTIMER_SINGLESHOT , (void*)fdpoll , 0);
|
||||
sleep(11);
|
||||
|
||||
//测试3 -- 单次触发
|
||||
printf("单次触发测试:\n");
|
||||
kyStartTimer(2000 , (time_handler)func , KTIMER_SINGLESHOT , "2号" , 0);
|
||||
sleep(5);
|
||||
|
||||
//测试2 -- 重置定时器时间
|
||||
printf("修改时间测试:\n");
|
||||
int persec = kyStartTimer(1000 , (time_handler)func , KTIMER_PERIODIC , "1号" , 0);
|
||||
int sec3 = kyStartTimer(3000 , (time_handler)func , KTIMER_SINGLESHOT , "3号" , 0);
|
||||
sleep(2);
|
||||
kyResetTickCount(sec3 , 4000);
|
||||
printf("sec3 时钟已被重置为4000ms\n");
|
||||
sleep(10);
|
||||
|
||||
printf("正在销毁定时器核心...\n");
|
||||
kyTimerDestroy();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
FILE=$(file)
|
||||
CFLAGS=$(cflags)
|
||||
CFLAGS += -g -O0 -std=gnu11 -Wall
|
||||
CLIBS=-lpthread -pthread -lrt
|
||||
CLIBS += $(clibs)
|
||||
|
||||
.PHONY:main test
|
||||
all:systemtime.c
|
||||
gcc -o demo systemtime.c ../timer/libkytimer.c $(CFLAGS) $(CLIBS)
|
||||
|
||||
.PHONY:clean
|
||||
clean:
|
||||
-rm main
|
||||
-rm *.o
|
Binary file not shown.
|
@ -0,0 +1,141 @@
|
|||
#include "systemtime.h"
|
||||
#include "../timer/libkytimer.h"
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <semaphore.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <errno.h>
|
||||
|
||||
pthread_mutex_t lock; //互斥锁
|
||||
u_int8_t g_Flag; //任何平台都是8位无符号数
|
||||
|
||||
u_int8_t g_Quit;
|
||||
sem_t g_Wait;
|
||||
|
||||
u_int8_t g_TimeChanged;
|
||||
|
||||
void sig_Handler(int sig)
|
||||
{
|
||||
g_Quit = 1;
|
||||
printf("%s----%d",__func__,sig);
|
||||
}
|
||||
|
||||
static void* printClock(void* ptr)
|
||||
{
|
||||
struct tm *now;
|
||||
time_t current;
|
||||
time(¤t);
|
||||
now = localtime(¤t);
|
||||
printf("%s,%d",__func__,__LINE__);
|
||||
printf("%d/%d/%d %d:%d:%d\n", now->tm_year, now->tm_mon, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
|
||||
|
||||
if (!g_Flag)
|
||||
{
|
||||
pthread_mutex_lock(&lock);
|
||||
if(now->tm_sec == 0)
|
||||
{
|
||||
g_Flag = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_Flag = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&lock);
|
||||
sem_post(&g_Wait);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void startBroadcastSystemTimePerMin()
|
||||
{
|
||||
size_t timerID = -1;
|
||||
while (!g_Quit)
|
||||
{
|
||||
sem_wait(&g_Wait);
|
||||
|
||||
if (g_TimeChanged)
|
||||
{
|
||||
printf("Get Time Changed signal. stop timerID %zd\n", timerID);
|
||||
kyStopTimer(timerID);
|
||||
g_TimeChanged = 0;
|
||||
timerID = -1;
|
||||
}
|
||||
|
||||
if (! g_Flag)
|
||||
kyStartTimer(500, printClock, KTIMER_SINGLESHOT, KTIMER_RELATIVE, NULL, 0);
|
||||
else
|
||||
{
|
||||
timerID = kyStartTimer(1000 * 60, printClock, KTIMER_PERIODIC, KTIMER_RELATIVE, NULL, 0);
|
||||
printf("start periodic timer with ID %zd\n", timerID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int monitorSystemTimeChange()
|
||||
{
|
||||
#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
|
||||
|
||||
struct itimerspec its = {.it_value.tv_sec = TIME_T_MAX};
|
||||
int fd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC);
|
||||
if (fd < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0)
|
||||
{
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
u_int64_t dep;
|
||||
ssize_t ret = read(fd, &dep, sizeof(u_int64_t));
|
||||
if (ret == -1 && errno == ECANCELED)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* actionTimeChanged(void* ptr)
|
||||
{
|
||||
while (!g_Quit)
|
||||
{
|
||||
if (monitorSystemTimeChange() == 1)
|
||||
{
|
||||
printf("System Time Changed.\n");
|
||||
g_TimeChanged = 1;
|
||||
g_Flag = 0;
|
||||
printClock(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
signal(SIGTERM, sig_Handler);
|
||||
signal(SIGQUIT, sig_Handler);
|
||||
signal(SIGKILL, sig_Handler);
|
||||
signal(SIGINT, sig_Handler);
|
||||
|
||||
sem_init(&g_Wait, 0, 1);
|
||||
|
||||
pthread_attr_t attr;
|
||||
pthread_t tid;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&tid, &attr, actionTimeChanged, NULL);
|
||||
|
||||
pthread_mutex_init(&lock, NULL);
|
||||
startBroadcastSystemTimePerMin();
|
||||
pthread_mutex_destroy(&lock);
|
||||
|
||||
sem_destroy(&g_Wait);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef KYSDK_SYSTIME_H__
|
||||
#define KYSDK_SYSTIME_H__
|
||||
|
||||
extern void startBroadcastSystemTimePerMin();
|
||||
|
||||
/**
|
||||
* @brief 当系统时间被修改时,该函数会返回 1
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
extern int monitorSystemTimeChange();
|
||||
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
CLIBS=-lpthread -lrt
|
||||
CFLAGS=-g -O0 -Wall -fPIC -shared
|
||||
CC=gcc
|
||||
TARGET=libkytimer.so
|
||||
LIBINSTALLNAME=libkytimer.so.1.0.0
|
||||
LIBINSTALLDIR=/usr/lib/
|
||||
HEADERINSTALLDIR=/usr/include/
|
||||
HEADERS=kytimer.h
|
||||
|
||||
.PHONY:all
|
||||
.PHONY:clean
|
||||
.PHONY:lib
|
||||
|
||||
all:lib
|
||||
|
||||
lib:
|
||||
$(CC) $(CFLAGS) $(CLIBS) -o $(TARGET) libkytimer.c
|
||||
mkdir -p ../lib/
|
||||
mv $(TARGET) ../lib/$(LIBINSTALLNAME)
|
||||
|
||||
clean:
|
||||
-rm ../lib/$(LIBINSTALLNAME)
|
|
@ -0,0 +1,340 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/epoll.h>
|
||||
#include "libkytimer.h"
|
||||
|
||||
static void* timerCoreThread(void* data); //定时器循环核心
|
||||
static KTimerNode* findNodeByFD(unsigned int fd); //根据fd查找节点
|
||||
static int testNodeInList(KTimerNode* node);
|
||||
static void insertIntoList(KTimerNode* node); //插入节点,按照间隔时间的顺序从小到大排序
|
||||
static void deleteFromList(KTimerNode* node , int locked); //从链表中删除节点
|
||||
static pthread_t g_coreThreadID; //核心循环线程
|
||||
static KTimerNode* g_list = NULL;
|
||||
static int epollfd; //事件池句柄
|
||||
static int curTimerCounts; //当前已注册定时器数量
|
||||
|
||||
int kyTimerInitialize(); //初始化定时器核心
|
||||
size_t kyStartTimer(unsigned int intervalms, time_handler callback, KTimerAttribute attr, KTimerType type, void* userdata, int freeOnDelete); //注册并开始一个定时器,返回定时器句柄号
|
||||
void kyStopTimer(size_t timerfd); //停止一个定时器
|
||||
void kyResetTickCount(size_t timerfd , unsigned int intervalms);
|
||||
void kyTimerDestroy(); //销毁定时器核心
|
||||
|
||||
#ifndef TFD_TIMER_CANCEL_ON_SET
|
||||
#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
|
||||
#endif
|
||||
|
||||
int kyTimerInitialize()
|
||||
{
|
||||
if (g_coreThreadID > 0)
|
||||
return 0;
|
||||
|
||||
epollfd = epoll_create1(EPOLL_CLOEXEC);
|
||||
if (epollfd <= 0)
|
||||
{
|
||||
printf("Epoll事件池创建失败!%s\n" , strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
if (pthread_create(&g_coreThreadID , NULL , timerCoreThread , NULL))
|
||||
{
|
||||
printf("kyTimer 定时器核心初始化失败:%s\n" , strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t kyStartTimer(unsigned int intervalms, time_handler callback, KTimerAttribute attr, KTimerType type, void* userdata, int freeOnDelete)
|
||||
{
|
||||
if (g_coreThreadID <= 0 && kyTimerInitialize())
|
||||
{
|
||||
printf("定时器注册失败:定时器全局句柄初始化失败!\n");
|
||||
return 0;
|
||||
}
|
||||
if (curTimerCounts >= KTIMER_MAXTIMERFD)
|
||||
{
|
||||
printf("定时器注册失败:定时器注册数量已超限!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
KTimerNode* node = (KTimerNode*)calloc(1 , sizeof(KTimerNode));
|
||||
if (!node)
|
||||
{
|
||||
printf("定时器注册失败:无法分配内存,%s\n" , strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
pthread_mutex_init(&node->lock , NULL);
|
||||
node->callback = callback;
|
||||
node->userdata = userdata;
|
||||
node->intervalms = intervalms;
|
||||
node->attr = attr;
|
||||
node->type = type;
|
||||
node->freeOnDelete = freeOnDelete;
|
||||
|
||||
if (type == KTIMER_ABSOLUTE)
|
||||
node->fd = timerfd_create(CLOCK_MONOTONIC , TFD_CLOEXEC|TFD_NONBLOCK);
|
||||
else
|
||||
node->fd = timerfd_create(CLOCK_REALTIME , TFD_CLOEXEC|TFD_NONBLOCK);
|
||||
|
||||
if ((int)node->fd <= 0)
|
||||
{
|
||||
printf("定时器注册失败:timerfd_create失败,%s\n" , strerror(errno));
|
||||
free(node);
|
||||
return 0;
|
||||
}
|
||||
struct itimerspec tval;
|
||||
tval.it_value.tv_sec = intervalms / 1000;
|
||||
tval.it_value.tv_nsec = (intervalms % 1000) * 1000000;
|
||||
|
||||
if (attr == KTIMER_PERIODIC) //多次触发时设置interval项
|
||||
{
|
||||
tval.it_interval.tv_sec = tval.it_value.tv_sec;
|
||||
tval.it_interval.tv_nsec = tval.it_value.tv_nsec;
|
||||
}
|
||||
else
|
||||
{
|
||||
tval.it_interval.tv_sec = 0;
|
||||
tval.it_interval.tv_nsec = 0;
|
||||
}
|
||||
|
||||
timerfd_settime(node->fd, 0, &tval, NULL);
|
||||
|
||||
//注册到Epoll池中
|
||||
struct epoll_event ev;
|
||||
memset(&ev , 0 , sizeof(struct epoll_event));
|
||||
if (attr == KTIMER_SINGLESHOT)
|
||||
ev.events = EPOLLIN | EPOLLONESHOT;
|
||||
else
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.ptr = node;
|
||||
if (epoll_ctl(epollfd , EPOLL_CTL_ADD , node->fd , &ev))
|
||||
{
|
||||
printf("定时器注册失败:epoll_ctl错误,%s\n" , strerror(errno));
|
||||
free(node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
insertIntoList(node);
|
||||
|
||||
return node->fd;
|
||||
}
|
||||
|
||||
//停止一个定时器,并从列表中移除
|
||||
void kyStopTimer(size_t timerfd)
|
||||
{
|
||||
if (timerfd <= 0)
|
||||
return;
|
||||
KTimerNode* node = findNodeByFD(timerfd);
|
||||
if (node)
|
||||
{
|
||||
if (epoll_ctl(epollfd , EPOLL_CTL_DEL , timerfd , NULL))
|
||||
{
|
||||
printf("无法注销文件句柄%zd : %s\n" , timerfd , strerror(errno));
|
||||
return;
|
||||
}
|
||||
deleteFromList(node , 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//定时器核心线程,负责循环处理已到期的定时器
|
||||
static void* timerCoreThread(void* data)
|
||||
{
|
||||
pthread_detach(pthread_self());
|
||||
|
||||
KTimerNode* knode = NULL;
|
||||
int read_fds = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE , NULL);
|
||||
pthread_testcancel();
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE , NULL);
|
||||
|
||||
struct epoll_event activeEvs[KTIMER_MAXTIMERFD];
|
||||
read_fds = epoll_wait(epollfd , activeEvs , KTIMER_MAXTIMERFD , -1);
|
||||
if (read_fds < 0)
|
||||
{
|
||||
printf("epoll wait error , %s\n" , strerror(errno));
|
||||
continue;
|
||||
}
|
||||
for (int i = 0 ; i < read_fds ; i ++)
|
||||
{
|
||||
knode = activeEvs[i].data.ptr;
|
||||
pthread_mutex_lock(&knode->lock);
|
||||
if (!testNodeInList(knode))
|
||||
continue;
|
||||
uint64_t dep;
|
||||
read(knode->fd , &dep , sizeof(uint64_t));
|
||||
|
||||
if (knode && knode->attr != KTIMER_NEVER && knode->callback)
|
||||
{
|
||||
#if 1
|
||||
pthread_attr_t attr;
|
||||
pthread_t tid;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr , PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&tid , &attr , knode->callback , knode->userdata);
|
||||
|
||||
knode->freeOnDelete = 0;
|
||||
#else
|
||||
knode->callback(knode->userdata);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (knode->attr == KTIMER_SINGLESHOT) //单次触发的在触发后就从链表中移除
|
||||
{
|
||||
// FixMe: 如果设置了freeOnDelete,并且采用上面的线程执行模式,
|
||||
// 则会发生段错误。因此只能在启用线程执行模式的时候强制禁止
|
||||
// freeOnDelete。
|
||||
deleteFromList(knode , 1);
|
||||
}
|
||||
if (testNodeInList(knode))
|
||||
pthread_mutex_unlock(&knode->lock);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static KTimerNode* findNodeByFD(unsigned int fd)
|
||||
{
|
||||
KTimerNode* tmp = g_list;
|
||||
while (tmp)
|
||||
{
|
||||
if (tmp->fd == fd)
|
||||
return tmp;
|
||||
tmp = tmp->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int testNodeInList(KTimerNode* node)
|
||||
{
|
||||
KTimerNode* pos = g_list;
|
||||
while (pos)
|
||||
{
|
||||
if (pos == node)
|
||||
return 1;
|
||||
pos = pos->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void insertIntoList(KTimerNode* node)
|
||||
{
|
||||
curTimerCounts ++;
|
||||
if (!g_list)
|
||||
{
|
||||
g_list = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_list->intervalms > node->intervalms)
|
||||
{
|
||||
node->next = g_list;
|
||||
g_list = node;
|
||||
return;
|
||||
}
|
||||
|
||||
KTimerNode* cur = g_list->next;
|
||||
KTimerNode* prev = g_list;
|
||||
|
||||
while (cur)
|
||||
{
|
||||
if (cur->intervalms > node->intervalms)
|
||||
{
|
||||
node->next = cur;
|
||||
prev->next = node;
|
||||
return;
|
||||
}
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
prev->next = node;
|
||||
node->next = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void deleteFromList(KTimerNode* node , int locked)
|
||||
{
|
||||
if (!g_list)
|
||||
return;
|
||||
curTimerCounts --;
|
||||
if (!locked)
|
||||
pthread_mutex_lock(&node->lock);
|
||||
if (node == g_list)
|
||||
{
|
||||
g_list = g_list->next;
|
||||
if (node->userdata && node->freeOnDelete)
|
||||
free(node->userdata);
|
||||
free(node);
|
||||
return;
|
||||
}
|
||||
|
||||
KTimerNode* pos = g_list;
|
||||
while (pos)
|
||||
{
|
||||
if (pos->next && pos->next == node)
|
||||
{
|
||||
pos->next = pos->next->next;
|
||||
if (node->userdata && node->freeOnDelete)
|
||||
free(node->userdata);
|
||||
free(node);
|
||||
return;
|
||||
}
|
||||
pos = pos->next;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void kyResetTickCount(size_t timerfd , unsigned int intervalms)
|
||||
{
|
||||
KTimerNode* node = findNodeByFD(timerfd);
|
||||
if (!node)
|
||||
return;
|
||||
node->intervalms = intervalms;
|
||||
struct itimerspec tval;
|
||||
tval.it_value.tv_sec = intervalms / 1000;
|
||||
tval.it_value.tv_nsec = (intervalms % 1000) * 1000000;
|
||||
|
||||
if (node->attr == KTIMER_PERIODIC) //多次触发时设置interval项
|
||||
{
|
||||
tval.it_interval.tv_sec = tval.it_value.tv_sec;
|
||||
tval.it_interval.tv_nsec = tval.it_value.tv_nsec;
|
||||
}
|
||||
else
|
||||
{
|
||||
tval.it_interval.tv_sec = 0;
|
||||
tval.it_interval.tv_nsec = 0;
|
||||
}
|
||||
timerfd_settime(timerfd , 0 , &tval , NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
void kyTimerDestroy() //停止所有定时器,并销毁定时器核心
|
||||
{
|
||||
//终止线程运行
|
||||
if (g_coreThreadID > 0)
|
||||
pthread_cancel(g_coreThreadID);
|
||||
|
||||
//销毁链表
|
||||
KTimerNode* node = g_list;
|
||||
while (node)
|
||||
{
|
||||
deleteFromList(node , 0);
|
||||
node = g_list;
|
||||
}
|
||||
|
||||
//关闭epoll池
|
||||
close(epollfd);
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef __KY_TIMER_H__
|
||||
#define __KY_TIMER_H__
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
KTIMER_SINGLESHOT = 0,
|
||||
KTIMER_PERIODIC = 1,
|
||||
KTIMER_NEVER = 2
|
||||
}KTimerAttribute;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
KTIMER_ABSOLUTE = 0,
|
||||
KTIMER_RELATIVE
|
||||
}KTimerType;
|
||||
|
||||
#define KTIMER_MAXTIMERFD 1000 //最多支持注册1000个定时器
|
||||
|
||||
typedef void* (*time_handler)(void* user_data); //回调函数规则
|
||||
|
||||
typedef struct _KTimerNode{
|
||||
pthread_mutex_t lock;
|
||||
size_t fd; //序列号
|
||||
time_handler callback; //到期后执行的函数
|
||||
int freeOnDelete; //删除定时器时自动释放userdata
|
||||
void* userdata; //callback使用的参数,外部使用alloc分配内存,在delete定时器的时候会根据配置自动释放
|
||||
unsigned int intervalms; //定时器间隔时间,单位毫秒
|
||||
KTimerAttribute attr; //触发类型,单次触发或多次触发
|
||||
KTimerType type; //时钟类型,绝对时间还是相对时间
|
||||
struct _KTimerNode* next;
|
||||
}KTimerNode;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
extern int kyTimerInitialize(); //初始化定时器核心
|
||||
extern size_t kyStartTimer(unsigned int intervalms, time_handler callback, KTimerAttribute attr, KTimerType type, void* userdata, int freeOnDelete); //注册并开始一个定时器,返回定时器句柄号
|
||||
extern void kyStopTimer(size_t timerfd); //停止一个定时器
|
||||
extern void kyResetTickCount(size_t timerfd , unsigned int intervalms); //重置一个定时器的时间
|
||||
extern void kyTimerDestroy(); //销毁定时器核心
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,5 @@
|
|||
all:
|
||||
gcc -g -O0 -o demo main.c -lkytimer -lpthread
|
||||
|
||||
clean:
|
||||
rm demo
|
|
@ -0,0 +1,62 @@
|
|||
#include <libkytimer.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
void func(char* comment)
|
||||
{
|
||||
char date[32] = {0};
|
||||
time_t now;
|
||||
time(&now);
|
||||
if (ctime_r(&now , date))
|
||||
date[strlen(date) - 1] = '\0';
|
||||
printf("[%s]这是%s\n" , date , comment);
|
||||
}
|
||||
|
||||
void stop(size_t* fdpoll)
|
||||
{
|
||||
printf("开始停止定时器\n");
|
||||
for (int i = 0 ; i < 10 ; i ++)
|
||||
{
|
||||
kyStopTimer(fdpoll[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
assert(kyTimerInitialize() == 0);
|
||||
|
||||
//测试1 -- 基本功能
|
||||
size_t fdpoll[10] = {0};
|
||||
for (int i = 1 ; i <= 10 ; i ++)
|
||||
{
|
||||
char* data = (char*)malloc(10);
|
||||
assert(data);
|
||||
sprintf(data , "%d号" , i);
|
||||
fdpoll[i - 1] = kyStartTimer(i * 1000 , (time_handler)func , KTIMER_PERIODIC , (void*)data , 1);
|
||||
assert(fdpoll[i - 1]);
|
||||
}
|
||||
kyStartTimer(10000 , (time_handler)stop , KTIMER_SINGLESHOT , (void*)fdpoll , 0);
|
||||
sleep(11);
|
||||
|
||||
//测试3 -- 单次触发
|
||||
printf("单次触发测试:\n");
|
||||
kyStartTimer(2000 , (time_handler)func , KTIMER_SINGLESHOT , "2号" , 0);
|
||||
sleep(5);
|
||||
|
||||
//测试2 -- 重置定时器时间
|
||||
printf("修改时间测试:\n");
|
||||
int persec = kyStartTimer(1000 , (time_handler)func , KTIMER_PERIODIC , "1号" , 0);
|
||||
int sec3 = kyStartTimer(3000 , (time_handler)func , KTIMER_SINGLESHOT , "3号" , 0);
|
||||
sleep(2);
|
||||
kyResetTickCount(sec3 , 4000);
|
||||
printf("sec3 时钟已被重置为4000ms\n");
|
||||
sleep(10);
|
||||
|
||||
printf("正在销毁定时器核心...\n");
|
||||
kyTimerDestroy();
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue