diff options
-rwxr-xr-x[-rw-r--r--] | gki/ulinux/gki_ulinux.c | 101 |
1 files changed, 92 insertions, 9 deletions
diff --git a/gki/ulinux/gki_ulinux.c b/gki/ulinux/gki_ulinux.c index fabaea1..e4e0323 100644..100755 --- a/gki/ulinux/gki_ulinux.c +++ b/gki/ulinux/gki_ulinux.c @@ -89,6 +89,9 @@ static int shutdown_timer = 0; #define GKI_SHUTDOWN_EVT APPL_EVT_7 #endif +#define __likely(cond) __builtin_expect(!!(cond), 1) +#define __unlikely(cond) __builtin_expect(!!(cond), 0) + /***************************************************************************** ** Local type definitions ******************************************************************************/ @@ -538,7 +541,7 @@ void GKI_shutdown(void) void gki_system_tick_start_stop_cback(BOOLEAN start) { tGKI_OS *p_os = &gki_cb.os; - volatile int *p_run_cond = &p_os->no_timer_suspend; + int *p_run_cond = &p_os->no_timer_suspend; static int wake_lock_count; if ( FALSE == start ) { @@ -584,24 +587,104 @@ void gki_system_tick_start_stop_cback(BOOLEAN start) ** one step, If your OS does it in one step, this function ** should be empty. *********************************************************************************/ - #ifdef NO_GKI_RUN_RETURN void* timer_thread(void *arg) { - struct timespec delay; + int timeout_ns=0; + struct timespec timeout; + struct timespec previous = {0,0}; + struct timespec current; int err; + int delta_ns; + int restart; + tGKI_OS *p_os = &gki_cb.os; + int *p_run_cond = &p_os->no_timer_suspend; + + /* Indicate that tick is just starting */ + restart = 1; while(!shutdown_timer) { - delay.tv_sec = LINUX_SEC / 1000; - delay.tv_nsec = 1000 * 1000 * (LINUX_SEC%1000); + /* If the timer has been stopped (no SW timer running) */ + if (*p_run_cond == GKI_TIMER_TICK_STOP_COND) + { + /* + * We will lock/wait on GKI_timer_mutex. + * This mutex will be unlocked when timer is re-started + */ + GKI_TRACE("GKI_run lock mutex"); + pthread_mutex_lock(&p_os->gki_timer_mutex); + + /* We are here because the mutex has been released by timer cback */ + /* Let's release it for future use */ + GKI_TRACE("GKI_run unlock mutex"); + pthread_mutex_unlock(&p_os->gki_timer_mutex); - /* [u]sleep can't be used because it uses SIGALRM */ + /* Indicate that tick is just starting */ + restart = 1; + } + + /* Get time */ + clock_gettime(CLOCK_MONOTONIC, ¤t); + + /* Check if tick was just restarted, indicating to the compiler that this is + * unlikely to happen (to help branch prediction) */ + if (__unlikely(restart)) + { + /* Clear the restart indication */ + restart = 0; - do { - err = nanosleep(&delay, &delay); - } while (err < 0 && errno ==EINTR); + timeout_ns = (GKI_TICKS_TO_MS(1) * 1000000); + } + else + { + /* Compute time elapsed since last sleep start */ + delta_ns = current.tv_nsec - previous.tv_nsec; + delta_ns += (current.tv_sec - previous.tv_sec) * 1000000000; + + /* Compute next timeout: + * timeout = (next theoretical expiration) - current time + * timeout = (previous time + timeout + delay) - current time + * timeout = timeout + delay - (current time - previous time) + * timeout += delay - delta */ + timeout_ns += (GKI_TICKS_TO_MS(1) * 1000000) - delta_ns; + } + /* Save the current time for next iteration */ + previous = current; + + timeout.tv_sec = 0; + + /* Sleep until next theoretical tick time. In case of excessive + elapsed time since last theoretical tick expiration, it is + possible that the timeout value is negative. To protect + against this error, we set minimum sleep time to 10% of the + tick period. We indicate to compiler that this is unlikely to + happen (to help branch prediction) */ + + if (__unlikely(timeout_ns < ((GKI_TICKS_TO_MS(1) * 1000000) * 0.1))) + { + timeout.tv_nsec = (GKI_TICKS_TO_MS(1) * 1000000) * 0.1; + + /* Print error message if tick really got delayed + (more than 5 ticks) */ + if (timeout_ns < GKI_TICKS_TO_MS(-5) * 1000000) + { + GKI_ERROR_LOG("tick delayed > 5 slots (%d,%d) -- cpu overload ? ", + timeout_ns, GKI_TICKS_TO_MS(-5) * 1000000); + } + } + else + { + timeout.tv_nsec = timeout_ns; + } + + do + { + /* [u]sleep can't be used because it uses SIGALRM */ + err = nanosleep(&timeout, &timeout); + } while (err < 0 && errno == EINTR); + /* Increment the GKI time value by one tick and update internal timers */ GKI_timer_update(1); } GKI_TRACE("gki_ulinux: Exiting timer_thread"); |