1 /******************************************************************************/
2 /* Mednafen - Multi-system Emulator                                           */
3 /******************************************************************************/
4 /* MThreading_POSIX.cpp:
5 **  Copyright (C) 2018-2020 Mednafen Team
6 **
7 ** This program is free software; you can redistribute it and/or
8 ** modify it under the terms of the GNU General Public License
9 ** as published by the Free Software Foundation; either version 2
10 ** of the License, or (at your option) any later version.
11 **
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 ** GNU General Public License for more details.
16 **
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program; if not, write to the Free Software Foundation, Inc.,
19 ** 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 */
21 
22 /*
23  TODO: Make thread ID correct for threads created outside
24        of Mednafen's framework.
25 */
26 
27 #include <mednafen/mednafen.h>
28 #include <mednafen/MThreading.h>
29 //
30 //
31 //
32 #if !defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP)
33  #define MDFN_USE_COND_TIMEDWAIT_RELATIVE_NP
34 #endif
35 
36 #if defined(HAVE_SEM_CLOCKWAIT)
37  #define MDFN_USE_SEM_CLOCKWAIT
38 #elif defined(HAVE_SEM_CLOCKWAIT_NP)
39  #define MDFN_USE_SEM_CLOCKWAIT_NP
40 #elif !defined(HAVE_SEM_TIMEDWAIT)
41  #define MDFN_USE_CONDVAR_SEMAPHORES
42 #else
43 
44 #endif
45 //
46 //
47 //
48 #include <pthread.h>
49 
50 #if defined HAVE_SCHED_H
51  #include <sched.h>
52 #endif
53 
54 #if defined(HAVE_PTHREAD_NP_H)
55  #include <pthread_np.h>
56 #endif
57 
58 #if !defined(MDFN_USE_CONDVAR_SEMAPHORES)
59  #include <semaphore.h>
60 #endif
61 
62 #include <time.h>
63 
64 namespace Mednafen
65 {
66 namespace MThreading
67 {
68 static __thread uintptr_t LocalThreadID = 0;
69 
TimeSpec_AddNanoseconds(struct timespec * ts,uint64 add_ns)70 static INLINE void TimeSpec_AddNanoseconds(struct timespec* ts, uint64 add_ns)
71 {
72  uint64 ns = (uint64)ts->tv_nsec + add_ns;
73 
74  ts->tv_sec += ns / (1000 * 1000 * 1000);
75  ts->tv_nsec = ns % (1000 * 1000 * 1000);
76 }
77 
78 struct Thread
79 {
ThreadMednafen::MThreading::Thread80  Thread() { }
81 
82  pthread_t t;
83  int (*ep)(void*);
84  void* data;
85  int rv;
86  private:
87  Thread(const Thread&);
88 };
89 
90 struct Mutex
91 {
MutexMednafen::MThreading::Mutex92  Mutex() { }
93 
94  pthread_mutex_t m;
95  private:
96  Mutex(const Mutex&);
97 };
98 
99 struct Cond
100 {
CondMednafen::MThreading::Cond101  Cond() { }
102 
103  pthread_cond_t c;
104  private:
105  Cond(const Cond&);
106 };
107 
PTCEP(void * arg)108 static void* PTCEP(void* arg)
109 {
110  Thread* t = (Thread*)arg;
111 
112  LocalThreadID = (uintptr_t)t;
113 
114  //printf("%016llx\n", (unsigned long long)ThreadID);
115 
116  t->rv = t->ep(t->data);
117 
118  return &t->rv;
119 }
120 
121 // Dummy
122 template<typename T>
PTSNW(T fn,pthread_t t,const char * n)123 static int PTSNW(T fn, pthread_t t, const char* n)
124 {
125  return 0;
126 }
127 
128 // NetBSD
PTSNW(int (* fn)(pthread_t,const char *,void *),pthread_t t,const char * n)129 static MDFN_NOWARN_UNUSED int PTSNW(int (*fn)(pthread_t, const char*, void*), pthread_t t, const char* n)
130 {
131  return fn(t, "%s", (void*)n);
132 }
133 
134 // Linux
PTSNW(int (* fn)(pthread_t,const char *),pthread_t t,const char * n)135 static MDFN_NOWARN_UNUSED int PTSNW(int (*fn)(pthread_t, const char*), pthread_t t, const char* n)
136 {
137  return fn(t, n);
138 }
139 
140 // Mac OS X
PTSNW(int (* fn)(const char *),pthread_t t,const char * n)141 static MDFN_NOWARN_UNUSED int PTSNW(int (*fn)(const char*), pthread_t t, const char* n)
142 {
143  return fn(n);
144 }
145 
Thread_Create(int (* fn)(void *),void * data,const char * debug_name)146 Thread* Thread_Create(int (*fn)(void *), void *data, const char* debug_name)
147 {
148  std::unique_ptr<Thread> ret(new Thread());
149  int ptec;
150 
151  ret->ep = fn;
152  ret->data = data;
153 
154  if((ptec = pthread_create(&ret->t, nullptr, PTCEP, ret.get())))
155  {
156   ErrnoHolder ene(ptec);
157 
158   throw MDFN_Error(0, _("%s failed: %s"), "pthread_create()", ene.StrError());
159  }
160 
161  if(debug_name)
162  {
163 #if defined(HAVE_PTHREAD_SETNAME_NP) && !defined(pthread_setname_np)
164   char tmp[16];
165 
166   strncpy(tmp, debug_name, 16);
167   tmp[15] = 0;
168 
169   PTSNW(pthread_setname_np, ret->t, tmp);
170 #elif defined(HAVE_PTHREAD_SET_NAME_NP)
171   pthread_set_name_np(ret->t, debug_name);
172 #endif
173  }
174 
175  return ret.release();
176 }
177 
Thread_Wait(Thread * thread,int * status)178 void Thread_Wait(Thread* thread, int* status)
179 {
180  try
181  {
182   void* vp;
183   int ptec;
184 
185   if((ptec = pthread_join(thread->t, &vp)))
186   {
187    ErrnoHolder ene(ptec);
188 
189    throw MDFN_Error(ene.Errno(), _("%s failed: %s"), "pthread_join()", ene.StrError());
190   }
191 
192   if(vp != &thread->rv)
193   {
194    delete thread;
195    throw MDFN_Error(0, _("Thread being joined exited improperly."));
196   }
197 
198   if(status)
199    *status = thread->rv;
200 
201   delete thread;
202  }
203  catch(std::exception& e)
204  {
205   MDFND_OutputNotice(MDFN_NOTICE_ERROR, e.what());
206  }
207 }
208 
Thread_ID(void)209 uintptr_t Thread_ID(void)
210 {
211  //printf("%16llx -- %16llx -- %16llx\n", (unsigned long long)ThreadID, (unsigned long long)pthread_self(), (unsigned long long)SDL_ThreadID());
212  return LocalThreadID;
213 }
214 
215 #if defined(PTHREAD_AFFINITY_NP)
216 //template<typename T> static T MDFN_PTHREAD_NP_CPUSET_T_HELPER(int (*)(pthread_t, size_t, T*)) { T dummy; CPU_ZERO(&dummy); return dummy; }
217 //typedef decltype(MDFN_PTHREAD_NP_CPUSET_T_HELPER(pthread_getaffinity_np)) MDFN_PTHREAD_NP_CPUSET_T;
218 
Thread_SetAffinity(Thread * thread,const uint64 mask)219 uint64 Thread_SetAffinity(Thread* thread, const uint64 mask)
220 {
221  try
222  {
223   assert(mask != 0);
224   //
225   pthread_t const t = thread ? thread->t : pthread_self();
226   PTHREAD_AFFINITY_NP c;
227   uint64 ret = 0;
228   int ptec;
229 
230   //printf("SetAffinity() %016llx %08x\n", (unsigned long long)t, mask);
231   //
232   //
233   CPU_ZERO(&c);
234   if((ptec = pthread_getaffinity_np(t, sizeof(c), &c)))
235   {
236    ErrnoHolder ene(ptec);
237 
238    throw MDFN_Error(ene.Errno(), _("%s failed: %s"), "pthread_getaffinity_np()", ene.StrError());
239   }
240 
241   for(unsigned i = 0; i < sizeof(ret) * 8 && i < CPU_SETSIZE; i++)
242   {
243    if(CPU_ISSET(i, &c))
244     ret |= (uint64)1 << i;
245   }
246   //
247   //
248   CPU_ZERO(&c);
249 
250   for(unsigned i = 0; i < sizeof(mask) * 8 && i < CPU_SETSIZE; i++)
251   {
252    if((mask >> i) & 1)
253     CPU_SET(i, &c);
254   }
255 
256   if(CPU_SETSIZE < 64 && mask >= ((uint64)1 << CPU_SETSIZE))
257   {
258    throw MDFN_Error(EINVAL, _("Affinity mask specifies CPU beyond capacity of the CPU set."));
259   }
260 
261   if((ptec = pthread_setaffinity_np(t, sizeof(c), &c)))
262   {
263    ErrnoHolder ene(ptec);
264 
265    throw MDFN_Error(ene.Errno(), _("%s failed: %s"), "pthread_setaffinity_np()", ene.StrError());
266   }
267 
268   return ret;
269  }
270  catch(std::exception& e)
271  {
272   throw MDFN_Error(0, _("Setting affinity to 0x%016llx failed: %s"), (unsigned long long)mask, e.what());
273  }
274 }
275 #else
276 
277 #warning "Compiling without affinity setting support."
Thread_SetAffinity(Thread * thread,uint64 mask)278 uint64 Thread_SetAffinity(Thread* thread, uint64 mask)
279 {
280  assert(mask != 0);
281  //
282  throw MDFN_Error(0, _("Setting affinity to 0x%016llx failed: %s"), (unsigned long long)mask, _("pthread_setaffinity_np() not available."));
283 }
284 #endif
285 
CreateMutex(Mutex * ret)286 static void CreateMutex(Mutex* ret)
287 {
288  pthread_mutexattr_t attr;
289  int ptec;
290 
291  if((ptec = pthread_mutexattr_init(&attr)))
292  {
293   ErrnoHolder ene(ptec);
294 
295   throw MDFN_Error(ene.Errno(), _("%s failed: %s"), "pthread_mutexattr_init()", ene.StrError());
296  }
297 
298  if((ptec = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK/*PTHREAD_MUTEX_NORMAL*/)))
299  {
300   ErrnoHolder ene(ptec);
301 
302   pthread_mutexattr_destroy(&attr);
303 
304   throw MDFN_Error(ene.Errno(), _("%s failed: %s"), "pthread_mutexattr_settype()", ene.StrError());
305  }
306 
307  if((ptec = pthread_mutex_init(&ret->m, &attr)))
308  {
309   ErrnoHolder ene(ptec);
310 
311   pthread_mutexattr_destroy(&attr);
312 
313   throw MDFN_Error(ene.Errno(), _("%s failed: %s"), "pthread_mutex_init()", ene.StrError());
314  }
315 
316  pthread_mutexattr_destroy(&attr);
317  //
318  //
319  //
320  if((ptec = pthread_mutex_lock(&ret->m)))
321  {
322   ErrnoHolder ene(ptec);
323 
324   throw MDFN_Error(ene.Errno(), _("%s failed: %s"), "pthread_mutex_lock()", ene.StrError());
325  }
326 
327  if((ptec = pthread_mutex_unlock(&ret->m)))
328  {
329   ErrnoHolder ene(ptec);
330 
331   throw MDFN_Error(ene.Errno(), _("%s failed: %s"), "pthread_mutex_unlock()", ene.StrError());
332  }
333 }
334 
Mutex_Create(void)335 Mutex* Mutex_Create(void)
336 {
337  std::unique_ptr<Mutex> ret(new Mutex);
338 
339  CreateMutex(ret.get());
340 
341  return ret.release();
342 }
343 
Mutex_Destroy(Mutex * mutex)344 void Mutex_Destroy(Mutex* mutex) noexcept
345 {
346  int ptec;
347 
348  if((ptec = pthread_mutex_destroy(&mutex->m)))
349  {
350   ErrnoHolder ene(ptec);
351 
352   MDFN_Notify(MDFN_NOTICE_ERROR, _("%s failed: %s"), "pthread_mutex_destroy()", ene.StrError());
353  }
354 
355  delete mutex;
356 }
357 
Mutex_Lock(Mutex * mutex)358 bool Mutex_Lock(Mutex* mutex) noexcept
359 {
360  int ptec;
361 
362  if((ptec = pthread_mutex_lock(&mutex->m)))
363  {
364   fprintf(stderr, "pthread_mutex_lock() failed: %d\n", ptec);
365   return false;
366  }
367 
368  return true;
369 }
370 
Mutex_Unlock(Mutex * mutex)371 bool Mutex_Unlock(Mutex* mutex) noexcept
372 {
373  int ptec;
374 
375  if((ptec = pthread_mutex_unlock(&mutex->m)))
376  {
377   fprintf(stderr, "pthread_mutex_unlock() failed: %d\n", ptec);
378   return false;
379  }
380 
381  return true;
382 }
383 
CreateCond(Cond * ret)384 static void CreateCond(Cond* ret)
385 {
386  pthread_condattr_t attr;
387  int ptec;
388 
389  if((ptec = pthread_condattr_init(&attr)))
390  {
391   ErrnoHolder ene(ptec);
392 
393   throw MDFN_Error(ene.Errno(), _("%s failed: %s"), "pthread_condattr_init()", ene.StrError());
394  }
395 
396 #if !defined(MDFN_USE_COND_TIMEDWAIT_RELATIVE_NP)
397  if((ptec = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)))
398  {
399   ErrnoHolder ene(ptec);
400 
401   pthread_condattr_destroy(&attr);
402 
403   throw MDFN_Error(ene.Errno(), _("%s failed: %s"), "pthread_condattr_setclock()", ene.StrError());
404  }
405 #endif
406 
407  if((ptec = pthread_cond_init(&ret->c, &attr)))
408  {
409   ErrnoHolder ene(ptec);
410 
411   pthread_condattr_destroy(&attr);
412 
413   throw MDFN_Error(ene.Errno(), _("%s failed: %s"), "pthread_cond_init()", ene.StrError());
414  }
415 
416  pthread_condattr_destroy(&attr);
417 }
418 
Cond_Create(void)419 Cond* Cond_Create(void)
420 {
421  std::unique_ptr<Cond> ret(new Cond);
422 
423  CreateCond(ret.get());
424 
425  return ret.release();
426 }
427 
Cond_Destroy(Cond * cond)428 void Cond_Destroy(Cond* cond) noexcept
429 {
430  int ptec;
431 
432  if((ptec = pthread_cond_destroy(&cond->c)))
433  {
434   ErrnoHolder ene(ptec);
435 
436   MDFN_Notify(MDFN_NOTICE_ERROR, _("%s failed: %s"), "pthread_cond_destroy()", ene.StrError());
437  }
438 
439  delete cond;
440 }
441 
Cond_Signal(Cond * cond)442 bool Cond_Signal(Cond* cond) noexcept
443 {
444  int ptec;
445 
446  if((ptec = pthread_cond_signal(&cond->c)))
447  {
448   fprintf(stderr, "pthread_cond_signal() failed: %d\n", ptec);
449   return false;
450  }
451 
452  return true;
453 }
454 
Cond_Wait(Cond * cond,Mutex * mutex)455 bool Cond_Wait(Cond* cond, Mutex* mutex) noexcept
456 {
457  int ptec;
458 
459  if((ptec = pthread_cond_wait(&cond->c, &mutex->m)))
460  {
461   fprintf(stderr, "pthread_cond_wait() failed: %d\n", ptec);
462   return false;
463  }
464 
465  return true;
466 }
467 
468 #if !defined(MDFN_USE_COND_TIMEDWAIT_RELATIVE_NP)
Cond_TimedWait(Cond * cond,Mutex * mutex,unsigned ms)469 bool Cond_TimedWait(Cond* cond, Mutex* mutex, unsigned ms) noexcept
470 {
471  struct timespec abstime;
472 
473  memset(&abstime, 0, sizeof(abstime));
474 
475  if(clock_gettime(CLOCK_MONOTONIC, &abstime))
476  {
477   fprintf(stderr, "clock_gettime() failed: %m\n");
478   return false;
479  }
480 
481  TimeSpec_AddNanoseconds(&abstime, (uint64)ms * 1000 * 1000);
482 
483  int ctw_rv = pthread_cond_timedwait(&cond->c, &mutex->m, &abstime);
484  if(ctw_rv == ETIMEDOUT)
485   return false;
486  else if(ctw_rv)
487  {
488   fprintf(stderr, "pthread_cond_timedwait() failed: %d\n", ctw_rv);
489   return false;
490  }
491 
492  return true;
493 }
494 #else
Cond_TimedWait(Cond * cond,Mutex * mutex,unsigned ms)495 bool Cond_TimedWait(Cond* cond, Mutex* mutex, unsigned ms) noexcept
496 {
497  struct timespec reltime;
498 
499  memset(&reltime, 0, sizeof(reltime));
500 
501  TimeSpec_AddNanoseconds(&reltime, (uint64)ms * 1000 * 1000);
502 
503  int ctw_rv = pthread_cond_timedwait_relative_np(&cond->c, &mutex->m, &reltime);
504  if(ctw_rv == ETIMEDOUT)
505   return false;
506  else if(ctw_rv)
507  {
508   fprintf(stderr, "pthread_cond_timedwait_relative_np() failed: %d\n", ctw_rv);
509   return false;
510  }
511 
512  return true;
513 }
514 #endif
515 
516 #if !defined(MDFN_USE_CONDVAR_SEMAPHORES)
517 //
518 //
519 //
520 struct Sem
521 {
SemMednafen::MThreading::Sem522  Sem() { }
523 
524  sem_t s;
525  private:
526  Sem(const Sem&);
527 };
528 
Sem_Create(void)529 Sem* Sem_Create(void)
530 {
531  std::unique_ptr<Sem> ret(new Sem);
532 
533  if(sem_init(&ret->s, 0, 0))
534  {
535   ErrnoHolder ene(errno);
536 
537   throw MDFN_Error(ene.Errno(), _("%s failed: %s"), "sem_init()", ene.StrError());
538  }
539 
540  return ret.release();
541 }
542 
Sem_Destroy(Sem * sem)543 void Sem_Destroy(Sem* sem) noexcept
544 {
545  if(sem_destroy(&sem->s))
546  {
547   ErrnoHolder ene(errno);
548 
549   MDFN_Notify(MDFN_NOTICE_ERROR, _("%s failed: %s"), "sem_destroy()", ene.StrError());
550  }
551 
552  delete sem;
553 }
554 
Sem_Wait(Sem * sem)555 bool Sem_Wait(Sem* sem) noexcept
556 {
557  tryagain:
558  if(sem_wait(&sem->s))
559  {
560   if(errno == EINTR)
561    goto tryagain;
562 
563   fprintf(stderr, "sem_wait() failed: %m");
564   return false;
565  }
566 
567  return true;
568 }
569 
570 #if defined(MDFN_USE_SEM_CLOCKWAIT_NP)
Sem_TimedWait(Sem * sem,unsigned ms)571 bool Sem_TimedWait(Sem* sem, unsigned ms) noexcept
572 {
573  struct timespec abstime;
574 
575  memset(&abstime, 0, sizeof(abstime));
576 
577  if(clock_gettime(CLOCK_MONOTONIC, &abstime))
578  {
579   fprintf(stderr, "clock_gettime() failed: %m");
580   return false;
581  }
582 
583  TimeSpec_AddNanoseconds(&abstime, (uint64)ms * 1000 * 1000);
584 
585  tryagain:
586  if(sem_clockwait_np(&sem->s, CLOCK_MONOTONIC, TIMER_ABSTIME, &abstime, nullptr))
587  {
588   if(errno == EINTR)
589    goto tryagain;
590 
591   if(errno == ETIMEDOUT)
592    return false;
593   //
594   fprintf(stderr, "sem_clockwait_np() failed: %m");
595   return false;
596  }
597 
598  return true;
599 }
600 #elif defined(MDFN_USE_SEM_CLOCKWAIT)
Sem_TimedWait(Sem * sem,unsigned ms)601 bool Sem_TimedWait(Sem* sem, unsigned ms) noexcept
602 {
603  struct timespec abstime;
604 
605  memset(&abstime, 0, sizeof(abstime));
606 
607  if(clock_gettime(CLOCK_MONOTONIC, &abstime))
608  {
609   fprintf(stderr, "clock_gettime() failed: %m");
610   return false;
611  }
612 
613  TimeSpec_AddNanoseconds(&abstime, (uint64)ms * 1000 * 1000);
614 
615  tryagain:
616  if(sem_clockwait(&sem->s, CLOCK_MONOTONIC, &abstime))
617  {
618   if(errno == EINTR)
619    goto tryagain;
620 
621   if(errno == ETIMEDOUT)
622    return false;
623   //
624   fprintf(stderr, "sem_clockwait() failed: %m");
625   return false;
626  }
627 
628  return true;
629 }
630 #else
631 
632 #warning "Using realtime-clock-based sem_timedwait()"
633 
Sem_TimedWait(Sem * sem,unsigned ms)634 bool Sem_TimedWait(Sem* sem, unsigned ms) noexcept
635 {
636  struct timespec abstime;
637 
638  memset(&abstime, 0, sizeof(abstime));
639 
640  if(clock_gettime(CLOCK_REALTIME, &abstime))
641  {
642   fprintf(stderr, "clock_gettime() failed: %m");
643   return false;
644  }
645 
646  TimeSpec_AddNanoseconds(&abstime, (uint64)ms * 1000 * 1000);
647 
648  tryagain:
649  if(sem_timedwait(&sem->s, &abstime))
650  {
651   if(errno == EINTR)
652    goto tryagain;
653 
654   if(errno == ETIMEDOUT)
655    return false;
656   //
657   fprintf(stderr, "sem_timedwait() failed: %m");
658   return false;
659  }
660 
661  return true;
662 }
663 #endif
664 
Sem_Post(Sem * sem)665 bool Sem_Post(Sem* sem) noexcept
666 {
667  if(sem_post(&sem->s))
668  {
669   fprintf(stderr, "sem_post() failed: %m");
670   return false;
671  }
672 
673  return true;
674 }
675 #else
676 #warning "Cond var semaphore"
677 //
678 // Semaphores via condition var.
679 //
680 struct Sem
681 {
SemMednafen::MThreading::Sem682  Sem() { }
683 
684  Cond c;
685  Mutex m;
686  int v;
687 
688  private:
689  Sem(const Sem&);
690 };
691 
Sem_Create(void)692 Sem* Sem_Create(void)
693 {
694  std::unique_ptr<Sem> ret(new Sem);
695 
696  CreateCond(&ret->c);
697  CreateMutex(&ret->m);
698 
699  ret->v = 0;
700 
701  return ret.release();
702 }
703 
Sem_Destroy(Sem * sem)704 void Sem_Destroy(Sem* sem) noexcept
705 {
706  int ptec;
707 
708  if((ptec = pthread_cond_destroy(&sem->c.c)))
709  {
710   ErrnoHolder ene(ptec);
711 
712   MDFN_Notify(MDFN_NOTICE_ERROR, _("%s failed: %s"), "pthread_cond_destroy()", ene.StrError());
713  }
714 
715  if((ptec = pthread_mutex_destroy(&sem->m.m)))
716  {
717   ErrnoHolder ene(ptec);
718 
719   MDFN_Notify(MDFN_NOTICE_ERROR, _("%s failed: %s"), "pthread_mutex_destroy()", ene.StrError());
720  }
721 
722  delete sem;
723 }
724 
Sem_Wait(Sem * sem)725 bool Sem_Wait(Sem* sem) noexcept
726 {
727  if(!Mutex_Lock(&sem->m))
728   return false;
729 
730  while(!sem->v)
731  {
732   if(!Cond_Wait(&sem->c, &sem->m))
733   {
734    Mutex_Unlock(&sem->m);
735    return false;
736   }
737  }
738 
739  sem->v--;
740  assert(sem->v >= 0);
741  return Mutex_Unlock(&sem->m);
742 }
743 
Sem_TimedWait(Sem * sem,unsigned ms)744 bool Sem_TimedWait(Sem* sem, unsigned ms) noexcept
745 {
746  if(!Mutex_Lock(&sem->m))
747   return false;
748 
749  while(!sem->v)
750  {
751   if(!Cond_TimedWait(&sem->c, &sem->m, ms))
752   {
753    Mutex_Unlock(&sem->m);
754    return false;
755   }
756  }
757 
758  sem->v--;
759  assert(sem->v >= 0);
760  return Mutex_Unlock(&sem->m);
761 }
762 
Sem_Post(Sem * sem)763 bool Sem_Post(Sem* sem) noexcept
764 {
765  if(!Mutex_Lock(&sem->m))
766   return false;
767 
768  if(sem->v == INT_MAX)
769  {
770   Mutex_Unlock(&sem->m);
771   return false;
772  }
773 
774  sem->v++;
775 
776  bool ret = true;
777 
778  ret &= Cond_Signal(&sem->c);
779  ret &= Mutex_Unlock(&sem->m);
780 
781  return ret;
782 }
783 
784 #endif
785 
786 }
787 }
788