Skip to main content
aboutsummaryrefslogblamecommitdiffstats
blob: 5865d9269a4e76de12312798707411d0b342707f (plain) (tree)
1
2
3
4
5
6
7
8
9
10


                                                                                
                                                                       
                                                           


                                         

                
                                                            






                                                                                 









                                                                                                               














                                  
                                   





                                                        
                                           






                                                 
                                  





                                             
                         
                            
                        
 


                                                                         

                                                  


                                                                          

                                            
                                                                                             
                                                       


                                                                                                                                                 
                                                                    
                                                                         






                                                            

                                                
 






                                                                                  
                                              
 


                                                  




                                                                                                                     

                                                 



                                                               
                                                      

                                         



                                                                  

                                            
                                                    




                                                                




                                                                      
                                                                                                        


                                       









                                                                


                                                                                                                   

                 
                                                          
                 
                                          

                                                                         
                                                                          
                                                                                                                   
                                                                                                

                                       


                                                                                                                       
                 





                                                



















                                                                                           
                                                                                                                      

                                              
                                                                                                                      

                                              
                                                                                                                          

                                              













                                                                                   
                                                                                                           












                                                                   
                                                                                       























                                                                                                   
/*******************************************************************************
 * Copyright (c) 2013 protos software gmbh (http://www.protos.de).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * CONTRIBUTORS:
 * 		Henrik Rentz-Reichert (initial contribution)
 *
 *******************************************************************************/

/**
 *
 * etTimer.c POSIX implementation of etTimer
 *
 * We use standard POSIX timers and signal handlers for the implementation.
 *
 * The idea is that all timer data (of type etTimer) are added to a linked list.
 * When a timer goes off the handler is called which sets the 'signaled' flag of the corresponding list element
 * (all list access is guarded by a global mutex) and signals a global semaphore.
 *
 * The global timer thread (which together with the other global synchronization objects is started once and
 * for all when the first timer is constructed) is waiting on this semaphore. When it is released it iterates
 * the linked list and checks for signaled timers. For signaled timers it resets the signaled flag and
 * calls the user function and retires again.
 */

#include <signal.h>
#include <string.h>
#include <errno.h>

#include "osal/etTimer.h"
#include "osal/etThread.h"
#include "osal/etSema.h"
#include "osal/etMutex.h"
#include "helpers/etTimeHelpers.h"

#include "debugging/etLogger.h"
#include "debugging/etMSCLogger.h"

/* the signal used for the timer */
#define TIMER_SIGNAL				SIGRTMIN

/* head of linked list of etTimer structs */
static etTimer* timers = NULL;

/* control initialization */
static etBool timer_initialized = ET_FALSE;

/* thread calling the timer functions */
#define TIMER_THREAD_STACK_SIZE		1024
#define TIMER_THREAD_PRIO			5

static etThread timer_thread;

/* semaphore used for signaling */
static etSema timer_sema;

/* mutex to guard linked list access */
static etMutex timer_mutex;

static void timerThreadFunction(void* data) {
	while (ET_TRUE) {
		etTimer* it;
		int idx;

#ifdef DEBUG_TIMER
		printf("timerThreadFunction: waiting\n"); fflush(stdout);
#endif
		etSema_waitForWakeup(&timer_sema);

#ifdef DEBUG_TIMER
		printf("timerThreadFunction: checking\n"); fflush(stdout);
#endif

		etMutex_enter(&timer_mutex);
		for (it=timers, idx=0; it!=NULL; it=(etTimer*) it->osTimerData.next, ++idx) {
			if (it->osTimerData.signaled) {
#ifdef DEBUG_TIMER
				printf("timerThreadFunction: signaled %d, calling user fct %p\n", idx, (void*)it->timerFunction); fflush(stdout);
#endif
				it->osTimerData.signaled = ET_FALSE;
				it->timerFunction(it->timerFunctionData);
			}
		}
		etMutex_leave(&timer_mutex);
	}
}

static void timerHandler(int sig, siginfo_t *si, void *uc) {
	etTimer* timer = si->si_value.sival_ptr;
	int sval = 0;

	/*
	 * Do not acquire the timer mutex in the handler!
	 * See signal-safety in linux manual.
	 * Amongst other things, this can cause deadlocks
	 * when the thread that executes the signal handler already holds the lock
	 * and then tries to acquire it again in the signal handler.
	 */
	timer->osTimerData.signaled = ET_TRUE;

	sem_getvalue(&(timer_sema.osData), &sval);
	if (sval==0)
		etSema_wakeup(&timer_sema);
}

void etTimer_construct(etTimer* self, etTime* timerInterval, etTimerFunction timerFunction, void* timerFunctionData){
	ET_MSC_LOGGER_SYNC_ENTRY("etTimer", "construct")
	{
		memset(self, 0, sizeof(etTimer));

		self->timerInterval.sec = timerInterval->sec;
		self->timerInterval.nSec = timerInterval->nSec;
		self->timerFunction = timerFunction;
		self->timerFunctionData = timerFunctionData;
		self->osTimerData.signaled = ET_FALSE;

		if (!timer_initialized) {
			/*
			 * All this is done once and for all.
			 * The resources are never released again.
			 */
			struct sigaction sa;

			timer_initialized = ET_TRUE;

			/* initialize our mutex and semaphore */
			etMutex_construct(&timer_mutex);
			etSema_construct(&timer_sema);

			/* we set up a signal handler */
			sigemptyset(&sa.sa_mask);
			sa.sa_flags = SA_SIGINFO;
			sa.sa_sigaction = timerHandler;
			if (sigaction(TIMER_SIGNAL, &sa, NULL) != 0) {
				etLogger_logError("etTimer_construct: failed setting action handler\n");
				return;
			}

			/* we start the timer thread */
			etThread_construct(
					&timer_thread,
					TIMER_THREAD_STACK_SIZE,
					TIMER_THREAD_PRIO,
					"timer_thread",
					timerThreadFunction,
					NULL);
			etThread_start(&timer_thread);

#ifdef DEBUG_TIMER
			printf("etTimer_construct: installed signal handler and started thread\n"); fflush(stdout);
#endif
		}

		/* create the timer (in disarmed state) */
		{
			/* create timer */
			self->osTimerData.te.sigev_notify = SIGEV_SIGNAL;
			self->osTimerData.te.sigev_signo = TIMER_SIGNAL;
			self->osTimerData.te.sigev_value.sival_ptr = self;
			if (timer_create(CLOCK_REALTIME, &self->osTimerData.te, &self->osTimerData.timerid) != 0) {
				etLogger_logError("etTimer_construct: failed creating a timer");
				return;
			}
#ifdef DEBUG_TIMER
			printf("etTimer_construct: user callback is %p\n", (void*)self->timerFunction); fflush(stdout);
#endif
		}

		/* place at list head */
		etMutex_enter(&timer_mutex);
		self->osTimerData.next = timers;
		timers = self;
		etMutex_leave(&timer_mutex);
	}
	ET_MSC_LOGGER_SYNC_EXIT
}

void etTimer_start(etTimer* self){
	ET_MSC_LOGGER_SYNC_ENTRY("etTimer", "start")
	{
		if (timers == NULL){
			etLogger_logError("etTimer_start: no timer initialized (NULL)");
		}
		else {
			struct itimerspec its;

			its.it_interval.tv_sec = self->timerInterval.sec;
			its.it_interval.tv_nsec = self->timerInterval.nSec;
			its.it_value.tv_sec = self->timerInterval.sec;
			its.it_value.tv_nsec = self->timerInterval.nSec;
			if (timer_settime(self->osTimerData.timerid, 0, &its, NULL) != 0) {
				switch (errno) {
				case EFAULT:
					etLogger_logError("etTimer_start: failed starting a timer with errno EFAULT");
					break;
				case EINVAL:
					etLogger_logError("etTimer_start: failed starting a timer with errno EINVAL");
					break;
				default:
					etLogger_logErrorF("etTimer_start: failed starting a timer with errno %d", errno);
					break;
				}
			}
		}
	}
	ET_MSC_LOGGER_SYNC_EXIT
}

void etTimer_stop(etTimer* self){
	ET_MSC_LOGGER_SYNC_ENTRY("etTimer", "stop")
	{
		struct itimerspec its;

		/* disarm the timer */
		memset(&its, 0, sizeof(its));
		if (timer_settime(self->osTimerData.timerid, 0, &its, NULL) != 0) {
			etLogger_logErrorF("etTimer_stop: failed stopping a timer with errno %d\n", errno);
		}
	}
	ET_MSC_LOGGER_SYNC_EXIT
}

void etTimer_destruct(etTimer* self){
	ET_MSC_LOGGER_SYNC_ENTRY("etTimer", "destruct")
	{
		etTimer* it;
		etTimer* pred = NULL;

		/* delete timer */
		if (timer_delete(self->osTimerData.timerid) != 0) {
			etLogger_logError("etTimer_delete: failed deleting a timer\n");
		}

		/* remove from queue */
		etMutex_enter(&timer_mutex);
		for (it=timers; it!=NULL; pred=it, it=(etTimer*) it->osTimerData.next) {
			if (it==self) {

				/* remove from list */
				if (pred==NULL) {
					timers = (etTimer*) it->osTimerData.next;

					if (timers==NULL) {
						/* TODO: last element removed, stop thread etc.? */
					}
				}
				else {
					pred->osTimerData.next = (etTimer*) it->osTimerData.next;
				}
			}
		}
		etMutex_leave(&timer_mutex);
	}
	ET_MSC_LOGGER_SYNC_EXIT
}

Back to the top