projects/linux/platform.c (354 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. #include <unistd.h> #include <time.h> #include <errno.h> #include <signal.h> #include <string.h> #include "platform_api.h" #include "status/rot_status.h" void platform_msleep (uint32_t msec) { struct timespec sleep_time; sleep_time.tv_sec = msec / 1000; sleep_time.tv_nsec = (msec % 1000) * 1000000ULL; nanosleep (&sleep_time, NULL); } /** * Initialize a timeout based on a specific clock source. * * @param msec The number of milliseconds to use for the timeout. * @param clock The clock source to use. * @param timeout The structure that will indicated the absolute timeout. * * @return 0 if the timeout was successfully initialized or an error code. */ static int platform_init_timeout_from_clock (uint32_t msec, clockid_t clock, platform_clock *timeout) { int status; status = clock_gettime (clock, timeout); if (status != 0) { return PLATFORM_TIMEOUT_ERROR (errno); } return platform_increase_timeout (msec, timeout); } int platform_init_timeout (uint32_t msec, platform_clock *timeout) { if (timeout == NULL) { return PLATFORM_TIMEOUT_ERROR (EINVAL); } return platform_init_timeout_from_clock (msec, CLOCK_MONOTONIC, timeout); } int platform_increase_timeout (uint32_t msec, platform_clock *timeout) { if (timeout == NULL) { return PLATFORM_TIMEOUT_ERROR (EINVAL); } timeout->tv_sec += (msec / 1000); timeout->tv_nsec += (msec % 1000) * 1000000ULL; if (timeout->tv_nsec > 999999999L) { timeout->tv_sec++; timeout->tv_nsec -= 1000000000ULL; } return 0; } /** * Check to see if a timeout has expired relative to specified time. * * @param now The current time. * @param timeout The timeout to check. * * @return 1 if the timeout has expired or 0 if it has not. */ static int platform_check_timeout (const platform_clock *now, const platform_clock *timeout) { if (now->tv_sec > timeout->tv_sec) { return 1; } else if (now->tv_sec < timeout->tv_sec) { return 0; } else if (now->tv_nsec < timeout->tv_nsec) { return 0; } else { return 1; } } int platform_has_timeout_expired (const platform_clock *timeout) { int status; struct timespec now; if (timeout == NULL) { return PLATFORM_TIMEOUT_ERROR (EINVAL); } status = clock_gettime (CLOCK_MONOTONIC, &now); if (status != 0) { return PLATFORM_TIMEOUT_ERROR (errno); } return platform_check_timeout (&now, timeout); } int platform_get_timeout_remaining (const platform_clock *timeout, uint32_t *msec) { int status; struct timespec now; if ((timeout == NULL) || (msec == NULL)) { return PLATFORM_TIMEOUT_ERROR (EINVAL); } status = clock_gettime (CLOCK_MONOTONIC, &now); if (status != 0) { return PLATFORM_TIMEOUT_ERROR (errno); } if (platform_check_timeout (&now, timeout)) { *msec = 0; } else { *msec = platform_get_duration (&now, timeout); } return 0; } int platform_init_current_tick (platform_clock *currtime) { int status; if (currtime == NULL) { return PLATFORM_TIMEOUT_ERROR (EINVAL); } status = clock_gettime (CLOCK_MONOTONIC, currtime); if (status != 0) { return PLATFORM_TIMEOUT_ERROR (errno); } return 0; } uint32_t platform_get_duration (const platform_clock *start, const platform_clock *end) { if ((end == NULL) || (start == NULL)) { return 0; } if (start->tv_sec > end->tv_sec) { return 0; } else if (start->tv_sec == end->tv_sec) { if (start->tv_nsec > end->tv_nsec) { return 0; } else { /* Round to the nearest millisecond. */ return ((end->tv_nsec - start->tv_nsec) + 500000) / 1000000ULL; } } else { uint64_t nsec_duration = end->tv_nsec; uint32_t duration; /* The total number of nanoseconds is total of ns in the current second and the remaining * ns from the previous second. */ nsec_duration = end->tv_nsec; nsec_duration += (1000000000ULL - start->tv_nsec); /* Round duration to the nearest millisecond. */ duration = (nsec_duration + 500000) / 1000000ULL; duration += (end->tv_sec - start->tv_sec - 1) * 1000; return duration; } } int platform_mutex_init (platform_mutex *mutex) { int status = pthread_mutex_init (mutex, NULL); return (status == 0) ? 0 : PLATFORM_MUTEX_ERROR (status); } int platform_mutex_free (platform_mutex *mutex) { int status = pthread_mutex_destroy (mutex); return (status == 0) ? 0 : PLATFORM_MUTEX_ERROR (status); } int platform_mutex_lock (platform_mutex *mutex) { int status = pthread_mutex_lock (mutex); return (status == 0) ? 0 : PLATFORM_MUTEX_ERROR (status); } int platform_mutex_unlock (platform_mutex *mutex) { int status = pthread_mutex_unlock (mutex); return (status == 0) ? 0 : PLATFORM_MUTEX_ERROR (status); } int platform_recursive_mutex_init (platform_mutex *mutex) { pthread_mutexattr_t attr; int status; if (mutex == NULL) { return PLATFORM_MUTEX_ERROR (EINVAL); } status = pthread_mutexattr_init (&attr); if (status != 0) { return PLATFORM_MUTEX_ERROR (status); } status = pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); if (status != 0) { return PLATFORM_MUTEX_ERROR (status); } status = pthread_mutex_init (mutex, &attr); return (status == 0) ? 0 : PLATFORM_MUTEX_ERROR (status); } /** * Internal thread function for running the timer. * * @param arg The timer context. */ static void* platform_timer_thread (void *arg) { platform_timer *timer = (platform_timer*) arg; int status; pthread_mutex_lock (&timer->lock); while (!timer->destroy) { if (timer->disarm) { pthread_mutex_unlock (&timer->lock); sem_wait (&timer->timer); pthread_mutex_lock (&timer->lock); } if (!timer->destroy) { pthread_mutex_unlock (&timer->lock); status = sem_timedwait (&timer->timer, &timer->timeout); if ((status != 0) && (errno == ETIMEDOUT)) { pthread_mutex_lock (&timer->lock); if (!timer->disarm && !timer->destroy) { timer->disarm = 1; timer->callback (timer->context); } } else { pthread_mutex_lock (&timer->lock); } } } pthread_mutex_unlock (&timer->lock); return NULL; } int platform_timer_create (platform_timer *timer, timer_callback callback, void *context) { pthread_mutexattr_t attr; int status; if ((timer == NULL) || (callback == NULL)) { return PLATFORM_TIMER_ERROR (EINVAL); } memset (timer, 0, sizeof (platform_timer)); status = sem_init (&timer->timer, 0, 0); if (status != 0) { return PLATFORM_TIMER_ERROR (errno); } pthread_mutexattr_init (&attr); pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); status = pthread_mutex_init (&timer->lock, &attr); pthread_mutexattr_destroy (&attr); if (status != 0) { sem_destroy (&timer->timer); return PLATFORM_TIMER_ERROR (status); } timer->disarm = 1; timer->callback = callback; timer->context = context; status = pthread_create (&timer->thread, NULL, platform_timer_thread, timer); if (status != 0) { pthread_mutex_destroy (&timer->lock); sem_destroy (&timer->timer); return PLATFORM_TIMER_ERROR (status); } return 0; } int platform_timer_arm_one_shot (platform_timer *timer, uint32_t ms_timeout) { int status; struct timespec timeout; if ((timer == NULL) || (ms_timeout == 0)) { return PLATFORM_TIMER_ERROR (EINVAL); } status = platform_init_timeout_from_clock (ms_timeout, CLOCK_REALTIME, &timeout); if (status != 0) { return status; } pthread_mutex_lock (&timer->lock); timer->disarm = 0; timer->timeout = timeout; status = sem_post (&timer->timer); pthread_mutex_unlock (&timer->lock); return (status == 0) ? 0 : PLATFORM_TIMER_ERROR (errno); } int platform_timer_disarm (platform_timer *timer) { int status = 0; if (timer == NULL) { return PLATFORM_TIMER_ERROR (EINVAL); } pthread_mutex_lock (&timer->lock); if (!timer->disarm) { timer->disarm = 1; status = sem_post (&timer->timer); } pthread_mutex_unlock (&timer->lock); return (status == 0) ? 0 : PLATFORM_TIMER_ERROR (errno); } void platform_timer_delete (platform_timer *timer) { int status; if (timer != NULL) { pthread_mutex_lock (&timer->lock); timer->destroy = 1; timer->disarm = 1; status = sem_post (&timer->timer); pthread_mutex_unlock (&timer->lock); if (status == 0) { pthread_join (timer->thread, NULL); pthread_mutex_destroy (&timer->lock); sem_destroy (&timer->timer); } } } int platform_semaphore_init (platform_semaphore *sem) { int status; if (sem == NULL) { return PLATFORM_SEMAPHORE_ERROR (EINVAL); } status = sem_init (sem, 0, 0); return (status == 0) ? 0 : PLATFORM_SEMAPHORE_ERROR (errno); } void platform_semaphore_free (platform_semaphore *sem) { if (sem) { sem_destroy (sem); } } int platform_semaphore_post (platform_semaphore *sem) { int status; if (sem == NULL) { return PLATFORM_SEMAPHORE_ERROR (EINVAL); } status = sem_post (sem); return (status == 0) ? 0 : PLATFORM_SEMAPHORE_ERROR (errno); } int platform_semaphore_wait (platform_semaphore *sem, uint32_t ms_timeout) { platform_clock timeout; int status; if (sem == NULL) { return PLATFORM_SEMAPHORE_ERROR (EINVAL); } if (ms_timeout == 0) { status = sem_wait (sem); } else { status = platform_init_timeout_from_clock (ms_timeout, CLOCK_REALTIME, &timeout); if (status != 0) { return status; } status = sem_timedwait (sem, &timeout); if ((status != 0) && (errno == ETIMEDOUT)) { return 1; } } return (status == 0) ? 0 : PLATFORM_SEMAPHORE_ERROR (errno); } int platform_semaphore_try_wait (platform_semaphore *sem) { int status; if (sem == NULL) { return PLATFORM_SEMAPHORE_ERROR (EINVAL); } status = sem_trywait (sem); if (status == 0) { return 0; } else if (errno == EAGAIN) { return 1; } else { return PLATFORM_SEMAPHORE_ERROR (errno); } } int platform_semaphore_reset (platform_semaphore *sem) { int status; do { status = platform_semaphore_try_wait (sem); } while (status == 0); return (status == 1) ? 0 : status; } /* Don't support OS suspending. This is typically a feature used only in target devices, anyway. */ int platform_os_suspend_scheduler () { return PLATFORM_OS_ERROR (ENOSYS); } int platform_os_resume_scheduler () { return PLATFORM_OS_ERROR (ENOSYS); }