N3DS: Backport semaphore fixes from #6776.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
diff --git a/docs/README-n3ds.md b/docs/README-n3ds.md
index 66e194d..e9e7c7d 100644
--- a/docs/README-n3ds.md
+++ b/docs/README-n3ds.md
@@ -25,3 +25,4 @@ cmake --install build
- SDL2main should be used to ensure ROMFS is enabled.
- By default, the extra L2 cache and higher clock speeds of the New 2/3DS lineup are enabled. If you wish to turn it off, use `osSetSpeedupEnable(false)` in your main function.
- `SDL_GetBasePath` returns the romfs root instead of the executable's directory.
+- The Nintendo 3DS uses a cooperative threading model on a single core, meaning a thread will never yield unless done manually through the `SDL_Delay` functions, or blocking waits (`SDL_LockMutex`, `SDL_SemWait`, `SDL_CondWait`, `SDL_WaitThread`). To avoid starving other threads, `SDL_SemTryWait` and `SDL_SemWaitTimeout` will yield if they fail to acquire the semaphore, see https://github.com/libsdl-org/SDL/pull/6776 for more information.
diff --git a/src/thread/n3ds/SDL_syssem.c b/src/thread/n3ds/SDL_syssem.c
index 1a4109b..e7cbacb 100644
--- a/src/thread/n3ds/SDL_syssem.c
+++ b/src/thread/n3ds/SDL_syssem.c
@@ -27,14 +27,16 @@
#include <3ds.h>
#include "SDL_thread.h"
+#include "SDL_timer.h"
+
+int WaitOnSemaphoreFor(SDL_sem *sem, Uint32 timeout);
struct SDL_semaphore
{
LightSemaphore semaphore;
};
-SDL_sem *
-SDL_CreateSemaphore(Uint32 initial_value)
+SDL_sem *SDL_CreateSemaphore(Uint32 initial_value)
{
SDL_sem *sem;
@@ -59,9 +61,7 @@ SDL_CreateSemaphore(Uint32 initial_value)
*/
void SDL_DestroySemaphore(SDL_sem *sem)
{
- if (sem) {
- SDL_free(sem);
- }
+ SDL_free(sem);
}
int SDL_SemTryWait(SDL_sem *sem)
@@ -70,35 +70,49 @@ int SDL_SemTryWait(SDL_sem *sem)
return SDL_InvalidParamError("sem");
}
- return SDL_SemWaitTimeout(sem, 0);
+ if (LightSemaphore_TryAcquire(&sem->semaphore, 1) != 0) {
+ /* If we failed, yield to avoid starvation on busy waits */
+ svcSleepThread(1);
+ return SDL_MUTEX_TIMEDOUT;
+ }
+
+ return 0;
}
int SDL_SemWaitTimeout(SDL_sem *sem, Uint32 timeout)
{
- int retval;
-
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
if (timeout == SDL_MUTEX_MAXWAIT) {
LightSemaphore_Acquire(&sem->semaphore, 1);
- retval = 0;
- } else {
- int return_code = LightSemaphore_TryAcquire(&sem->semaphore, 1);
- if (return_code != 0) {
- for (u32 i = 0; i < timeout; i++) {
- svcSleepThread(1000000LL);
- return_code = LightSemaphore_TryAcquire(&sem->semaphore, 1);
- if (return_code == 0) {
- break;
- }
- }
+ return 0;
+ }
+
+ if (LightSemaphore_TryAcquire(&sem->semaphore, 1) != 0) {
+ return WaitOnSemaphoreFor(sem, timeout);
+ }
+
+ return 0;
+}
+
+int WaitOnSemaphoreFor(SDL_sem *sem, Uint32 timeout)
+{
+ Uint64 stop_time = SDL_GetTicks64() + timeout;
+ Uint64 current_time = SDL_GetTicks64();
+ while (current_time < stop_time) {
+ if (LightSemaphore_TryAcquire(&sem->semaphore, 1) == 0) {
+ return 0;
}
- retval = return_code != 0 ? SDL_MUTEX_TIMEDOUT : 0;
+ /* 100 microseconds seems to be the sweet spot */
+ svcSleepThread(100000LL);
+ current_time = SDL_GetTicks64();
}
- return retval;
+ /* If we failed, yield to avoid starvation on busy waits */
+ svcSleepThread(1);
+ return SDL_MUTEX_TIMEDOUT;
}
int SDL_SemWait(SDL_sem *sem)
@@ -106,8 +120,7 @@ int SDL_SemWait(SDL_sem *sem)
return SDL_SemWaitTimeout(sem, SDL_MUTEX_MAXWAIT);
}
-Uint32
-SDL_SemValue(SDL_sem *sem)
+Uint32 SDL_SemValue(SDL_sem *sem)
{
if (sem == NULL) {
SDL_InvalidParamError("sem");
diff --git a/src/thread/n3ds/SDL_systhread.c b/src/thread/n3ds/SDL_systhread.c
index caf2df7..926dd34 100644
--- a/src/thread/n3ds/SDL_systhread.c
+++ b/src/thread/n3ds/SDL_systhread.c
@@ -49,8 +49,9 @@ static void ThreadEntry(void *arg)
int SDL_SYS_CreateThread(SDL_Thread *thread)
{
- s32 priority = N3DS_THREAD_PRIORITY_MEDIUM;
+ s32 priority;
size_t stack_size = GetStackSize(thread->stacksize);
+ svcGetThreadPriority(&priority, CUR_THREAD_HANDLE);
thread->handle = threadCreate(ThreadEntry,
thread,
diff --git a/test/testsem.c b/test/testsem.c
index 2b36a3a..8d8a249 100644
--- a/test/testsem.c
+++ b/test/testsem.c
@@ -210,6 +210,8 @@ TestOverheadContended(SDL_bool try_wait)
}
/* Make sure threads consumed everything */
while (SDL_SemValue(sem)) {
+ /* Friendlier with cooperative threading models */
+ SDL_Delay(1);
}
}
end_ticks = SDL_GetTicks();