Skip to content

Commit e580e08

Browse files
FtZPetruskaslouken
authored andcommitted
N3DS: Backport semaphore fixes from libsdl-org#6776.
1 parent b79732b commit e580e08

File tree

4 files changed

+41
-24
lines changed

4 files changed

+41
-24
lines changed

docs/README-n3ds.md

+1
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ cmake --install build
2525
- SDL2main should be used to ensure ROMFS is enabled.
2626
- 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.
2727
- `SDL_GetBasePath` returns the romfs root instead of the executable's directory.
28+
- 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.

src/thread/n3ds/SDL_syssem.c

+36-23
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,16 @@
2727
#include <3ds.h>
2828

2929
#include "SDL_thread.h"
30+
#include "SDL_timer.h"
31+
32+
int WaitOnSemaphoreFor(SDL_sem *sem, Uint32 timeout);
3033

3134
struct SDL_semaphore
3235
{
3336
LightSemaphore semaphore;
3437
};
3538

36-
SDL_sem *
37-
SDL_CreateSemaphore(Uint32 initial_value)
39+
SDL_sem *SDL_CreateSemaphore(Uint32 initial_value)
3840
{
3941
SDL_sem *sem;
4042

@@ -59,9 +61,7 @@ SDL_CreateSemaphore(Uint32 initial_value)
5961
*/
6062
void SDL_DestroySemaphore(SDL_sem *sem)
6163
{
62-
if (sem) {
63-
SDL_free(sem);
64-
}
64+
SDL_free(sem);
6565
}
6666

6767
int SDL_SemTryWait(SDL_sem *sem)
@@ -70,44 +70,57 @@ int SDL_SemTryWait(SDL_sem *sem)
7070
return SDL_InvalidParamError("sem");
7171
}
7272

73-
return SDL_SemWaitTimeout(sem, 0);
73+
if (LightSemaphore_TryAcquire(&sem->semaphore, 1) != 0) {
74+
/* If we failed, yield to avoid starvation on busy waits */
75+
svcSleepThread(1);
76+
return SDL_MUTEX_TIMEDOUT;
77+
}
78+
79+
return 0;
7480
}
7581

7682
int SDL_SemWaitTimeout(SDL_sem *sem, Uint32 timeout)
7783
{
78-
int retval;
79-
8084
if (sem == NULL) {
8185
return SDL_InvalidParamError("sem");
8286
}
8387

8488
if (timeout == SDL_MUTEX_MAXWAIT) {
8589
LightSemaphore_Acquire(&sem->semaphore, 1);
86-
retval = 0;
87-
} else {
88-
int return_code = LightSemaphore_TryAcquire(&sem->semaphore, 1);
89-
if (return_code != 0) {
90-
for (u32 i = 0; i < timeout; i++) {
91-
svcSleepThread(1000000LL);
92-
return_code = LightSemaphore_TryAcquire(&sem->semaphore, 1);
93-
if (return_code == 0) {
94-
break;
95-
}
96-
}
90+
return 0;
91+
}
92+
93+
if (LightSemaphore_TryAcquire(&sem->semaphore, 1) != 0) {
94+
return WaitOnSemaphoreFor(sem, timeout);
95+
}
96+
97+
return 0;
98+
}
99+
100+
int WaitOnSemaphoreFor(SDL_sem *sem, Uint32 timeout)
101+
{
102+
Uint64 stop_time = SDL_GetTicks64() + timeout;
103+
Uint64 current_time = SDL_GetTicks64();
104+
while (current_time < stop_time) {
105+
if (LightSemaphore_TryAcquire(&sem->semaphore, 1) == 0) {
106+
return 0;
97107
}
98-
retval = return_code != 0 ? SDL_MUTEX_TIMEDOUT : 0;
108+
/* 100 microseconds seems to be the sweet spot */
109+
svcSleepThread(100000LL);
110+
current_time = SDL_GetTicks64();
99111
}
100112

101-
return retval;
113+
/* If we failed, yield to avoid starvation on busy waits */
114+
svcSleepThread(1);
115+
return SDL_MUTEX_TIMEDOUT;
102116
}
103117

104118
int SDL_SemWait(SDL_sem *sem)
105119
{
106120
return SDL_SemWaitTimeout(sem, SDL_MUTEX_MAXWAIT);
107121
}
108122

109-
Uint32
110-
SDL_SemValue(SDL_sem *sem)
123+
Uint32 SDL_SemValue(SDL_sem *sem)
111124
{
112125
if (sem == NULL) {
113126
SDL_InvalidParamError("sem");

src/thread/n3ds/SDL_systhread.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ static void ThreadEntry(void *arg)
4949

5050
int SDL_SYS_CreateThread(SDL_Thread *thread)
5151
{
52-
s32 priority = N3DS_THREAD_PRIORITY_MEDIUM;
52+
s32 priority;
5353
size_t stack_size = GetStackSize(thread->stacksize);
54+
svcGetThreadPriority(&priority, CUR_THREAD_HANDLE);
5455

5556
thread->handle = threadCreate(ThreadEntry,
5657
thread,

test/testsem.c

+2
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,8 @@ TestOverheadContended(SDL_bool try_wait)
210210
}
211211
/* Make sure threads consumed everything */
212212
while (SDL_SemValue(sem)) {
213+
/* Friendlier with cooperative threading models */
214+
SDL_Delay(1);
213215
}
214216
}
215217
end_ticks = SDL_GetTicks();

0 commit comments

Comments
 (0)