1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28/*#define VCOS_INLINE_BODIES */
29#include "interface/vcos/vcos.h"
30#include "interface/vcos/vcos_msgqueue.h"
31#include <string.h>
32#include <stdarg.h>
33#include <stdlib.h>
34#include <stdio.h>
35#include <sys/time.h>
36#include <linux/param.h>
37
38/* Cygwin doesn't always have prctl.h and it doesn't have PR_SET_NAME */
39#if defined( __linux__ )
40# if !defined(HAVE_PRCTL)
41# define HAVE_PRCTL
42# endif
43#include <sys/prctl.h>
44#endif
45
46#ifdef HAVE_CMAKE_CONFIG
47#include "cmake_config.h"
48#endif
49
50#ifdef HAVE_MTRACE
51#include <mcheck.h>
52#endif
53
54#if defined(ANDROID)
55#include <android/log.h>
56#endif
57
58#ifndef VCOS_DEFAULT_STACK_SIZE
59#define VCOS_DEFAULT_STACK_SIZE 4096
60#endif
61
62static int vcos_argc;
63static const char **vcos_argv;
64
65typedef void (*LEGACY_ENTRY_FN_T)(int, void *);
66
67static VCOS_THREAD_ATTR_T default_attrs = {
68 .ta_stacksz = VCOS_DEFAULT_STACK_SIZE,
69};
70
71/** Singleton global lock used for vcos_global_lock/unlock(). */
72static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
73
74#ifdef ANDROID
75static VCOS_MUTEX_T printf_lock;
76#endif
77
78/* Create a per-thread key for faking up vcos access
79 * on non-vcos threads.
80 */
81pthread_key_t _vcos_thread_current_key;
82
83static VCOS_UNSIGNED _vcos_thread_current_key_created = 0;
84static VCOS_ONCE_T current_thread_key_once; /* init just once */
85
86static void vcos_thread_cleanup(VCOS_THREAD_T *thread)
87{
88 vcos_semaphore_delete(&thread->suspend);
89 if (thread->task_timer_created)
90 {
91 vcos_timer_delete(&thread->task_timer);
92 }
93}
94
95static void vcos_dummy_thread_cleanup(void *cxt)
96{
97 VCOS_THREAD_T *thread = cxt;
98 if (thread->dummy)
99 {
100 int i;
101 /* call termination functions */
102 for (i=0; thread->at_exit[i].pfn != NULL; i++)
103 {
104 thread->at_exit[i].pfn(thread->at_exit[i].cxt);
105 }
106 vcos_thread_cleanup(thread);
107 vcos_free(thread);
108 }
109}
110
111static void current_thread_key_init(void)
112{
113 vcos_assert(!_vcos_thread_current_key_created);
114 pthread_key_create (&_vcos_thread_current_key, vcos_dummy_thread_cleanup);
115 _vcos_thread_current_key_created = 1;
116}
117
118
119/* A VCOS wrapper for the thread which called vcos_init. */
120static VCOS_THREAD_T vcos_thread_main;
121
122static void *vcos_thread_entry(void *arg)
123{
124 int i;
125 void *ret;
126 VCOS_THREAD_T *thread = (VCOS_THREAD_T *)arg;
127
128 vcos_assert(thread != NULL);
129 thread->dummy = 0;
130
131 pthread_setspecific(_vcos_thread_current_key, thread);
132#if defined( HAVE_PRCTL ) && defined( PR_SET_NAME )
133 /* cygwin doesn't have PR_SET_NAME */
134 prctl( PR_SET_NAME, (unsigned long)thread->name, 0, 0, 0 );
135#endif
136 if (thread->legacy)
137 {
138 LEGACY_ENTRY_FN_T fn = (LEGACY_ENTRY_FN_T)thread->entry;
139 (*fn)(0, thread->arg);
140 ret = 0;
141 }
142 else
143 {
144 ret = (*thread->entry)(thread->arg);
145 }
146
147 /* call termination functions */
148 for (i=0; thread->at_exit[i].pfn != NULL; i++)
149 {
150 thread->at_exit[i].pfn(thread->at_exit[i].cxt);
151 }
152
153 return ret;
154}
155
156static void _task_timer_expiration_routine(void *cxt)
157{
158 VCOS_THREAD_T *thread = (VCOS_THREAD_T *)cxt;
159
160 vcos_assert(thread->orig_task_timer_expiration_routine);
161 thread->orig_task_timer_expiration_routine(thread->orig_task_timer_context);
162 thread->orig_task_timer_expiration_routine = NULL;
163}
164
165VCOS_STATUS_T vcos_thread_create(VCOS_THREAD_T *thread,
166 const char *name,
167 VCOS_THREAD_ATTR_T *attrs,
168 VCOS_THREAD_ENTRY_FN_T entry,
169 void *arg)
170{
171 VCOS_STATUS_T st;
172 pthread_attr_t pt_attrs;
173 VCOS_THREAD_ATTR_T *local_attrs = attrs ? attrs : &default_attrs;
174 int rc;
175
176 vcos_assert(thread);
177 memset(thread, 0, sizeof(VCOS_THREAD_T));
178
179 rc = pthread_attr_init(&pt_attrs);
180 if (rc < 0)
181 return VCOS_ENOMEM;
182
183 st = vcos_semaphore_create(&thread->suspend, NULL, 0);
184 if (st != VCOS_SUCCESS)
185 {
186 pthread_attr_destroy(&pt_attrs);
187 return st;
188 }
189
190 pthread_attr_setstacksize(&pt_attrs, local_attrs->ta_stacksz);
191#if VCOS_CAN_SET_STACK_ADDR
192 if (local_attrs->ta_stackaddr)
193 {
194 pthread_attr_setstackaddr(&pt_attrs, local_attrs->ta_stackaddr);
195 }
196#else
197 vcos_demand(local_attrs->ta_stackaddr == 0);
198#endif
199
200 /* pthread_attr_setpriority(&pt_attrs, local_attrs->ta_priority); */
201
202 vcos_assert(local_attrs->ta_stackaddr == 0); /* Not possible */
203
204 thread->entry = entry;
205 thread->arg = arg;
206 thread->legacy = local_attrs->legacy;
207
208 strncpy(thread->name, name, sizeof(thread->name));
209 thread->name[sizeof(thread->name)-1] = '\0';
210 memset(thread->at_exit, 0, sizeof(thread->at_exit));
211
212 rc = pthread_create(&thread->thread, &pt_attrs, vcos_thread_entry, thread);
213
214 pthread_attr_destroy(&pt_attrs);
215
216 if (rc < 0)
217 {
218 vcos_semaphore_delete(&thread->suspend);
219 return VCOS_ENOMEM;
220 }
221 else
222 {
223 return VCOS_SUCCESS;
224 }
225}
226
227void vcos_thread_join(VCOS_THREAD_T *thread,
228 void **pData)
229{
230 pthread_join(thread->thread, pData);
231 vcos_thread_cleanup(thread);
232}
233
234VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_thread_create_classic(VCOS_THREAD_T *thread,
235 const char *name,
236 void *(*entry)(void *arg),
237 void *arg,
238 void *stack,
239 VCOS_UNSIGNED stacksz,
240 VCOS_UNSIGNED priaff,
241 VCOS_UNSIGNED timeslice,
242 VCOS_UNSIGNED autostart)
243{
244 VCOS_THREAD_ATTR_T attrs;
245 vcos_thread_attr_init(&attrs);
246 vcos_thread_attr_setstacksize(&attrs, stacksz);
247 vcos_thread_attr_setpriority(&attrs, priaff & ~_VCOS_AFFINITY_MASK);
248 vcos_thread_attr_setaffinity(&attrs, priaff & _VCOS_AFFINITY_MASK);
249 (void)timeslice;
250 (void)autostart;
251
252 if (VCOS_CAN_SET_STACK_ADDR)
253 {
254 vcos_thread_attr_setstack(&attrs, stack, stacksz);
255 }
256
257 return vcos_thread_create(thread, name, &attrs, entry, arg);
258}
259
260uint64_t vcos_getmicrosecs64_internal(void)
261{
262 struct timeval tv;
263 uint64_t tm = 0;
264
265 if (!gettimeofday(&tv, NULL))
266 {
267 tm = (tv.tv_sec * 1000000LL) + tv.tv_usec;
268 }
269
270 return tm;
271}
272
273#ifdef ANDROID
274
275static int log_prio[] =
276{
277 ANDROID_LOG_INFO, /* VCOS_LOG_UNINITIALIZED */
278 ANDROID_LOG_INFO, /* VCOS_LOG_NEVER */
279 ANDROID_LOG_ERROR, /* VCOS_LOG_ERROR */
280 ANDROID_LOG_WARN, /* VCOS_LOG_WARN */
281 ANDROID_LOG_INFO, /* VCOS_LOG_INFO */
282 ANDROID_LOG_DEBUG /* VCOS_LOG_TRACE */
283};
284
285int vcos_use_android_log = 1;
286int vcos_log_to_file = 0;
287#else
288int vcos_use_android_log = 0;
289int vcos_log_to_file = 0;
290#endif
291
292static FILE * log_fhandle = NULL;
293
294void vcos_vlog_default_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, va_list args)
295{
296 (void)_level;
297
298#ifdef ANDROID
299 if ( vcos_use_android_log )
300 {
301 __android_log_vprint(log_prio[_level], cat->name, fmt, args);
302 }
303 else
304 {
305 vcos_mutex_lock(&printf_lock);
306#endif
307 if(NULL != log_fhandle)
308 {
309 if (cat->flags.want_prefix)
310 fprintf( log_fhandle, "%s: ", cat->name );
311 vfprintf(log_fhandle, fmt, args);
312 fputs("\n", log_fhandle);
313 fflush(log_fhandle);
314 }
315#ifdef ANDROID
316 vcos_mutex_unlock(&printf_lock);
317 }
318#endif
319}
320
321void _vcos_log_platform_init(void)
322{
323 if(vcos_log_to_file)
324 {
325 char log_fname[100];
326#ifdef ANDROID
327 snprintf(log_fname, 100, "/data/log/vcos_log%u.txt", vcos_process_id_current());
328#else
329 snprintf(log_fname, 100, "/var/log/vcos_log%u.txt", vcos_process_id_current());
330#endif
331 log_fhandle = fopen(log_fname, "w");
332 }
333 else
334 log_fhandle = stderr;
335}
336
337/* Flags for init/deinit components */
338enum
339{
340 VCOS_INIT_NAMED_SEM = (1 << 0),
341 VCOS_INIT_PRINTF_LOCK = (1 << 1),
342 VCOS_INIT_MAIN_SEM = (1 << 2),
343 VCOS_INIT_MSGQ = (1 << 3),
344 VCOS_INIT_ALL = 0xffffffff
345};
346
347static void vcos_term(uint32_t flags)
348{
349 if (flags & VCOS_INIT_MSGQ)
350 vcos_msgq_deinit();
351
352 if (flags & VCOS_INIT_MAIN_SEM)
353 vcos_semaphore_delete(&vcos_thread_main.suspend);
354
355#ifdef ANDROID
356 if (flags & VCOS_INIT_PRINTF_LOCK)
357 vcos_mutex_delete(&printf_lock);
358#endif
359
360 if (flags & VCOS_INIT_NAMED_SEM)
361 _vcos_named_semaphore_deinit();
362}
363
364VCOS_STATUS_T vcos_platform_init(void)
365{
366 VCOS_STATUS_T st;
367 uint32_t flags = 0;
368 int pst;
369
370 st = _vcos_named_semaphore_init();
371 if (!vcos_verify(st == VCOS_SUCCESS))
372 goto end;
373
374 flags |= VCOS_INIT_NAMED_SEM;
375
376#ifdef HAVE_MTRACE
377 /* enable glibc memory debugging, if the environment
378 * variable MALLOC_TRACE names a valid file.
379 */
380 mtrace();
381#endif
382
383#ifdef ANDROID
384 st = vcos_mutex_create(&printf_lock, "printf");
385 if (!vcos_verify(st == VCOS_SUCCESS))
386 goto end;
387
388 flags |= VCOS_INIT_PRINTF_LOCK;
389#endif
390
391 st = vcos_once(&current_thread_key_once, current_thread_key_init);
392 if (!vcos_verify(st == VCOS_SUCCESS))
393 goto end;
394
395 /* Initialise a VCOS wrapper for the thread which called vcos_init. */
396 st = vcos_semaphore_create(&vcos_thread_main.suspend, NULL, 0);
397 if (!vcos_verify(st == VCOS_SUCCESS))
398 goto end;
399
400 flags |= VCOS_INIT_MAIN_SEM;
401
402 vcos_thread_main.thread = pthread_self();
403
404 pst = pthread_setspecific(_vcos_thread_current_key, &vcos_thread_main);
405 if (!vcos_verify(pst == 0))
406 {
407 st = VCOS_EINVAL;
408 goto end;
409 }
410
411 st = vcos_msgq_init();
412 if (!vcos_verify(st == VCOS_SUCCESS))
413 goto end;
414
415 flags |= VCOS_INIT_MSGQ;
416
417 vcos_logging_init();
418
419end:
420 if (st != VCOS_SUCCESS)
421 vcos_term(flags);
422
423 return st;
424}
425
426void vcos_platform_deinit(void)
427{
428 vcos_term(VCOS_INIT_ALL);
429}
430
431void vcos_global_lock(void)
432{
433 pthread_mutex_lock(&lock);
434}
435
436void vcos_global_unlock(void)
437{
438 pthread_mutex_unlock(&lock);
439}
440
441void vcos_thread_exit(void *arg)
442{
443 VCOS_THREAD_T *thread = vcos_thread_current();
444
445 if ( thread && thread->dummy )
446 {
447 vcos_free ( (void*) thread );
448 thread = NULL;
449 }
450
451 pthread_exit(arg);
452}
453
454
455void vcos_thread_attr_init(VCOS_THREAD_ATTR_T *attrs)
456{
457 *attrs = default_attrs;
458}
459
460VCOS_STATUS_T vcos_pthreads_map_error(int error)
461{
462 switch (error)
463 {
464 case ENOMEM:
465 return VCOS_ENOMEM;
466 case ENXIO:
467 return VCOS_ENXIO;
468 case EAGAIN:
469 return VCOS_EAGAIN;
470 case ENOSPC:
471 return VCOS_ENOSPC;
472 default:
473 return VCOS_EINVAL;
474 }
475}
476
477VCOS_STATUS_T vcos_pthreads_map_errno(void)
478{
479 return vcos_pthreads_map_error(errno);
480}
481
482void _vcos_task_timer_set(void (*pfn)(void*), void *cxt, VCOS_UNSIGNED ms)
483{
484 VCOS_THREAD_T *thread = vcos_thread_current();
485
486 if (thread == NULL)
487 return;
488
489 vcos_assert(thread->orig_task_timer_expiration_routine == NULL);
490
491 if (!thread->task_timer_created)
492 {
493 VCOS_STATUS_T st = vcos_timer_create(&thread->task_timer, NULL,
494 _task_timer_expiration_routine, thread);
495 (void)st;
496 vcos_assert(st == VCOS_SUCCESS);
497 thread->task_timer_created = 1;
498 }
499
500 thread->orig_task_timer_expiration_routine = pfn;
501 thread->orig_task_timer_context = cxt;
502
503 vcos_timer_set(&thread->task_timer, ms);
504}
505
506void _vcos_task_timer_cancel(void)
507{
508 VCOS_THREAD_T *thread = vcos_thread_current();
509
510 if (thread == NULL || !thread->task_timer_created)
511 return;
512
513 vcos_timer_cancel(&thread->task_timer);
514 thread->orig_task_timer_expiration_routine = NULL;
515}
516
517int vcos_vsnprintf( char *buf, size_t buflen, const char *fmt, va_list ap )
518{
519 return vsnprintf( buf, buflen, fmt, ap );
520}
521
522int vcos_snprintf(char *buf, size_t buflen, const char *fmt, ...)
523{
524 int ret;
525 va_list ap;
526 va_start(ap,fmt);
527 ret = vsnprintf(buf, buflen, fmt, ap);
528 va_end(ap);
529 return ret;
530}
531
532int vcos_have_rtos(void)
533{
534 return 1;
535}
536
537const char * vcos_thread_get_name(const VCOS_THREAD_T *thread)
538{
539 return thread->name;
540}
541
542#ifdef VCOS_HAVE_BACKTRACK
543void __attribute__((weak)) vcos_backtrace_self(void);
544#endif
545
546void vcos_pthreads_logging_assert(const char *file, const char *func, unsigned int line, const char *fmt, ...)
547{
548 va_list ap;
549 va_start(ap, fmt);
550 fprintf(stderr, "assertion failure:%s:%d:%s():",
551 file, line, func);
552 vfprintf(stderr, fmt, ap);
553 va_end(ap);
554 fprintf(stderr, "\n");
555
556#ifdef VCOS_HAVE_BACKTRACK
557 if (vcos_backtrace_self)
558 vcos_backtrace_self();
559#endif
560 abort();
561}
562
563extern VCOS_STATUS_T vcos_thread_at_exit(void (*pfn)(void*), void *cxt)
564{
565 int i;
566 VCOS_THREAD_T *self = vcos_thread_current();
567 if (!self)
568 {
569 vcos_assert(0);
570 return VCOS_EINVAL;
571 }
572 for (i=0; i<VCOS_MAX_EXIT_HANDLERS; i++)
573 {
574 if (self->at_exit[i].pfn == NULL)
575 {
576 self->at_exit[i].pfn = pfn;
577 self->at_exit[i].cxt = cxt;
578 return VCOS_SUCCESS;
579 }
580 }
581 return VCOS_ENOSPC;
582}
583
584void vcos_set_args(int argc, const char **argv)
585{
586 vcos_argc = argc;
587 vcos_argv = argv;
588}
589
590int vcos_get_argc(void)
591{
592 return vcos_argc;
593}
594
595const char ** vcos_get_argv(void)
596{
597 return vcos_argv;
598}
599
600/* we can't inline this, because HZ comes from sys/param.h which
601 * dumps all sorts of junk into the global namespace, notable MIN and
602 * MAX.
603 */
604uint32_t _vcos_get_ticks_per_second(void)
605{
606 return HZ;
607}
608
609VCOS_STATUS_T vcos_once(VCOS_ONCE_T *once_control,
610 void (*init_routine)(void))
611{
612 int rc = pthread_once(once_control, init_routine);
613 if (rc != 0)
614 {
615 switch (errno)
616 {
617 case EINVAL:
618 return VCOS_EINVAL;
619 default:
620 vcos_assert(0);
621 return VCOS_EACCESS;
622 }
623 }
624 else
625 {
626 return VCOS_SUCCESS;
627 }
628}
629
630
631VCOS_THREAD_T *vcos_dummy_thread_create(void)
632{
633 VCOS_STATUS_T st;
634 VCOS_THREAD_T *thread_hndl = NULL;
635 int rc;
636
637 thread_hndl = (VCOS_THREAD_T *)vcos_malloc(sizeof(VCOS_THREAD_T), NULL);
638 vcos_assert(thread_hndl != NULL);
639
640 memset(thread_hndl, 0, sizeof(VCOS_THREAD_T));
641
642 thread_hndl->dummy = 1;
643 thread_hndl->thread = pthread_self();
644
645 st = vcos_semaphore_create(&thread_hndl->suspend, NULL, 0);
646 if (st != VCOS_SUCCESS)
647 {
648 vcos_free(thread_hndl);
649 return( thread_hndl );
650 }
651
652 vcos_once(&current_thread_key_once, current_thread_key_init);
653
654 rc = pthread_setspecific(_vcos_thread_current_key,
655 thread_hndl);
656 (void)rc;
657
658 return( thread_hndl );
659}
660
661
662/***********************************************************
663 *
664 * Timers
665 *
666 ***********************************************************/
667
668/* On Linux we could use POSIX timers with a bit of synchronization.
669 * Unfortunately POSIX timers on Bionic are NOT POSIX compliant
670 * what makes that option not viable.
671 * That's why we ended up with our own implementation of timers.
672 * NOTE: That condition variables on Bionic are also buggy and
673 * they work incorrectly with CLOCK_MONOTONIC, so we have to
674 * use CLOCK_REALTIME (and hope that no one will change the time
675 * significantly after the timer has been set up
676 */
677#define NSEC_IN_SEC (1000*1000*1000)
678#define MSEC_IN_SEC (1000)
679#define NSEC_IN_MSEC (1000*1000)
680
681static int _timespec_is_zero(struct timespec *ts)
682{
683 return ((ts->tv_sec == 0) && (ts->tv_nsec == 0));
684}
685
686static void _timespec_set_zero(struct timespec *ts)
687{
688 ts->tv_sec = ts->tv_nsec = 0;
689}
690
691/* Adds left to right and stores the result in left */
692static void _timespec_add(struct timespec *left, struct timespec *right)
693{
694 left->tv_sec += right->tv_sec;
695 left->tv_nsec += right->tv_nsec;
696 if (left->tv_nsec >= (NSEC_IN_SEC))
697 {
698 left->tv_nsec -= NSEC_IN_SEC;
699 left->tv_sec++;
700 }
701}
702
703static int _timespec_is_larger(struct timespec *left, struct timespec *right)
704{
705 if (left->tv_sec != right->tv_sec)
706 return left->tv_sec > right->tv_sec;
707 else
708 return left->tv_nsec > right->tv_nsec;
709}
710
711static void* _timer_thread(void *arg)
712{
713 VCOS_TIMER_T *timer = (VCOS_TIMER_T*)arg;
714
715 pthread_mutex_lock(&timer->lock);
716 while (!timer->quit)
717 {
718 struct timespec now;
719
720 /* Wait until next expiry time, or until timer's settings are changed */
721 if (_timespec_is_zero(&timer->expires))
722 pthread_cond_wait(&timer->settings_changed, &timer->lock);
723 else
724 pthread_cond_timedwait(&timer->settings_changed, &timer->lock, &timer->expires);
725
726 /* See if the timer has expired - reloop if it didn't */
727 clock_gettime(CLOCK_REALTIME, &now);
728 if (_timespec_is_zero(&timer->expires) || _timespec_is_larger(&timer->expires, &now))
729 continue;
730
731 /* The timer has expired. Clear the expiry time and call the
732 * expiration routine
733 */
734 _timespec_set_zero(&timer->expires);
735 timer->orig_expiration_routine(timer->orig_context);
736 }
737 pthread_mutex_unlock(&timer->lock);
738
739 return NULL;
740}
741
742VCOS_STATUS_T vcos_timer_init(void)
743{
744 return VCOS_SUCCESS;
745}
746
747VCOS_STATUS_T vcos_pthreads_timer_create(VCOS_TIMER_T *timer,
748 const char *name,
749 void (*expiration_routine)(void *context),
750 void *context)
751{
752 pthread_mutexattr_t lock_attr;
753 VCOS_STATUS_T result = VCOS_SUCCESS;
754 int settings_changed_initialized = 0;
755 int lock_attr_initialized = 0;
756 int lock_initialized = 0;
757
758 (void)name;
759
760 vcos_assert(timer);
761 vcos_assert(expiration_routine);
762
763 memset(timer, 0, sizeof(VCOS_TIMER_T));
764
765 timer->orig_expiration_routine = expiration_routine;
766 timer->orig_context = context;
767
768 /* Create conditional variable for notifying the timer's thread
769 * when settings change.
770 */
771 if (result == VCOS_SUCCESS)
772 {
773 int rc = pthread_cond_init(&timer->settings_changed, NULL);
774 if (rc == 0)
775 settings_changed_initialized = 1;
776 else
777 result = vcos_pthreads_map_error(rc);
778 }
779
780 /* Create attributes for the lock (we want it to be recursive) */
781 if (result == VCOS_SUCCESS)
782 {
783 int rc = pthread_mutexattr_init(&lock_attr);
784 if (rc == 0)
785 {
786 pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_RECURSIVE);
787 lock_attr_initialized = 1;
788 }
789 else
790 {
791 result = vcos_pthreads_map_error(rc);
792 }
793 }
794
795 /* Create lock for the timer structure */
796 if (result == VCOS_SUCCESS)
797 {
798 int rc = pthread_mutex_init(&timer->lock, &lock_attr);
799 if (rc == 0)
800 lock_initialized = 1;
801 else
802 result = vcos_pthreads_map_error(rc);
803 }
804
805 /* Lock attributes are no longer needed */
806 if (lock_attr_initialized)
807 pthread_mutexattr_destroy(&lock_attr);
808
809 /* Create the underlying thread */
810 if (result == VCOS_SUCCESS)
811 {
812 int rc = pthread_create(&timer->thread, NULL, _timer_thread, timer);
813 if (rc != 0)
814 result = vcos_pthreads_map_error(rc);
815 }
816
817 /* Clean up if anything went wrong */
818 if (result != VCOS_SUCCESS)
819 {
820 if (lock_initialized)
821 pthread_mutex_destroy(&timer->lock);
822
823 if (settings_changed_initialized)
824 pthread_cond_destroy(&timer->settings_changed);
825 }
826
827 return result;
828}
829
830void vcos_pthreads_timer_set(VCOS_TIMER_T *timer, VCOS_UNSIGNED delay_ms)
831{
832 struct timespec now;
833
834 vcos_assert(timer);
835
836 /* Other implementations of this function do undefined things
837 * when delay_ms is 0. This implementation will simply assert and return
838 */
839 vcos_assert(delay_ms != 0);
840 if (delay_ms == 0)
841 return;
842
843 pthread_mutex_lock(&timer->lock);
844
845 /* Calculate the new absolute expiry time */
846 clock_gettime(CLOCK_REALTIME, &now);
847 timer->expires.tv_sec = delay_ms / MSEC_IN_SEC;
848 timer->expires.tv_nsec = (delay_ms % MSEC_IN_SEC) * NSEC_IN_MSEC;
849 _timespec_add(&timer->expires, &now);
850
851 /* Notify the timer's thread about the change */
852 pthread_cond_signal(&timer->settings_changed);
853
854 pthread_mutex_unlock(&timer->lock);
855}
856
857void vcos_pthreads_timer_cancel(VCOS_TIMER_T *timer)
858{
859 vcos_assert(timer);
860
861 pthread_mutex_lock(&timer->lock);
862
863 _timespec_set_zero(&timer->expires);
864 pthread_cond_signal(&timer->settings_changed);
865
866 pthread_mutex_unlock(&timer->lock);
867}
868
869void vcos_pthreads_timer_delete(VCOS_TIMER_T *timer)
870{
871 vcos_assert(timer);
872
873 pthread_mutex_lock(&timer->lock);
874
875 /* Other implementation of this function (e.g. ThreadX)
876 * disallow it being called from the expiration routine
877 */
878 vcos_assert(pthread_self() != timer->thread);
879
880 /* Stop the timer and set flag telling the timer thread to quit */
881 _timespec_set_zero(&timer->expires);
882 timer->quit = 1;
883
884 /* Notify the timer's thread about the change */
885 pthread_cond_signal(&timer->settings_changed);
886
887 /* Release the lock, so that the timer's thread can quit */
888 pthread_mutex_unlock(&timer->lock);
889
890 /* Wait for the timer thread to finish */
891 pthread_join(timer->thread, NULL);
892
893 /* Free resources used by the timer */
894 pthread_mutex_destroy(&timer->lock);
895 pthread_cond_destroy(&timer->settings_changed);
896}
897
898