summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/hle/function_wrappers.h17
-rw-r--r--src/core/hle/kernel/event.cpp3
-rw-r--r--src/core/hle/kernel/kernel.cpp22
-rw-r--r--src/core/hle/kernel/kernel.h6
-rw-r--r--src/core/hle/kernel/mutex.cpp9
-rw-r--r--src/core/hle/kernel/semaphore.cpp8
-rw-r--r--src/core/hle/kernel/thread.cpp76
-rw-r--r--src/core/hle/kernel/thread.h14
-rw-r--r--src/core/hle/svc.cpp26
9 files changed, 68 insertions, 113 deletions
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 23c86a72d..5949cb470 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -9,11 +9,15 @@
9#include "core/arm/arm_interface.h" 9#include "core/arm/arm_interface.h"
10#include "core/memory.h" 10#include "core/memory.h"
11#include "core/hle/hle.h" 11#include "core/hle/hle.h"
12#include "core/hle/result.h"
12 13
13namespace HLE { 14namespace HLE {
14 15
15#define PARAM(n) Core::g_app_core->GetReg(n) 16#define PARAM(n) Core::g_app_core->GetReg(n)
16 17
18/// An invalid result code that is meant to be overwritten when a thread resumes from waiting
19static const ResultCode RESULT_INVALID(0xDEADC0DE);
20
17/** 21/**
18 * HLE a function return from the current ARM11 userland process 22 * HLE a function return from the current ARM11 userland process
19 * @param res Result to return 23 * @param res Result to return
@@ -57,8 +61,11 @@ template<ResultCode func(s32*, u32*, s32, bool, s64)> void Wrap() {
57 s32 param_1 = 0; 61 s32 param_1 = 0;
58 s32 retval = func(&param_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2), 62 s32 retval = func(&param_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2),
59 (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0))).raw; 63 (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0))).raw;
60 Core::g_app_core->SetReg(1, (u32)param_1); 64
61 FuncReturn(retval); 65 if (retval != RESULT_INVALID.raw) {
66 Core::g_app_core->SetReg(1, (u32)param_1);
67 FuncReturn(retval);
68 }
62} 69}
63 70
64template<ResultCode func(u32, u32, u32, u32, s64)> void Wrap() { 71template<ResultCode func(u32, u32, u32, u32, s64)> void Wrap() {
@@ -73,7 +80,11 @@ template<ResultCode func(u32*)> void Wrap(){
73} 80}
74 81
75template<ResultCode func(u32, s64)> void Wrap() { 82template<ResultCode func(u32, s64)> void Wrap() {
76 FuncReturn(func(PARAM(0), (((s64)PARAM(3) << 32) | PARAM(2))).raw); 83 s32 retval = func(PARAM(0), (((s64)PARAM(3) << 32) | PARAM(2))).raw;
84
85 if (retval != RESULT_INVALID.raw) {
86 FuncReturn(retval);
87 }
77} 88}
78 89
79template<ResultCode func(void*, void*, u32)> void Wrap(){ 90template<ResultCode func(void*, void*, u32)> void Wrap(){
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
index e45deb1c6..f338f3266 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/event.cpp
@@ -41,10 +41,7 @@ void Event::Acquire() {
41 41
42void Event::Signal() { 42void Event::Signal() {
43 signaled = true; 43 signaled = true;
44
45 WakeupAllWaitingThreads(); 44 WakeupAllWaitingThreads();
46
47 HLE::Reschedule(__func__);
48} 45}
49 46
50void Event::Clear() { 47void Event::Clear() {
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 726e4d2ff..20e11da16 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -32,27 +32,13 @@ void WaitObject::RemoveWaitingThread(Thread* thread) {
32 waiting_threads.erase(itr); 32 waiting_threads.erase(itr);
33} 33}
34 34
35SharedPtr<Thread> WaitObject::WakeupNextThread() {
36 if (waiting_threads.empty())
37 return nullptr;
38
39 auto next_thread = std::move(waiting_threads.front());
40 waiting_threads.erase(waiting_threads.begin());
41
42 next_thread->ReleaseWaitObject(this);
43
44 return next_thread;
45}
46
47void WaitObject::WakeupAllWaitingThreads() { 35void WaitObject::WakeupAllWaitingThreads() {
48 auto waiting_threads_copy = waiting_threads; 36 for (auto thread : waiting_threads)
37 thread->ResumeFromWait();
49 38
50 // We use a copy because ReleaseWaitObject will remove the thread from this object's 39 waiting_threads.clear();
51 // waiting_threads list
52 for (auto thread : waiting_threads_copy)
53 thread->ReleaseWaitObject(this);
54 40
55 ASSERT_MSG(waiting_threads.empty(), "failed to awaken all waiting threads!"); 41 HLE::Reschedule(__func__);
56} 42}
57 43
58HandleTable::HandleTable() { 44HandleTable::HandleTable() {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index a5a0f4800..64595f758 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -140,12 +140,6 @@ public:
140 */ 140 */
141 void RemoveWaitingThread(Thread* thread); 141 void RemoveWaitingThread(Thread* thread);
142 142
143 /**
144 * Wake up the next thread waiting on this object
145 * @return Pointer to the thread that was resumed, nullptr if no threads are waiting
146 */
147 SharedPtr<Thread> WakeupNextThread();
148
149 /// Wake up all threads waiting on this object 143 /// Wake up all threads waiting on this object
150 void WakeupAllWaitingThreads(); 144 void WakeupAllWaitingThreads();
151 145
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 6aa73df86..edb97d324 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -23,12 +23,7 @@ static void ResumeWaitingThread(Mutex* mutex) {
23 // Reset mutex lock thread handle, nothing is waiting 23 // Reset mutex lock thread handle, nothing is waiting
24 mutex->lock_count = 0; 24 mutex->lock_count = 0;
25 mutex->holding_thread = nullptr; 25 mutex->holding_thread = nullptr;
26 26 mutex->WakeupAllWaitingThreads();
27 // Find the next waiting thread for the mutex...
28 auto next_thread = mutex->WakeupNextThread();
29 if (next_thread != nullptr) {
30 mutex->Acquire(next_thread);
31 }
32} 27}
33 28
34void ReleaseThreadMutexes(Thread* thread) { 29void ReleaseThreadMutexes(Thread* thread) {
@@ -94,8 +89,6 @@ void Mutex::Release() {
94 ResumeWaitingThread(this); 89 ResumeWaitingThread(this);
95 } 90 }
96 } 91 }
97
98 HLE::Reschedule(__func__);
99} 92}
100 93
101} // namespace 94} // namespace
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp
index 96d61ed3a..4b359ed07 100644
--- a/src/core/hle/kernel/semaphore.cpp
+++ b/src/core/hle/kernel/semaphore.cpp
@@ -48,13 +48,7 @@ ResultVal<s32> Semaphore::Release(s32 release_count) {
48 s32 previous_count = available_count; 48 s32 previous_count = available_count;
49 available_count += release_count; 49 available_count += release_count;
50 50
51 // Notify some of the threads that the semaphore has been released 51 WakeupAllWaitingThreads();
52 // stop once the semaphore is full again or there are no more waiting threads
53 while (!ShouldWait() && WakeupNextThread() != nullptr) {
54 Acquire();
55 }
56
57 HLE::Reschedule(__func__);
58 52
59 return MakeResult<s32>(previous_count); 53 return MakeResult<s32>(previous_count);
60} 54}
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 22c795ad4..4729a7fe0 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -13,6 +13,7 @@
13#include "common/thread_queue_list.h" 13#include "common/thread_queue_list.h"
14 14
15#include "core/arm/arm_interface.h" 15#include "core/arm/arm_interface.h"
16#include "core/arm/skyeye_common/armdefs.h"
16#include "core/core.h" 17#include "core/core.h"
17#include "core/core_timing.h" 18#include "core/core_timing.h"
18#include "core/hle/hle.h" 19#include "core/hle/hle.h"
@@ -193,8 +194,22 @@ static void SwitchContext(Thread* new_thread) {
193 if (new_thread) { 194 if (new_thread) {
194 DEBUG_ASSERT_MSG(new_thread->status == THREADSTATUS_READY, "Thread must be ready to become running."); 195 DEBUG_ASSERT_MSG(new_thread->status == THREADSTATUS_READY, "Thread must be ready to become running.");
195 196
197 // Cancel any outstanding wakeup events for this thread
198 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle);
199
196 current_thread = new_thread; 200 current_thread = new_thread;
197 201
202 // If the thread was waited by a svcWaitSynch call, step back PC by one instruction to rerun
203 // the SVC when the thread wakes up. This is necessary to ensure that the thread can acquire
204 // the requested wait object(s) before continuing.
205 if (new_thread->waitsynch_waited) {
206 // CPSR flag indicates CPU mode
207 bool thumb_mode = (new_thread->context.cpsr & TBIT) != 0;
208
209 // SVC instruction is 2 bytes for THUMB, 4 bytes for ARM
210 new_thread->context.pc -= thumb_mode ? 2 : 4;
211 }
212
198 ready_queue.remove(new_thread->current_priority, new_thread); 213 ready_queue.remove(new_thread->current_priority, new_thread);
199 new_thread->status = THREADSTATUS_RUNNING; 214 new_thread->status = THREADSTATUS_RUNNING;
200 215
@@ -243,6 +258,7 @@ void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wa
243 thread->wait_set_output = wait_set_output; 258 thread->wait_set_output = wait_set_output;
244 thread->wait_all = wait_all; 259 thread->wait_all = wait_all;
245 thread->wait_objects = std::move(wait_objects); 260 thread->wait_objects = std::move(wait_objects);
261 thread->waitsynch_waited = true;
246 thread->status = THREADSTATUS_WAIT_SYNCH; 262 thread->status = THREADSTATUS_WAIT_SYNCH;
247} 263}
248 264
@@ -268,6 +284,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
268 return; 284 return;
269 } 285 }
270 286
287 thread->waitsynch_waited = false;
288
271 if (thread->status == THREADSTATUS_WAIT_SYNCH) { 289 if (thread->status == THREADSTATUS_WAIT_SYNCH) {
272 thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, 290 thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS,
273 ErrorSummary::StatusChanged, ErrorLevel::Info)); 291 ErrorSummary::StatusChanged, ErrorLevel::Info));
@@ -288,63 +306,20 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
288 CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, callback_handle); 306 CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, callback_handle);
289} 307}
290 308
291void Thread::ReleaseWaitObject(WaitObject* wait_object) {
292 if (status != THREADSTATUS_WAIT_SYNCH || wait_objects.empty()) {
293 LOG_CRITICAL(Kernel, "thread is not waiting on any objects!");
294 return;
295 }
296
297 // Remove this thread from the waiting object's thread list
298 wait_object->RemoveWaitingThread(this);
299
300 unsigned index = 0;
301 bool wait_all_failed = false; // Will be set to true if any object is unavailable
302
303 // Iterate through all waiting objects to check availability...
304 for (auto itr = wait_objects.begin(); itr != wait_objects.end(); ++itr) {
305 if ((*itr)->ShouldWait())
306 wait_all_failed = true;
307
308 // The output should be the last index of wait_object
309 if (*itr == wait_object)
310 index = itr - wait_objects.begin();
311 }
312
313 // If we are waiting on all objects...
314 if (wait_all) {
315 // Resume the thread only if all are available...
316 if (!wait_all_failed) {
317 SetWaitSynchronizationResult(RESULT_SUCCESS);
318 SetWaitSynchronizationOutput(-1);
319
320 ResumeFromWait();
321 }
322 } else {
323 // Otherwise, resume
324 SetWaitSynchronizationResult(RESULT_SUCCESS);
325
326 if (wait_set_output)
327 SetWaitSynchronizationOutput(index);
328
329 ResumeFromWait();
330 }
331}
332
333void Thread::ResumeFromWait() { 309void Thread::ResumeFromWait() {
334 // Cancel any outstanding wakeup events for this thread
335 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
336
337 switch (status) { 310 switch (status) {
338 case THREADSTATUS_WAIT_SYNCH: 311 case THREADSTATUS_WAIT_SYNCH:
339 // Remove this thread from all other WaitObjects
340 for (auto wait_object : wait_objects)
341 wait_object->RemoveWaitingThread(this);
342 break;
343 case THREADSTATUS_WAIT_ARB: 312 case THREADSTATUS_WAIT_ARB:
344 case THREADSTATUS_WAIT_SLEEP: 313 case THREADSTATUS_WAIT_SLEEP:
345 break; 314 break;
346 case THREADSTATUS_RUNNING: 315
347 case THREADSTATUS_READY: 316 case THREADSTATUS_READY:
317 // If the thread is waiting on multiple wait objects, it might be awoken more than once
318 // before actually resuming. We can ignore subsequent wakeups if the thread status has
319 // already been set to THREADSTATUS_READY.
320 return;
321
322 case THREADSTATUS_RUNNING:
348 DEBUG_ASSERT_MSG(false, "Thread with object id %u has already resumed.", GetObjectId()); 323 DEBUG_ASSERT_MSG(false, "Thread with object id %u has already resumed.", GetObjectId());
349 return; 324 return;
350 case THREADSTATUS_DEAD: 325 case THREADSTATUS_DEAD:
@@ -415,6 +390,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
415 thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); 390 thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom();
416 thread->owner_process = g_current_process; 391 thread->owner_process = g_current_process;
417 thread->tls_index = -1; 392 thread->tls_index = -1;
393 thread->waitsynch_waited = false;
418 394
419 // Find the next available TLS index, and mark it as used 395 // Find the next available TLS index, and mark it as used
420 auto& used_tls_slots = Kernel::g_current_process->used_tls_slots; 396 auto& used_tls_slots = Kernel::g_current_process->used_tls_slots;
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 2c65419c3..b8160bb2c 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -96,12 +96,6 @@ public:
96 u32 GetThreadId() const { return thread_id; } 96 u32 GetThreadId() const { return thread_id; }
97 97
98 /** 98 /**
99 * Release an acquired wait object
100 * @param wait_object WaitObject to release
101 */
102 void ReleaseWaitObject(WaitObject* wait_object);
103
104 /**
105 * Resumes a thread from waiting 99 * Resumes a thread from waiting
106 */ 100 */
107 void ResumeFromWait(); 101 void ResumeFromWait();
@@ -152,6 +146,8 @@ public:
152 146
153 s32 tls_index; ///< Index of the Thread Local Storage of the thread 147 s32 tls_index; ///< Index of the Thread Local Storage of the thread
154 148
149 bool waitsynch_waited; ///< Set to true if the last svcWaitSynch call caused the thread to wait
150
155 /// Mutexes currently held by this thread, which will be released when it exits. 151 /// Mutexes currently held by this thread, which will be released when it exits.
156 boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; 152 boost::container::flat_set<SharedPtr<Mutex>> held_mutexes;
157 153
@@ -163,12 +159,12 @@ public:
163 159
164 std::string name; 160 std::string name;
165 161
162 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
163 Handle callback_handle;
164
166private: 165private:
167 Thread(); 166 Thread();
168 ~Thread() override; 167 ~Thread() override;
169
170 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
171 Handle callback_handle;
172}; 168};
173 169
174/** 170/**
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index d1555c753..6cde4fc87 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -40,9 +40,6 @@ const ResultCode ERR_NOT_FOUND(ErrorDescription::NotFound, ErrorModule::Kernel,
40const ResultCode ERR_PORT_NAME_TOO_LONG(ErrorDescription(30), ErrorModule::OS, 40const ResultCode ERR_PORT_NAME_TOO_LONG(ErrorDescription(30), ErrorModule::OS,
41 ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E0181E 41 ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E0181E
42 42
43/// An invalid result code that is meant to be overwritten when a thread resumes from waiting
44const ResultCode RESULT_INVALID(0xDEADC0DE);
45
46enum ControlMemoryOperation { 43enum ControlMemoryOperation {
47 MEMORY_OPERATION_HEAP = 0x00000003, 44 MEMORY_OPERATION_HEAP = 0x00000003,
48 MEMORY_OPERATION_GSP_HEAP = 0x00010003, 45 MEMORY_OPERATION_GSP_HEAP = 0x00010003,
@@ -143,6 +140,10 @@ static ResultCode CloseHandle(Handle handle) {
143/// Wait for a handle to synchronize, timeout after the specified nanoseconds 140/// Wait for a handle to synchronize, timeout after the specified nanoseconds
144static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { 141static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) {
145 auto object = Kernel::g_handle_table.GetWaitObject(handle); 142 auto object = Kernel::g_handle_table.GetWaitObject(handle);
143 Kernel::Thread* thread = Kernel::GetCurrentThread();
144
145 thread->waitsynch_waited = false;
146
146 if (object == nullptr) 147 if (object == nullptr)
147 return ERR_INVALID_HANDLE; 148 return ERR_INVALID_HANDLE;
148 149
@@ -154,14 +155,14 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) {
154 // Check for next thread to schedule 155 // Check for next thread to schedule
155 if (object->ShouldWait()) { 156 if (object->ShouldWait()) {
156 157
157 object->AddWaitingThread(Kernel::GetCurrentThread()); 158 object->AddWaitingThread(thread);
158 Kernel::WaitCurrentThread_WaitSynchronization({ object }, false, false); 159 Kernel::WaitCurrentThread_WaitSynchronization({ object }, false, false);
159 160
160 // Create an event to wake the thread up after the specified nanosecond delay has passed 161 // Create an event to wake the thread up after the specified nanosecond delay has passed
161 Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); 162 thread->WakeAfterDelay(nano_seconds);
162 163
163 // NOTE: output of this SVC will be set later depending on how the thread resumes 164 // NOTE: output of this SVC will be set later depending on how the thread resumes
164 return RESULT_INVALID; 165 return HLE::RESULT_INVALID;
165 } 166 }
166 167
167 object->Acquire(); 168 object->Acquire();
@@ -173,6 +174,9 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) {
173static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, s64 nano_seconds) { 174static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, s64 nano_seconds) {
174 bool wait_thread = !wait_all; 175 bool wait_thread = !wait_all;
175 int handle_index = 0; 176 int handle_index = 0;
177 Kernel::Thread* thread = Kernel::GetCurrentThread();
178 bool was_waiting = thread->waitsynch_waited;
179 thread->waitsynch_waited = false;
176 180
177 // Check if 'handles' is invalid 181 // Check if 'handles' is invalid
178 if (handles == nullptr) 182 if (handles == nullptr)
@@ -190,6 +194,9 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou
190 // necessary 194 // necessary
191 if (handle_count != 0) { 195 if (handle_count != 0) {
192 bool selected = false; // True once an object has been selected 196 bool selected = false; // True once an object has been selected
197
198 Kernel::SharedPtr<Kernel::WaitObject> wait_object;
199
193 for (int i = 0; i < handle_count; ++i) { 200 for (int i = 0; i < handle_count; ++i) {
194 auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); 201 auto object = Kernel::g_handle_table.GetWaitObject(handles[i]);
195 if (object == nullptr) 202 if (object == nullptr)
@@ -204,10 +211,11 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou
204 wait_thread = true; 211 wait_thread = true;
205 } else { 212 } else {
206 // Do not wait on this object, check if this object should be selected... 213 // Do not wait on this object, check if this object should be selected...
207 if (!wait_all && !selected) { 214 if (!wait_all && (!selected || (wait_object == object && was_waiting))) {
208 // Do not wait the thread 215 // Do not wait the thread
209 wait_thread = false; 216 wait_thread = false;
210 handle_index = i; 217 handle_index = i;
218 wait_object = object;
211 selected = true; 219 selected = true;
212 } 220 }
213 } 221 }
@@ -241,7 +249,7 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou
241 Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); 249 Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds);
242 250
243 // NOTE: output of this SVC will be set later depending on how the thread resumes 251 // NOTE: output of this SVC will be set later depending on how the thread resumes
244 return RESULT_INVALID; 252 return HLE::RESULT_INVALID;
245 } 253 }
246 254
247 // Acquire objects if we did not wait... 255 // Acquire objects if we did not wait...
@@ -261,7 +269,7 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou
261 269
262 // TODO(bunnei): If 'wait_all' is true, this is probably wrong. However, real hardware does 270 // TODO(bunnei): If 'wait_all' is true, this is probably wrong. However, real hardware does
263 // not seem to set it to any meaningful value. 271 // not seem to set it to any meaningful value.
264 *out = wait_all ? 0 : handle_index; 272 *out = handle_count != 0 ? (wait_all ? -1 : handle_index) : 0;
265 273
266 return RESULT_SUCCESS; 274 return RESULT_SUCCESS;
267} 275}