version 2.8
gos_linux.c
1 /*
2  * This file is subject to the terms of the GFX License. If a copy of
3  * the license was not distributed with this file, you can obtain one at:
4  *
5  * http://ugfx.org/license.html
6  */
7 
8 // We need to include stdio.h below. Turn off GFILE_NEED_STDIO just for this file to prevent conflicts
9 #define GFILE_NEED_STDIO_MUST_BE_OFF
10 
11 #include "../../gfx.h"
12 
13 #if GFX_USE_OS_LINUX
14 
15 // Linux seems to have deprecated pthread_yield() and now says to use sched_yield()
16 #define USE_SCHED_NOT_PTHREAD_YIELD TRUE
17 
18 #include <stdio.h>
19 #include <unistd.h>
20 #include <time.h>
21 #if USE_SCHED_NOT_PTHREAD_YIELD
22  #include <sched.h>
23  #define linuxyield() sched_yield()
24 #else
25  #define linuxyield() pthread_yield()
26 #endif
27 
28 static gfxMutex SystemMutex;
29 
30 void _gosInit(void)
31 {
32  /* No initialization of the operating system itself is needed */
33  gfxMutexInit(&SystemMutex);
34 }
35 
36 void _gosPostInit(void)
37 {
38 }
39 
40 void _gosDeinit(void)
41 {
42  /* ToDo */
43 }
44 
45 void gfxSystemLock(void) {
46  gfxMutexEnter(&SystemMutex);
47 }
48 
49 void gfxSystemUnlock(void) {
50  gfxMutexExit(&SystemMutex);
51 }
52 
53 void gfxYield(void) {
54  linuxyield();
55 }
56 
57 void gfxHalt(const char *msg) {
58  if (msg)
59  fprintf(stderr, "%s\n", msg);
60  exit(1);
61 }
62 
63 void gfxSleepMilliseconds(delaytime_t ms) {
64  struct timespec ts;
65 
66  switch(ms) {
67  case TIME_IMMEDIATE:
68  linuxyield();
69  return;
70 
71  case TIME_INFINITE:
72  while(1)
73  sleep(60);
74  return;
75 
76  default:
77  ts.tv_sec = ms / 1000;
78  ts.tv_nsec = (ms % 1000) * 1000000;
79  nanosleep(&ts, 0);
80  return;
81  }
82 }
83 
84 void gfxSleepMicroseconds(delaytime_t us) {
85  struct timespec ts;
86 
87  switch(us) {
88  case TIME_IMMEDIATE:
89  linuxyield();
90  return;
91 
92  case TIME_INFINITE:
93  while(1)
94  sleep(60);
95  return;
96 
97  default:
98  ts.tv_sec = us / 1000000;
99  ts.tv_nsec = (us % 1000000) * 1000;
100  nanosleep(&ts, 0);
101  return;
102  }
103 }
104 
105 systemticks_t gfxSystemTicks(void) {
106  struct timespec ts;
107 
108  clock_gettime(CLOCK_MONOTONIC, &ts);
109  return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
110 }
111 
112 gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param) {
113  gfxThreadHandle th;
114  (void) stackarea;
115  (void) stacksz;
116  (void) prio;
117 
118  // Implementing priority with pthreads is a rats nest that is also pthreads implementation dependent.
119  // Only some pthreads schedulers support it, some implementations use the operating system process priority mechanisms.
120  // Even those that do support it can have different ranges of priority and "normal" priority is an undefined concept.
121  // Across different UNIX style operating systems things can be very different (let alone OS's such as Windows).
122  // Even just Linux changes the way priority works with different kernel schedulers and across kernel versions.
123  // For these reasons we ignore the priority.
124 
125  if (pthread_create(&th, 0, fn, param))
126  return 0;
127 
128  return th;
129 }
130 
131 threadreturn_t gfxThreadWait(gfxThreadHandle thread) {
132  threadreturn_t retval;
133 
134  if (pthread_join(thread, &retval))
135  return 0;
136 
137  return retval;
138 }
139 
140 #if GFX_USE_POSIX_SEMAPHORES
141  void gfxSemInit(gfxSem *pSem, semcount_t val, semcount_t limit) {
142  pSem->max = limit;
143  sem_init(&pSem->sem, 0, val);
144  }
145  void gfxSemDestroy(gfxSem *pSem) {
146  sem_destroy(&pSem->sem);
147  }
148  bool_t gfxSemWait(gfxSem *pSem, delaytime_t ms) {
149  switch (ms) {
150  case TIME_INFINITE:
151  return sem_wait(&pSem->sem) ? FALSE : TRUE;
152 
153  case TIME_IMMEDIATE:
154  return sem_trywait(&pSem->sem) ? FALSE : TRUE;
155 
156  default:
157  {
158  struct timespec tm;
159 
160  clock_gettime(CLOCK_REALTIME, &tm);
161  tm.tv_sec += ms / 1000;
162  tm.tv_nsec += (ms % 1000) * 1000000;
163  return sem_timedwait(&pSem->sem, &tm) ? FALSE : TRUE;
164  }
165  }
166  }
167  void gfxSemSignal(gfxSem *pSem) {
168  int res;
169 
170  res = 0;
171  sem_getvalue(&pSem->sem, &res);
172  if (res < pSem->max)
173  sem_post(&pSem->sem);
174  }
175 #else
176  void gfxSemInit(gfxSem *pSem, semcount_t val, semcount_t limit) {
177  pthread_mutex_init(&pSem->mtx, 0);
178  pthread_cond_init(&pSem->cond, 0);
179  pthread_mutex_lock(&pSem->mtx);
180  pSem->cnt = val;
181  pSem->max = limit;
182  pthread_mutex_unlock(&pSem->mtx);
183  }
184  void gfxSemDestroy(gfxSem *pSem) {
185  pthread_mutex_destroy(&pSem->mtx);
186  pthread_cond_destroy(&pSem->cond);
187  }
188  bool_t gfxSemWait(gfxSem *pSem, delaytime_t ms) {
189  pthread_mutex_lock(&pSem->mtx);
190 
191  switch (ms) {
192  case TIME_INFINITE:
193  while (!pSem->cnt)
194  pthread_cond_wait(&pSem->cond, &pSem->mtx);
195  break;
196 
197  case TIME_IMMEDIATE:
198  if (!pSem->cnt) {
199  pthread_mutex_unlock(&pSem->mtx);
200  return FALSE;
201  }
202  break;
203 
204  default:
205  {
206  struct timespec tm;
207 
208  clock_gettime(CLOCK_REALTIME, &tm);
209  tm.tv_sec += ms / 1000;
210  tm.tv_nsec += (ms % 1000) * 1000000;
211  while (!pSem->cnt) {
212  // We used to test the return value for ETIMEDOUT. This doesn't
213  // work in some current pthread libraries which return -1 instead
214  // and set errno to ETIMEDOUT. So, we will return FALSE on any error
215  // including a ETIMEDOUT.
216  if (pthread_cond_timedwait(&pSem->cond, &pSem->mtx, &tm)) {
217  pthread_mutex_unlock(&pSem->mtx);
218  return FALSE;
219  }
220  }
221  }
222  break;
223  }
224 
225  pSem->cnt--;
226  pthread_mutex_unlock(&pSem->mtx);
227  return TRUE;
228  }
229  void gfxSemSignal(gfxSem *pSem) {
230  pthread_mutex_lock(&pSem->mtx);
231 
232  if (pSem->cnt < pSem->max) {
233  pSem->cnt++;
234  pthread_cond_signal(&pSem->cond);
235  }
236 
237  pthread_mutex_unlock(&pSem->mtx);
238  }
239 #endif // GFX_USE_POSIX_SEMAPHORES
240 
241 #endif /* GFX_USE_OS_LINUX */
void * gfxThreadHandle
A thread handle.
Definition: gos.h:117
void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit)
Initialise a Counted Semaphore.
systemticks_t gfxSystemTicks(void)
Get the current operating system tick time.
#define FALSE
Generic &#39;false&#39; boolean constant.
Definition: gfx.h:31
void gfxSemDestroy(gfxSem *psem)
Destroy a Counted Semaphore.
bool_t gfxSemWait(gfxSem *psem, delaytime_t ms)
Wait on a semaphore.
A semaphore.
Definition: gos.h:105
void gfxMutexExit(gfxMutex *pmutex)
Exit the critical code region protected by the mutex.
void gfxSystemLock(void)
Lock the operating system to protect a sequence of code.
#define DECLARE_THREAD_FUNCTION(fnName, param)
Declare a thread function.
Definition: gos.h:62
void gfxHalt(const char *msg)
Halt the GFX application due to an error.
void gfxMutexEnter(gfxMutex *pmutex)
Enter the critical code region protected by the mutex.
void gfxSemSignal(gfxSem *psem)
Signal a semaphore.
void gfxSleepMicroseconds(delaytime_t us)
Put the current thread to sleep for the specified period in microseconds.
gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn), p), void *param)
Start a new thread.
A mutex.
Definition: gos.h:111
threadreturn_t gfxThreadWait(gfxThreadHandle thread)
Wait for a thread to finish.
void gfxMutexInit(gfxMutex *pmutex)
Initialise a mutex to protect a region of code from other threads.
void gfxYield(void)
Yield the current thread.
#define TRUE
Generic &#39;true&#39; boolean constant.
Definition: gfx.h:38
void gfxSleepMilliseconds(delaytime_t ms)
Put the current thread to sleep for the specified period in milliseconds.
void gfxSystemUnlock(void)
Unlock the operating system previous locked by gfxSystemLock()