/* * timer.cpp * * Created on: Nov 9, 2015 * Author: Philipp Hinz * Source: http://man7.org/linux/man-pages/man2/timer_create.2.html * http://www.informit.com/articles/article.aspx?p=23618&seqNum=14 */ #include #include #include #include #include //#include #include #include #include //#include #include "global.h" #include "timer.h" #include "logger.h" int counter = 0; int timercnt = 10; timer *firstTimer = NULL; timer *lastTimer = NULL; /** * Timer constructor * Depending on the call of the constructor the handler will be called as thread or not * @param *handler target pointer to the method */ timer::timer(void (*handler)(void)) { this->active = false; this->divider = 0; this->handler = handler; this->thandler = NULL; this->next = firstTimer; this->asThread = false; this->id = timercnt++; this->thread = pthread_t(); firstTimer = this; } timer::timer(void *(*handler)(void *)) { this->active = false; this->divider = 0; this->thandler = handler; this->handler = NULL; this->next = firstTimer; this->thread = pthread_t(); this->id = timercnt++; // Now calling a dummy thread, to allow later the check of the thread state int rc = pthread_create(&this->thread, NULL, nullThread, (void *) this->id); if (rc) { logger_error("Error: unable to create thread in timer, %d\n", rc); exit(-1); } this->asThread = true; firstTimer = this; } /** * Calls the timer handler if enabled */ // void *MainLoop(void *threadid) void timer::call() { int rc, ret = 0; if (this->asThread) { if (this->thread) ret = pthread_kill(this->thread, 0); if (ret == ESRCH) { // Thread clear pthread_detach(this->thread); // Important! Freeing the resources of the previous thread rc = pthread_create(&this->thread, NULL, this->thandler, (void *) this->id); if (rc) { logger_error( "Error: unable to create thread in timer, %s (%d)\n", strerror(rc), rc); exit(-1); } } } else this->handler(); } /** * enables the timer */ void timer::start() { this->active = true; } /** * disables the timer */ void timer::stop() { this->active = false; } /** * sets the divider for this timer (based on system timer) * @param divider integer timer divider */ void timer::setDivider(unsigned int divider) { this->divider = divider; } /** * reads the timer divider * @return timer divider */ int timer::getDivider() { return this->divider; } /** * Returns the timers active state * @return true if active */ bool timer::isActive() { return this->active; } /** * this method is called by the system timer and handles the other timers */ void timer_handler(int sig, siginfo_t *si, void *uc) { timer *t = firstTimer; if (++counter >= TIMER_DELAY_US) counter = 0; while (t != NULL) { if (t->isActive() && t->getDivider() > 0 && (counter % t->getDivider()) == 0) t->call(); t = t->next; } } /** * inits the system timer based on signals */ void initTimers(void) { /*struct sigaction sa; struct itimerval timer; * Install timer_handler as the signal handler for SIGVTALRM. memset(&sa, 0, sizeof(sa)); sa.sa_handler = &timer_handler; sigaction(SIGVTALRM, &sa, NULL);*/ timer_t timerid; struct sigevent sev; struct itimerspec its; //sigset_t mask; struct sigaction sa; /* Establish handler for timer signal */ logger(V_BASIC, "timer.cpp: Establishing handler for signal %d\n", SIG); sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = &timer_handler; sigemptyset(&sa.sa_mask); if (sigaction(SIG, &sa, NULL) == -1) errExit("sigaction"); /* Block timer signal temporarily printf("Blocking signal %d\n", SIG); sigemptyset(&mask); sigaddset(&mask, SIG); if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) errExit("sigprocmask"); */ /* Create the timer */ sev.sigev_notify = SIGEV_SIGNAL; sev.sigev_signo = SIG; sev.sigev_value.sival_ptr = &timerid; if (timer_create(CLOCKID, &sev, &timerid) == -1) errExit("timer_create"); logger(V_BASIC, "timer ID is 0x%lx\n", (long) timerid); /* Configure the timer to expire after x msec... */ its.it_value.tv_sec = TIMER_DELAY_US / 1000000; its.it_value.tv_nsec = TIMER_DELAY_US * 1000; /* ... and every x msec after that. */ its.it_interval.tv_sec = its.it_value.tv_sec; its.it_interval.tv_nsec = its.it_value.tv_nsec; /* Start a virtual timer. It counts down whenever this process is executing. */ //setitimer(ITIMER_VIRTUAL, &timer, NULL); if (timer_settime(timerid, 0, &its, NULL) == -1) errExit("timer_settime"); counter = 0; } /** * Calculates the divider corresponding to the given period. * If the given period does not match exactly to a divider, the * next higher is returned! */ uint32_t ms2Divider(uint64_t millisec) { uint32_t divider = (millisec * 1000) / (uint64_t) TIMER_DELAY_US; if (((uint64_t) divider * TIMER_DELAY_US) < (millisec * 1000)) return ++divider; else return divider; } /** * Stops all existing timers */ void stopTimers(void) { timer *t = firstTimer; while (t != NULL) { t->stop(); t = t->next; } logger(V_BASIC, "Stopped all timers.\n"); } /** * This is a dummy thread * @param *threadid Thread ID */ void *nullThread(void *threadid) { pthread_exit(NULL); }