• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright (c) 2009, 2022 IBM Corp. and Ian Craggs
3  *
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v2.0
6  * and Eclipse Distribution License v1.0 which accompany this distribution.
7  *
8  * The Eclipse Public License is available at
9  *    https://www.eclipse.org/legal/epl-2.0/
10  * and the Eclipse Distribution License is available at
11  *   http://www.eclipse.org/org/documents/edl-v10.php.
12  *
13  * Contributors:
14  *    Ian Craggs - initial implementation
15  *    Ian Craggs, Allan Stockdill-Mander - async client updates
16  *    Ian Craggs - bug #415042 - start Linux thread as disconnected
17  *    Ian Craggs - fix for bug #420851
18  *    Ian Craggs - change MacOS semaphore implementation
19  *    Ian Craggs - fix for clock #284
20  *******************************************************************************/
21 
22 /**
23  * @file
24  * \brief Threading related functions
25  *
26  * Used to create platform independent threading functions
27  */
28 
29 
30 #include "Thread.h"
31 #if defined(THREAD_UNIT_TESTS)
32 #define NOSTACKTRACE
33 #endif
34 #include "Log.h"
35 #include "StackTrace.h"
36 
37 #undef malloc
38 #undef realloc
39 #undef free
40 
41 #if !defined(_WIN32) && !defined(_WIN64) && !defined (COMPAT_CMSIS)
42 #include <errno.h>
43 #include <unistd.h>
44 #include <sys/time.h>
45 #include <fcntl.h>
46 #include <stdio.h>
47 #include <sys/stat.h>
48 #include <limits.h>
49 #endif
50 #include <stdlib.h>
51 #include "OsWrapper.h"
52 #if defined(IOT_CONNECT)
53 #include "atiny_mqtt_commu.h"
54 #endif
55 #if defined (IOT_LITEOS_ADAPT)
56 #include "los_task.h"
57 #define MQTT_TASK_PRIO  10
58 #define MQTT_TASK_STACK_SIZE  0x800
59 #endif
60 /**
61  * Start a new thread
62  * @param fn the function to run, must be of the correct signature
63  * @param parameter pointer to the function parameter, can be NULL
64  */
Thread_start(thread_fn fn,void * parameter)65 void Thread_start(thread_fn fn, void* parameter)
66 {
67 #if defined(_WIN32) || defined(_WIN64)
68 	thread_type thread = NULL;
69 #elif defined (IOT_CONNECT)
70 	new_mqtt_run_job(fn, parameter);
71 #elif defined (IOT_LITEOS_ADAPT)
72     thread_type thread = 0;
73 #else
74 	thread_type thread = 0;
75 	pthread_attr_t attr;
76 #endif
77 
78 	FUNC_ENTRY;
79 #if defined(_WIN32) || defined(_WIN64)
80 	thread = CreateThread(NULL, 0, fn, parameter, 0, NULL);
81     CloseHandle(thread);
82 #elif defined (IOT_CONNECT)
83 
84 #elif defined (IOT_LITEOS_ADAPT)
85     TSK_INIT_PARAM_S app_task;
86     /* create task for send_start_report */
87     app_task.pfnTaskEntry = (TSK_ENTRY_FUNC)fn;
88     app_task.uwStackSize  = MQTT_TASK_STACK_SIZE;
89     app_task.pcName = "mqtt";
90     app_task.usTaskPrio = MQTT_TASK_PRIO;
91     app_task.uwResved   = LOS_TASK_STATUS_DETACHED;
92 
93     if (LOS_TaskCreate((UINT32 *)&thread, &app_task) != 0)
94         thread = 0;
95 #else
96 	pthread_attr_init(&attr);
97 	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
98 	if (pthread_create(&thread, &attr, fn, parameter) != 0)
99 		thread = 0;
100 	pthread_attr_destroy(&attr);
101 #endif
102 	FUNC_EXIT;
103 }
104 
105 
Thread_set_name(const char * thread_name)106 int Thread_set_name(const char* thread_name)
107 {
108 	int rc = 0;
109 #if defined(_WIN32) || defined(_WIN64)
110 #define MAX_THREAD_NAME_LENGTH 30
111 	wchar_t wchars[MAX_THREAD_NAME_LENGTH];
112 #endif
113 	FUNC_ENTRY;
114 
115 #if defined(_WIN32) || defined(_WIN64)
116 /* Using NTDDI_VERSION rather than WINVER for more detailed version targeting */
117 /* Can't get this conditional compilation to work so remove it for now */
118 /*#if NTDDI_VERSION >= NTDDI_WIN10_RS1
119 	mbstowcs(wchars, thread_name, MAX_THREAD_NAME_LENGTH);
120 	rc = (int)SetThreadDescription(GetCurrentThread(), wchars);
121 #endif*/
122 #elif defined(OSX)
123 	/* pthread_setname_np __API_AVAILABLE(macos(10.6), ios(3.2)) */
124 #if defined(__APPLE__) && __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
125 	rc = pthread_setname_np(thread_name);
126 #endif
127 #else
128 #if defined(__GNUC__) && defined(__linux__)
129 #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 12
130 	rc = pthread_setname_np(Thread_getid(), thread_name);
131 #endif
132 #endif
133 #endif
134 	FUNC_EXIT_RC(rc);
135 	return rc;
136 }
137 
138 
139 /**
140  * Create a new mutex
141  * @param rc return code: 0 for success, negative otherwise
142  * @return the new mutex
143  */
Thread_create_mutex(int * rc)144 mutex_type Thread_create_mutex(int* rc)
145 {
146 	mutex_type mutex = NULL;
147 
148 	FUNC_ENTRY;
149 	*rc = -1;
150 	#if defined(_WIN32) || defined(_WIN64)
151 		mutex = CreateMutex(NULL, 0, NULL);
152 		*rc = (mutex == NULL) ? GetLastError() : 0;
153 	#elif defined (COMPAT_CMSIS)
154 		mutex = osMutexNew(NULL);
155 		*rc = (mutex == NULL) ? -1 : 0;
156 	#else
157 		mutex = malloc(sizeof(pthread_mutex_t));
158 		if (mutex)
159 			*rc = pthread_mutex_init(mutex, NULL);
160 	#endif
161 	FUNC_EXIT_RC(*rc);
162 	return mutex;
163 }
164 
165 
166 /**
167  * Lock a mutex which has alrea
168  * @return completion code, 0 is success
169  */
Thread_lock_mutex(mutex_type mutex)170 int Thread_lock_mutex(mutex_type mutex)
171 {
172 	int rc = -1;
173 
174 	/* don't add entry/exit trace points as the stack log uses mutexes - recursion beckons */
175 	#if defined(_WIN32) || defined(_WIN64)
176 		/* WaitForSingleObject returns WAIT_OBJECT_0 (0), on success */
177 		rc = WaitForSingleObject(mutex, INFINITE);
178 	#elif defined (COMPAT_CMSIS)
179 		rc = osMutexAcquire(mutex, osWaitForever);
180 	#else
181 		rc = pthread_mutex_lock(mutex);
182 	#endif
183 
184 	return rc;
185 }
186 
187 
188 /**
189  * Unlock a mutex which has already been locked
190  * @param mutex the mutex
191  * @return completion code, 0 is success
192  */
Thread_unlock_mutex(mutex_type mutex)193 int Thread_unlock_mutex(mutex_type mutex)
194 {
195 	int rc = -1;
196 
197 	/* don't add entry/exit trace points as the stack log uses mutexes - recursion beckons */
198 	#if defined(_WIN32) || defined(_WIN64)
199 		/* if ReleaseMutex fails, the return value is 0 */
200 		if (ReleaseMutex(mutex) == 0)
201 			rc = GetLastError();
202 		else
203 			rc = 0;
204 	#elif defined (COMPAT_CMSIS)
205 		rc = osMutexRelease(mutex);
206 	#else
207 		rc = pthread_mutex_unlock(mutex);
208 	#endif
209 
210 	return rc;
211 }
212 
213 
214 /**
215  * Destroy a mutex which has already been created
216  * @param mutex the mutex
217  */
Thread_destroy_mutex(mutex_type mutex)218 int Thread_destroy_mutex(mutex_type mutex)
219 {
220 #if defined(IOT_CONNECT)
221 	if (mutex == NULL)
222 		return -1;
223 #endif
224 	int rc = 0;
225 
226 	FUNC_ENTRY;
227 	#if defined(_WIN32) || defined(_WIN64)
228 		rc = CloseHandle(mutex);
229 	#elif defined (COMPAT_CMSIS)
230 		rc = osMutexDelete(mutex);
231 	#else
232 		rc = pthread_mutex_destroy(mutex);
233 		free(mutex);
234 	#endif
235 	FUNC_EXIT_RC(rc);
236 	return rc;
237 }
238 
239 
240 /**
241  * Get the thread id of the thread from which this function is called
242  * @return thread id, type varying according to OS
243  */
Thread_getid(void)244 thread_id_type Thread_getid(void)
245 {
246 	#if defined(_WIN32) || defined(_WIN64)
247 		return GetCurrentThreadId();
248 	#elif defined (COMPAT_CMSIS)
249 		return osThreadGetId();
250 	#else
251 		return pthread_self();
252 	#endif
253 }
254 
255 
256 /**
257  * Create a new semaphore
258  * @param rc return code: 0 for success, negative otherwise
259  * @return the new condition variable
260  */
Thread_create_sem(int * rc)261 sem_type Thread_create_sem(int *rc)
262 {
263 	sem_type sem = NULL;
264 
265 	FUNC_ENTRY;
266 	*rc = -1;
267 	#if defined(_WIN32) || defined(_WIN64)
268 		sem = CreateEvent(
269 		        NULL,               /* default security attributes */
270 		        FALSE,              /* manual-reset event? */
271 		        FALSE,              /* initial state is nonsignaled */
272 		        NULL                /* object name */
273 		        );
274 		*rc = (sem == NULL) ? GetLastError() : 0;
275 	#elif defined(OSX)
276 		sem = dispatch_semaphore_create(0L);
277 		*rc = (sem == NULL) ? -1 : 0;
278 	#elif defined (COMPAT_CMSIS)
279 		sem = osSemaphoreNew(1, 0, NULL);
280 		*rc = (sem == NULL) ? -1 : 0;
281 	#else
282 		sem = malloc(sizeof(sem_t));
283 		if (sem)
284 			*rc = sem_init(sem, 0, 0);
285 	#endif
286 	FUNC_EXIT_RC(*rc);
287 	return sem;
288 }
289 
290 
291 /**
292  * Wait for a semaphore to be posted, or timeout.
293  * @param sem the semaphore
294  * @param timeout the maximum time to wait, in milliseconds
295  * @return completion code
296  */
Thread_wait_sem(sem_type sem,int timeout)297 int Thread_wait_sem(sem_type sem, int timeout)
298 {
299 /* sem_timedwait is the obvious call to use, but seemed not to work on the Viper,
300  * so I've used trywait in a loop instead. Ian Craggs 23/7/2010
301  */
302 	int rc = -1;
303 #if !defined(_WIN32) && !defined(_WIN64) && !defined(OSX) && !defined(COMPAT_CMSIS)
304 #define USE_TRYWAIT
305 #if defined(USE_TRYWAIT)
306 	int i = 0;
307 	useconds_t interval = 10000; /* 10000 microseconds: 10 milliseconds */
308 	int count = (1000 * timeout) / interval; /* how many intervals in timeout period */
309 #else
310 	struct timespec ts;
311 #endif
312 #endif
313 
314 	FUNC_ENTRY;
315 	#if defined(_WIN32) || defined(_WIN64)
316 		/* returns 0 (WAIT_OBJECT_0) on success, non-zero (WAIT_TIMEOUT) if timeout occurred */
317 		rc = WaitForSingleObject(sem, timeout < 0 ? 0 : timeout);
318 		if (rc == WAIT_TIMEOUT)
319 			rc = ETIMEDOUT;
320 	#elif defined(OSX)
321 		/* returns 0 on success, non-zero if timeout occurred */
322 		rc = (int)dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, (int64_t)timeout*1000000L));
323 		if (rc != 0)
324 			rc = ETIMEDOUT;
325 	#elif defined(USE_TRYWAIT)
326 		while (++i < count && (rc = sem_trywait(sem)) != 0)
327 		{
328 			if (rc == -1 && ((rc = errno) != EAGAIN))
329 			{
330 				rc = 0;
331 				break;
332 			}
333 			usleep(interval); /* microseconds - .1 of a second */
334 		}
335 	#elif defined (COMPAT_CMSIS)
336 		rc = osSemaphoreAcquire(sem, timeout);
337 	#else
338 		/* We have to use CLOCK_REALTIME rather than MONOTONIC for sem_timedwait interval.
339 		 * Does this make it susceptible to system clock changes?
340 		 * The intervals are small enough, and repeated, that I think it's not an issue.
341 		 */
342 		if (clock_gettime(CLOCK_REALTIME, &ts) != -1)
343 		{
344 			ts.tv_sec += timeout;
345 			rc = sem_timedwait(sem, &ts);
346 		}
347 	#endif
348 
349  	FUNC_EXIT_RC(rc);
350  	return rc;
351 }
352 
353 
354 /**
355  * Check to see if a semaphore has been posted, without waiting
356  * The semaphore will be unchanged, if the return value is false.
357  * The semaphore will have been decremented, if the return value is true.
358  * @param sem the semaphore
359  * @return 0 (false) or 1 (true)
360  */
Thread_check_sem(sem_type sem)361 int Thread_check_sem(sem_type sem)
362 {
363 #if defined(_WIN32) || defined(_WIN64)
364 	/* if the return value is not 0, the semaphore will not have been decremented */
365 	return WaitForSingleObject(sem, 0) == WAIT_OBJECT_0;
366 #elif defined(OSX)
367 	/* if the return value is not 0, the semaphore will not have been decremented */
368 	return dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW) == 0;
369 #else
370 	/* If the call was unsuccessful, the state of the semaphore shall be unchanged,
371 	 * and the function shall return a value of -1 */
372 	return sem_trywait(sem) == 0;
373 #endif
374 }
375 
376 
377 /**
378  * Post a semaphore
379  * @param sem the semaphore
380  * @return 0 on success
381  */
Thread_post_sem(sem_type sem)382 int Thread_post_sem(sem_type sem)
383 {
384 	int rc = 0;
385 
386 	FUNC_ENTRY;
387 	#if defined(_WIN32) || defined(_WIN64)
388 		if (SetEvent(sem) == 0)
389 			rc = GetLastError();
390 	#elif defined(OSX)
391 		rc = (int)dispatch_semaphore_signal(sem);
392 	#elif defined (COMPAT_CMSIS)
393 		osSemaphoreRelease(sem);
394 	#else
395 		int val;
396 		int rc1 = sem_getvalue(sem, &val);
397 		if (rc1 != 0)
398 			rc = errno;
399 		else if (val == 0 && sem_post(sem) == -1)
400 			rc = errno;
401 	#endif
402 
403  	FUNC_EXIT_RC(rc);
404  	return rc;
405 }
406 
407 
408 /**
409  * Destroy a semaphore which has already been created
410  * @param sem the semaphore
411  */
Thread_destroy_sem(sem_type sem)412 int Thread_destroy_sem(sem_type sem)
413 {
414 	int rc = 0;
415 
416 	FUNC_ENTRY;
417 	#if defined(_WIN32) || defined(_WIN64)
418 		rc = CloseHandle(sem);
419 	#elif defined(OSX)
420 	  dispatch_release(sem);
421 	#elif defined (COMPAT_CMSIS)
422 		rc = osSemaphoreDelete(sem);
423 	#else
424 		rc = sem_destroy(sem);
425 		free(sem);
426 	#endif
427 	FUNC_EXIT_RC(rc);
428 	return rc;
429 }
430 
431 
432 #if !defined(_WIN32) && !defined(_WIN64)
433 
434 /**
435  * Create a new condition variable
436  * @return the condition variable struct
437  */
438 #if !defined (COMPAT_CMSIS)
Thread_create_cond(int * rc)439 cond_type Thread_create_cond(int *rc)
440 {
441 	cond_type condvar = NULL;
442 	pthread_condattr_t attr;
443 
444 	FUNC_ENTRY;
445 	*rc = -1;
446 	pthread_condattr_init(&attr);
447 
448 #if 0
449     /* in theory, a monotonic clock should be able to be used.  However on at least
450      * one system reported, even though setclock() reported success, it didn't work.
451      */
452 	if ((rc = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) == 0)
453 		use_clock_monotonic = 1;
454 	else
455 		Log(LOG_ERROR, -1, "Error %d calling pthread_condattr_setclock(CLOCK_MONOTONIC)", rc);
456 #endif
457 
458 	condvar = malloc(sizeof(cond_type_struct));
459 	if (condvar)
460 	{
461 		*rc = pthread_cond_init(&condvar->cond, &attr);
462 		*rc = pthread_mutex_init(&condvar->mutex, NULL);
463 	}
464 
465 	FUNC_EXIT_RC(*rc);
466 	return condvar;
467 }
468 #endif
469 
470 /**
471  * Signal a condition variable
472  * @return completion code
473  */
474 #if !defined (COMPAT_CMSIS)
Thread_signal_cond(cond_type condvar)475 int Thread_signal_cond(cond_type condvar)
476 {
477 	int rc = 0;
478 
479 	FUNC_ENTRY;
480 	pthread_mutex_lock(&condvar->mutex);
481 	rc = pthread_cond_signal(&condvar->cond);
482 	pthread_mutex_unlock(&condvar->mutex);
483 
484 	FUNC_EXIT_RC(rc);
485 	return rc;
486 }
487 #endif
488 
489 /**
490  * Wait with a timeout (ms) for condition variable
491  * @return 0 for success, ETIMEDOUT otherwise
492  */
493 #if !defined (COMPAT_CMSIS)
Thread_wait_cond(cond_type condvar,int timeout_ms)494 int Thread_wait_cond(cond_type condvar, int timeout_ms)
495 {
496 	int rc = 0;
497 	struct timespec cond_timeout;
498 	struct timespec interval;
499 
500 	FUNC_ENTRY;
501 	interval.tv_sec = timeout_ms / 1000;
502 	interval.tv_nsec = (timeout_ms % 1000) * 1000000L;
503 
504 #if defined(__APPLE__) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200 /* for older versions of MacOS */
505 	struct timeval cur_time;
506     gettimeofday(&cur_time, NULL);
507     cond_timeout.tv_sec = cur_time.tv_sec;
508     cond_timeout.tv_nsec = cur_time.tv_usec * 1000;
509 #else
510 	clock_gettime(CLOCK_REALTIME, &cond_timeout);
511 #endif
512 
513 	cond_timeout.tv_sec += interval.tv_sec;
514 	cond_timeout.tv_nsec += (timeout_ms % 1000) * 1000000L;
515 
516 	if (cond_timeout.tv_nsec >= 1000000000L)
517 	{
518 		cond_timeout.tv_sec++;
519 		cond_timeout.tv_nsec += (cond_timeout.tv_nsec - 1000000000L);
520 	}
521 
522 	pthread_mutex_lock(&condvar->mutex);
523 	rc = pthread_cond_timedwait(&condvar->cond, &condvar->mutex, &cond_timeout);
524 	pthread_mutex_unlock(&condvar->mutex);
525 
526 	FUNC_EXIT_RC(rc);
527 	return rc;
528 }
529 #endif
530 
531 /**
532  * Destroy a condition variable
533  * @return completion code
534  */
535 #if !defined (COMPAT_CMSIS)
Thread_destroy_cond(cond_type condvar)536 int Thread_destroy_cond(cond_type condvar)
537 {
538 	int rc = 0;
539 
540 	rc = pthread_mutex_destroy(&condvar->mutex);
541 	rc = pthread_cond_destroy(&condvar->cond);
542 	free(condvar);
543 
544 	return rc;
545 }
546 #endif
547 #endif
548 
549 
550 #if defined(THREAD_UNIT_TESTS)
551 
552 #if defined(_WIN32) || defined(_WINDOWS)
553 #define mqsleep(A) Sleep(1000*A)
554 #define START_TIME_TYPE DWORD
555 static DWORD start_time = 0;
start_clock(void)556 START_TIME_TYPE start_clock(void)
557 {
558 	return GetTickCount();
559 }
560 #elif defined(AIX)
561 #define mqsleep sleep
562 #define START_TIME_TYPE struct timespec
start_clock(void)563 START_TIME_TYPE start_clock(void)
564 {
565 	static struct timespec start;
566 	clock_gettime(CLOCK_REALTIME, &start);
567 	return start;
568 }
569 #else
570 #define mqsleep sleep
571 #define START_TIME_TYPE struct timeval
572 /* TODO - unused - remove? static struct timeval start_time; */
start_clock(void)573 START_TIME_TYPE start_clock(void)
574 {
575 	struct timeval start_time;
576 	gettimeofday(&start_time, NULL);
577 	return start_time;
578 }
579 #endif
580 
581 
582 #if defined(_WIN32)
elapsed(START_TIME_TYPE start_time)583 long elapsed(START_TIME_TYPE start_time)
584 {
585 	return GetTickCount() - start_time;
586 }
587 #elif defined(AIX)
588 #define assert(a)
elapsed(struct timespec start)589 long elapsed(struct timespec start)
590 {
591 	struct timespec now, res;
592 
593 	clock_gettime(CLOCK_REALTIME, &now);
594 	ntimersub(now, start, res);
595 	return (res.tv_sec)*1000L + (res.tv_nsec)/1000000L;
596 }
597 #else
elapsed(START_TIME_TYPE start_time)598 long elapsed(START_TIME_TYPE start_time)
599 {
600 	struct timeval now, res;
601 
602 	gettimeofday(&now, NULL);
603 	timersub(&now, &start_time, &res);
604 	return (res.tv_sec)*1000 + (res.tv_usec)/1000;
605 }
606 #endif
607 
608 
609 int tests = 0, failures = 0;
610 
myassert(char * filename,int lineno,char * description,int value,char * format,...)611 void myassert(char* filename, int lineno, char* description, int value, char* format, ...)
612 {
613 	++tests;
614 	if (!value)
615 	{
616 		va_list args;
617 
618 		++failures;
619 		printf("Assertion failed, file %s, line %d, description: %s\n", filename, lineno, description);
620 
621 		va_start(args, format);
622 		vprintf(format, args);
623 		va_end(args);
624 
625 		//cur_output += sprintf(cur_output, "<failure type=\"%s\">file %s, line %d </failure>\n",
626         //                description, filename, lineno);
627 	}
628     else
629     		printf("Assertion succeeded, file %s, line %d, description: %s\n", filename, lineno, description);
630 }
631 
632 #define assert(a, b, c, d) myassert(__FILE__, __LINE__, a, b, c, d)
633 #define assert1(a, b, c, d, e) myassert(__FILE__, __LINE__, a, b, c, d, e)
634 
635 #include <stdio.h>
636 
cond_secondary(void * n)637 thread_return_type cond_secondary(void* n)
638 {
639 	int rc = 0;
640 	cond_type cond = n;
641 
642 	printf("This should return immediately as it was posted already\n");
643 	rc = Thread_wait_cond(cond, 99999);
644 	assert("rc 1 from wait_cond", rc == 1, "rc was %d", rc);
645 
646 	printf("This should hang around a few seconds\n");
647 	rc = Thread_wait_cond(cond, 99999);
648 	assert("rc 1 from wait_cond", rc == 1, "rc was %d", rc);
649 
650 	printf("Secondary cond thread ending\n");
651 	return 0;
652 }
653 
654 
cond_test()655 int cond_test()
656 {
657 	int rc = 0;
658 	cond_type cond = Thread_create_cond();
659 
660 	printf("Post secondary so it should return immediately\n");
661 	rc = Thread_signal_cond(cond);
662 	assert("rc 0 from signal cond", rc == 0, "rc was %d", rc);
663 
664 	printf("Starting secondary thread\n");
665 	Thread_start(cond_secondary, (void*)cond);
666 
667 	sleep(3);
668 
669 	printf("post secondary\n");
670 	rc = Thread_signal_cond(cond);
671 	assert("rc 1 from signal cond", rc == 1, "rc was %d", rc);
672 
673 	sleep(3);
674 
675 	printf("Main thread ending\n");
676 
677 	return failures;
678 }
679 
680 
sem_secondary(void * n)681 thread_return_type sem_secondary(void* n)
682 {
683 	int rc = 0;
684 	sem_type sem = n;
685 
686 	printf("Secondary semaphore pointer %p\n", sem);
687 
688 	rc = Thread_check_sem(sem);
689 	assert("rc 1 from check_sem", rc == 1, "rc was %d", rc);
690 
691 	printf("Secondary thread about to wait\n");
692 	rc = Thread_wait_sem(sem, 99999);
693 	printf("Secondary thread returned from wait %d\n", rc);
694 
695 	printf("Secondary thread about to wait\n");
696 	rc = Thread_wait_sem(sem, 99999);
697 	printf("Secondary thread returned from wait %d\n", rc);
698 	printf("Secondary check sem %d\n", Thread_check_sem(sem));
699 
700 	printf("Secondary thread ending\n");
701 	return 0;
702 }
703 
704 
sem_test()705 int sem_test()
706 {
707 	int rc = 0;
708 	sem_type sem = Thread_create_sem();
709 
710 	printf("Primary semaphore pointer %p\n", sem);
711 
712 	rc = Thread_check_sem(sem);
713 	assert("rc 0 from check_sem", rc == 0, "rc was %d\n", rc);
714 
715 	printf("post secondary so then check should be 1\n");
716 	rc = Thread_post_sem(sem);
717 	assert("rc 0 from post_sem", rc == 0, "rc was %d\n", rc);
718 
719 	rc = Thread_check_sem(sem);
720 	assert("rc 1 from check_sem", rc == 1, "rc was %d", rc);
721 
722 	printf("Starting secondary thread\n");
723 	Thread_start(sem_secondary, (void*)sem);
724 
725 	sleep(3);
726 	rc = Thread_check_sem(sem);
727 	assert("rc 1 from check_sem", rc == 1, "rc was %d", rc);
728 
729 	printf("post secondary\n");
730 	rc = Thread_post_sem(sem);
731 	assert("rc 1 from post_sem", rc == 1, "rc was %d", rc);
732 
733 	sleep(3);
734 
735 	printf("Main thread ending\n");
736 
737 	return failures;
738 }
739 
740 
main(int argc,char * argv[])741 int main(int argc, char *argv[])
742 {
743 	sem_test();
744 	//cond_test();
745 }
746 
747 #endif
748