PS2SDK
PS2 Homebrew Libraries
Loading...
Searching...
No Matches
osal.c
1/*
2# _____ ___ ____ ___ ____
3# ____| | ____| | | |____|
4# | ___| |____ ___| ____| | \ PS2DEV Open Source Project.
5#-----------------------------------------------------------------------
6# Copyright 2001-2004, ps2dev - http://www.ps2dev.org
7# Licenced under Academic Free License version 2.0
8# Review ps2sdk README & LICENSE files for further details.
9*/
10
11#include <ps2_osal.h>
12#include <time.h>
13#include <kernel_util.h>
14#include <delaythread.h>
15
16#define MAX_PS2_UID 2048 // SWAG
17#define DEFAULT_STACK_SIZE_BYTES 4096
18#define PS2_MAX_TLS 32
19#define MAX_THREADS 256
20
21#if 0
22#define PS2_DEBUG(x) printf(x)
23#else
24#define PS2_DEBUG(x)
25#endif
26
27#define POLLING_DELAY_IN_us 100
28
29#if F___threadInfo
30struct OsalThreadInfo __threadInfo[MAX_THREADS];
31#endif
32
33/* TLS key used to access ps2ThreadData struct for reach thread. */
34#ifdef F___threadDataKey
35unsigned int __threadDataKey;
36#else
37extern unsigned int __threadDataKey;
38#endif
39
40extern void *__globalTls;
41
42/*
43 * Data stored on a per-thread basis - allocated in pte_osThreadCreate
44 * and freed in pte_osThreadDelete.
45 */
46typedef struct ps2ThreadData
47 {
48 /* Entry point and parameters to thread's main function */
49 pte_osThreadEntryPoint entryPoint;
50 void * argv;
51
52 /* Semaphore used for cancellation. Posted to by pte_osThreadCancel,
53 polled in pte_osSemaphoreCancellablePend */
54 s32 cancelSem;
55
57
58
59/****************************************************************************
60 *
61 * Helper functions
62 *
63 ***************************************************************************/
64#ifdef F___getThreadData
65ps2ThreadData *__getThreadData(s32 threadHandle)
66{
67 ps2ThreadData *pThreadData;
68 void *pTls;
69
70 pTls = __getTlsStructFromThread(threadHandle);
71 pThreadData = (ps2ThreadData *) pteTlsGetValue(pTls, __threadDataKey);
72
73 return pThreadData;
74}
75#else
76ps2ThreadData *__getThreadData(s32 threadHandle);
77#endif
78
79static inline int32_t SemWaitTimeout(s32 semHandle, uint32_t timeout)
80{
81 u64 timeoutUsec;
82 u64 *timeoutPtr;
83
84 if (timeout == 0) {
85 return PollSema(semHandle);
86 }
87
88 timeoutPtr = NULL;
89
90 if (timeout > 0 && timeout != UINT32_MAX) {
91 timeoutUsec = timeout;
92 timeoutPtr = &timeoutUsec;
93 return WaitSemaEx(semHandle, 1, timeoutPtr);
94 }
95
96 return WaitSema(semHandle);
97}
98
99/* A new thread's stub entry point. It retrieves the real entry point from the per thread control
100 * data as well as any parameters to this function, and then calls the entry point.
101 */
102#ifdef F___ps2StubThreadEntry
103int __ps2StubThreadEntry(void *argv)
104{
105 int result;
106 ps2ThreadData *pThreadData;
107
108 pThreadData = __getThreadData(GetThreadId());
109 result = (*(pThreadData->entryPoint))(pThreadData->argv);
110
111 return result;
112}
113#else
114extern int __ps2StubThreadEntry(void *argv);
115#endif
116
117/****************************************************************************
118 *
119 * Initialization
120 *
121 ***************************************************************************/
122#ifdef F_pte_osInit
123pte_osResult pte_osInit(void)
124{
125 ee_sema_t sema;
126 pte_osResult result;
127 ps2ThreadData *pThreadData;
128
129 /* Allocate and initialize TLS support */
130 result = pteTlsGlobalInit(PS2_MAX_TLS);
131
132 if (result == PTE_OS_OK) {
133 /* Allocate a key that we use to store control information (e.g. cancellation semaphore) per thread */
134 result = __pteTlsAlloc(&__threadDataKey);
135
136 if (result == PTE_OS_OK) {
137 /* Initialize the structure used to emulate TLS for
138 * non-POSIX threads
139 */
140 __globalTls = pteTlsThreadInit();
141
142 /* Also create a "thread data" structure for a single non-POSIX thread. */
143
144 /* Allocate some memory for our per-thread control data. We use this for:
145 * 1. Entry point and parameters for the user thread's main function.
146 * 2. Semaphore used for thread cancellation.
147 */
148 pThreadData = (ps2ThreadData *) malloc(sizeof(ps2ThreadData));
149
150 if (pThreadData == NULL) {
151 result = PTE_OS_NO_RESOURCES;
152 } else {
153 /* Save a pointer to our per-thread control data as a TLS value */
154 __pteTlsSetValue(__globalTls, __threadDataKey, pThreadData);
155
156 sema.init_count = 0;
157 sema.max_count = 255;
158 sema.option = 0;
159 pThreadData->cancelSem = CreateSema(&sema);
160
161 result = PTE_OS_OK;
162 }
163 }
164 }
165
166 return result;
167}
168#endif
169
170#ifdef F_pte_osTerminate
171pte_osResult pte_osTerminate(void) {
172 pteTlsGlobalDestroy();
173 return PTE_OS_OK;
174}
175#endif
176
177/****************************************************************************
178 *
179 * Threads
180 *
181 ***************************************************************************/
182
183static inline int invert_priority(int priority)
184{
185 return (pte_osThreadGetMinPriority() - priority) + pte_osThreadGetMaxPriority();
186}
187
188#ifdef F_pte_osThreadCreate
189pte_osResult pte_osThreadCreate(pte_osThreadEntryPoint entryPoint,
190 int stackSize,
191 int initialPriority,
192 void *argv,
193 pte_osThreadHandle* ppte_osThreadHandle)
194{
195 ee_thread_t eethread;
196 ee_sema_t sema;
197 struct OsalThreadInfo *threadInfo;
198 void *stack;
199 static int threadNum = 1;
200 void *pTls;
201 s32 threadId;
202 pte_osResult result;
203 ps2ThreadData *pThreadData;
204
205 if (threadNum++ > MAX_PS2_UID) {
206 threadNum = 0;
207 }
208
209 /* Make sure that the stack we're going to allocate is big enough */
210 if (stackSize < DEFAULT_STACK_SIZE_BYTES) {
211 stackSize = DEFAULT_STACK_SIZE_BYTES;
212 }
213
214 /* Allocate TLS structure for this thread. */
215 pTls = pteTlsThreadInit();
216 if (pTls == NULL) {
217 PS2_DEBUG("pteTlsThreadInit: PTE_OS_NO_RESOURCES\n");
218 result = PTE_OS_NO_RESOURCES;
219 goto FAIL0;
220 }
221
222 /* Allocate some memory for our per-thread control data. We use this for:
223 * 1. Entry point and parameters for the user thread's main function.
224 * 2. Semaphore used for thread cancellation.
225 */
226 pThreadData = (ps2ThreadData *) malloc(sizeof(ps2ThreadData));
227
228 if (pThreadData == NULL) {
229 pteTlsThreadDestroy(pTls);
230
231 PS2_DEBUG("malloc(ps2ThreadData): PTE_OS_NO_RESOURCES\n");
232 result = PTE_OS_NO_RESOURCES;
233 goto FAIL0;
234 }
235
236 /* Save a pointer to our per-thread control data as a TLS value */
237 __pteTlsSetValue(pTls, __threadDataKey, pThreadData);
238
239 pThreadData->entryPoint = entryPoint;
240 pThreadData->argv = argv;
241
242 sema.init_count = 0;
243 sema.max_count = 255;
244 sema.option = 0;
245 pThreadData->cancelSem = CreateSema(&sema);
246
247 /* Create EE Thread */
248 stack = malloc(stackSize);
249
250 eethread.attr = 0;
251 eethread.option = 0;
252 eethread.func = &__ps2StubThreadEntry;
253 eethread.stack = stack;
254 eethread.stack_size = stackSize;
255 eethread.gp_reg = &_gp;
256 eethread.initial_priority = invert_priority(initialPriority);
257 threadId = CreateThread(&eethread);
258
259 /* In order to emulate TLS functionality, we append the address of the TLS structure that we
260 * allocated above to an additional struct. To set or get TLS values for this thread, the user
261 * needs to get the threadId from the OS and then extract
262 * a pointer to the TLS structure.
263 */
264 threadInfo = &__threadInfo[threadId];
265 threadInfo->threadNumber = threadNum;
266 threadInfo->tlsPtr = pTls;
267
268 if (!stack) {
269 free(pThreadData);
270 pteTlsThreadDestroy(pTls);
271
272 PS2_DEBUG("CreateThread: PTE_OS_NO_RESOURCES\n");
273 result = PTE_OS_NO_RESOURCES;
274 } else if (threadId < 0) {
275 free(pThreadData);
276 pteTlsThreadDestroy(pTls);
277
278 PS2_DEBUG("CreateThread: PTE_OS_GENERAL_FAILURE\n");
279 result = PTE_OS_GENERAL_FAILURE;
280 } else {
281 *ppte_osThreadHandle = threadId;
282 result = PTE_OS_OK;
283 }
284
285FAIL0:
286 return result;
287}
288#endif
289
290#ifdef F_pte_osThreadStart
291pte_osResult pte_osThreadStart(pte_osThreadHandle osThreadHandle)
292{
293 StartThread(osThreadHandle, 0);
294
295 return PTE_OS_OK;
296}
297#endif
298
299#ifdef F_pte_osThreadDelete
300pte_osResult pte_osThreadDelete(pte_osThreadHandle handle)
301{
302 ps2ThreadData *pThreadData;
303 void *pTls;
305 struct OsalThreadInfo *threadInfo;
306 int res;
307
308 res = ReferThreadStatus(handle, &info);
309
310 pTls = __getTlsStructFromThread(handle);
311 pThreadData = __getThreadData(handle);
312 DeleteSema(pThreadData->cancelSem);
313 free(pThreadData);
314 pteTlsThreadDestroy(pTls);
315 TerminateThread(handle);
316 DeleteThread(handle);
317
318 if (res > 0 && info.stack) {
319 free(info.stack);
320 }
321
322 threadInfo = &__threadInfo[handle];
323 threadInfo->threadNumber = 0;
324 threadInfo->tlsPtr = NULL;
325
326 return PTE_OS_OK;
327}
328#endif
329
330#ifdef F_pte_osThreadExitAndDelete
331pte_osResult pte_osThreadExitAndDelete(pte_osThreadHandle handle)
332{
333 pte_osThreadDelete(handle);
334 ExitDeleteThread();
335
336 return PTE_OS_OK;
337}
338#endif
339
340#ifdef F_pte_osThreadExit
341void pte_osThreadExit()
342{
343 ExitThread();
344}
345#endif
346
347/*
348 * This has to be cancellable, so we can't just call sceKernelWaitThreadEnd.
349 * Instead, poll on this in a loop, like we do for a cancellable semaphore.
350 */
351#ifdef F_pte_osThreadWaitForEnd
352pte_osResult pte_osThreadWaitForEnd(pte_osThreadHandle threadHandle)
353{
354 pte_osResult result;
355 ps2ThreadData *pThreadData;
356
357 pThreadData = __getThreadData(GetThreadId());
358
359 while (1) {
361
362 /* Poll task to see if it has ended */
363 ReferThreadStatus(threadHandle, &info);
364
365 if (info.status == THS_DORMANT) {
366 /* Thread has ended */
367 result = PTE_OS_OK;
368 break;
369 } else {
370 ee_sema_t semInfo;
371
372 if (pThreadData != NULL) {
373 s32 osResult;
374
375 osResult = ReferSemaStatus(pThreadData->cancelSem, &semInfo);
376 if (osResult == pThreadData->cancelSem) {
377 if (semInfo.count > 0) {
378 result = PTE_OS_INTERRUPTED;
379 break;
380 } else {
381 /* Nothing found and not timed out yet; let's yield so we're not
382 * in busy loop.
383 */
384 DelayThread(POLLING_DELAY_IN_us);
385 }
386 } else {
387 result = PTE_OS_GENERAL_FAILURE;
388 break;
389 }
390 }
391 }
392 }
393
394 return result;
395}
396#endif
397
398#ifdef F_pte_osThreadGetHandle
399pte_osThreadHandle pte_osThreadGetHandle(void)
400{
401 return GetThreadId();
402}
403#endif
404
405#ifdef F_pte_osThreadGetPriority
406int pte_osThreadGetPriority(pte_osThreadHandle threadHandle)
407{
408 ee_thread_status_t thinfo;
409
410 ReferThreadStatus(threadHandle, &thinfo);
411
412 return invert_priority(thinfo.current_priority);
413}
414#endif
415
416#ifdef F_pte_osThreadSetPriority
417pte_osResult pte_osThreadSetPriority(pte_osThreadHandle threadHandle, int newPriority)
418{
419 ChangeThreadPriority(threadHandle, invert_priority(newPriority));
420 return PTE_OS_OK;
421}
422#endif
423
424#ifdef F_pte_osThreadCancel
425pte_osResult pte_osThreadCancel(pte_osThreadHandle threadHandle)
426{
427 s32 osResult;
428 pte_osResult result;
429 ps2ThreadData *pThreadData;
430
431 pThreadData = __getThreadData(threadHandle);
432 osResult = SignalSema(pThreadData->cancelSem);
433
434 if (osResult == pThreadData->cancelSem) {
435 result = PTE_OS_OK;
436 } else {
437 result = PTE_OS_GENERAL_FAILURE;
438 }
439
440 return result;
441}
442#endif
443
444#ifdef F_pte_osThreadCheckCancel
445pte_osResult pte_osThreadCheckCancel(pte_osThreadHandle threadHandle)
446{
447 ps2ThreadData *pThreadData;
448 ee_sema_t semInfo;
449 s32 osResult;
450 pte_osResult result;
451
452 pThreadData = __getThreadData(threadHandle);
453 if (pThreadData != NULL) {
454 osResult = ReferSemaStatus(pThreadData->cancelSem, &semInfo);
455
456 if (osResult == pThreadData->cancelSem) {
457 if (semInfo.count > 0) {
458 result = PTE_OS_INTERRUPTED;
459 } else {
460 result = PTE_OS_OK;
461 }
462 } else {
463 /* sceKernelReferSemaStatus returned an error */
464 result = PTE_OS_GENERAL_FAILURE;
465 }
466 } else {
467 /* For some reason, we couldn't get thread data */
468 result = PTE_OS_GENERAL_FAILURE;
469 }
470
471 return result;
472}
473#endif
474
475#ifdef F_pte_osThreadSleep
476void pte_osThreadSleep(unsigned int msecs)
477{
478 DelayThread(msecs * 1000);
479}
480#endif
481
482#ifdef F_pte_osThreadGetMinPriority
483int pte_osThreadGetMinPriority()
484{
485 return pte_osThreadGetDefaultPriority() - 32;
486}
487#endif
488
489#ifdef F_pte_osThreadGetMaxPriority
490int pte_osThreadGetMaxPriority()
491{
492 return pte_osThreadGetDefaultPriority() + 32;
493}
494#endif
495
496#ifdef F_pte_osThreadGetDefaultPriority
497int pte_osThreadGetDefaultPriority()
498{
499 return 60;
500}
501#endif
502
503#ifdef F_pthread_num_processors_np
504int pthread_num_processors_np(void)
505{
506 return 1;
507}
508#endif
509
510/****************************************************************************
511 *
512 * Mutexes
513 *
514 ****************************************************************************/
515#ifdef F_pte_osMutexCreate
516pte_osResult pte_osMutexCreate(pte_osMutexHandle *pHandle)
517{
518 static int mutexCtr = 0;
519 ee_sema_t sema;
520 pte_osMutexHandle handle;
521
522 if (mutexCtr++ > MAX_PS2_UID) {
523 mutexCtr = 0;
524 }
525
526 sema.init_count = 1;
527 sema.max_count = 1;
528 sema.option = 0;
529 handle = CreateSema(&sema);
530
531 *pHandle = handle;
532 return PTE_OS_OK;
533}
534#endif
535
536#ifdef F_pte_osMutexDelete
537pte_osResult pte_osMutexDelete(pte_osMutexHandle handle)
538{
539 DeleteSema(handle);
540
541 return PTE_OS_OK;
542}
543#endif
544
545#ifdef F_pte_osMutexLock
546pte_osResult pte_osMutexLock(pte_osMutexHandle handle)
547{
548 SemWaitTimeout(handle, UINT32_MAX);
549
550 return PTE_OS_OK;
551}
552#endif
553
554#ifdef F_pte_osMutexTimedLock
555pte_osResult pte_osMutexTimedLock(pte_osMutexHandle handle, unsigned int timeoutMsecs)
556{
557 pte_osResult result;
558 int32_t timeout = timeoutMsecs * 1000;
559 int status = SemWaitTimeout(handle, timeout);
560 if (status > 0) {
561 result = PTE_OS_OK;
562 } else if (status == -1) {
563 result = PTE_OS_TIMEOUT;
564 } else {
565 result = PTE_OS_GENERAL_FAILURE;
566 }
567
568 return result;
569}
570#endif
571
572#ifdef F_pte_osMutexUnlock
573pte_osResult pte_osMutexUnlock(pte_osMutexHandle handle)
574{
575 SignalSema(handle);
576 return PTE_OS_OK;
577}
578#endif
579
580/****************************************************************************
581 *
582 * Semaphores
583 *
584 ***************************************************************************/
585#ifdef F_pte_osSemaphoreCreate
586pte_osResult pte_osSemaphoreCreate(int initialValue, pte_osSemaphoreHandle *pHandle)
587{
588 pte_osSemaphoreHandle handle;
589 ee_sema_t sema;
590 static int semCtr = 0;
591
592 if (semCtr++ > MAX_PS2_UID) {
593 semCtr = 0;
594 }
595
596 sema.init_count = initialValue;
597 sema.max_count = 32767;
598 sema.option = 0;
599 handle = CreateSema(&sema);
600
601 *pHandle = handle;
602 return PTE_OS_OK;
603}
604#endif
605
606#ifdef F_pte_osSemaphoreDelete
607pte_osResult pte_osSemaphoreDelete(pte_osSemaphoreHandle handle)
608{
609 DeleteSema(handle);
610 return PTE_OS_OK;
611}
612#endif
613
614#ifdef F_pte_osSemaphorePost
615pte_osResult pte_osSemaphorePost(pte_osSemaphoreHandle handle, int count)
616{
617 int i;
618 for (i = 0; i < count; i++)
619 SignalSema(handle);
620
621 return PTE_OS_OK;
622}
623#endif
624
625#ifdef F_pte_osSemaphorePend
626pte_osResult pte_osSemaphorePend(pte_osSemaphoreHandle handle, unsigned int *pTimeoutMsecs)
627{
628 uint32_t timeout;
629 int32_t result;
630 pte_osResult osResult;
631
632 if (pTimeoutMsecs == NULL) {
633 timeout = UINT32_MAX;
634 } else {
635 timeout = *pTimeoutMsecs * 1000;
636 }
637
638 result = SemWaitTimeout(handle, timeout);
639 if (result > 0) {
640 osResult = PTE_OS_OK;
641 } else if (result == -1) {
642 osResult = PTE_OS_TIMEOUT;
643 } else {
644 osResult = PTE_OS_GENERAL_FAILURE;
645 }
646
647 return osResult;
648}
649#endif
650
651
652/*
653 * Pend on a semaphore- and allow the pend to be cancelled.
654 *
655 * PS2 OS provides no functionality to asynchronously interrupt a blocked call. We simulte
656 * this by polling on the main semaphore and the cancellation semaphore and sleeping in a loop.
657 */
658#ifdef F_pte_osSemaphoreCancellablePend
659pte_osResult pte_osSemaphoreCancellablePend(pte_osSemaphoreHandle semHandle, unsigned int *pTimeout)
660{
661 ps2ThreadData *pThreadData;
662
663 pThreadData = __getThreadData(GetThreadId());
664
665 clock_t start_time;
666 pte_osResult result = PTE_OS_OK;
667 uint32_t timeout;
668
669 start_time = clock();
670
671 // clock() is in microseconds, timeout as passed in was in milliseconds
672 if (pTimeout == NULL) {
673 timeout = 0;
674 } else {
675 timeout = *pTimeout * 1000;
676 }
677
678 while (1) {
679 int32_t status;
680
681 /* Poll semaphore */
682 status = SemWaitTimeout(semHandle, 0);
683 if (status > 0) {
684 /* User semaphore posted to */
685 result = PTE_OS_OK;
686 break;
687 } else if ((pTimeout != NULL) && ((clock() - start_time) > timeout)) {
688 /* Timeout expired */
689 result = PTE_OS_TIMEOUT;
690 break;
691 } else {
692 ee_sema_t semInfo;
693
694 if (pThreadData != NULL) {
695 s32 osResult;
696
697 osResult = ReferSemaStatus(pThreadData->cancelSem, &semInfo);
698 if (osResult == pThreadData->cancelSem) {
699 if (semInfo.count > 0) {
700 result = PTE_OS_INTERRUPTED;
701 break;
702 } else {
703 /* Nothing found and not timed out yet; let's yield so we're not
704 * in busy loop.
705 */
706 DelayThread(POLLING_DELAY_IN_us);
707 }
708 } else {
709 result = PTE_OS_GENERAL_FAILURE;
710 break;
711 }
712 }
713 }
714 }
715
716 return result;
717}
718#endif
719
720
721/****************************************************************************
722 *
723 * Atomic Operations
724 *
725 ***************************************************************************/
726#ifdef F_pte_osAtomicExchange
727int pte_osAtomicExchange(int *ptarg, int val)
728{
729 int intc = DIntr();
730 int origVal;
731
732 origVal = *ptarg;
733 *ptarg = val;
734
735 if(intc) { EIntr(); }
736 return origVal;
737}
738#endif
739
740#ifdef F_pte_osAtomicCompareExchange
741int pte_osAtomicCompareExchange(int *pdest, int exchange, int comp)
742{
743 int intc = DIntr();
744 int origVal;
745
746 origVal = *pdest;
747 if (*pdest == comp){
748 *pdest = exchange;
749 }
750
751 if(intc) { EIntr(); }
752 return origVal;
753}
754#endif
755
756#ifdef F_pte_osAtomicExchangeAdd
757int pte_osAtomicExchangeAdd(int volatile* pAddend, int value)
758{
759 int origVal;
760 int intc = DIntr();
761
762 origVal = *pAddend;
763 *pAddend += value;
764
765 if(intc) { EIntr(); }
766 return origVal;
767}
768#endif
769
770#ifdef F_pte_osAtomicDecrement
771int pte_osAtomicDecrement(int *pdest)
772{
773 int val;
774 int intc = DIntr();
775
776 (*pdest)--;
777 val = *pdest;
778
779 if(intc) { EIntr(); }
780 return val;
781}
782#endif
783
784#ifdef F_pte_osAtomicIncrement
785int pte_osAtomicIncrement(int *pdest)
786{
787 int val;
788 int intc = DIntr();
789
790 (*pdest)++;
791 val = *pdest;
792
793 if(intc) { EIntr(); }
794 return val;
795}
796#endif
#define MAX_THREADS
Definition kernel.h:70
u32 count
start sector of fragmented bd/file