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