summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar bunnei2015-01-18 01:27:46 -0500
committerGravatar bunnei2015-01-21 19:11:47 -0500
commit6643673f28b9273149fc945849a13ed832e9ef33 (patch)
tree026410cf5b0b57d350ac979380307991646e546c
parentKernel: Separate WaitSynchronization into Wait and Acquire methods. (diff)
downloadyuzu-6643673f28b9273149fc945849a13ed832e9ef33.tar.gz
yuzu-6643673f28b9273149fc945849a13ed832e9ef33.tar.xz
yuzu-6643673f28b9273149fc945849a13ed832e9ef33.zip
WaitSynchronizationN: Refactor to fix several bugs
- Separate wait checking from waiting the current thread - Resume thread when wait_all=true only if all objects are available at once - Set output to correct wait object index when there are duplicate handles
-rw-r--r--src/core/hle/kernel/event.cpp6
-rw-r--r--src/core/hle/kernel/kernel.h12
-rw-r--r--src/core/hle/kernel/mutex.cpp8
-rw-r--r--src/core/hle/kernel/semaphore.cpp6
-rw-r--r--src/core/hle/kernel/thread.cpp61
-rw-r--r--src/core/hle/kernel/thread.h4
-rw-r--r--src/core/hle/kernel/timer.cpp6
-rw-r--r--src/core/hle/svc.cpp52
8 files changed, 76 insertions, 79 deletions
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
index 41e1bd6c5..ae9b06b84 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/event.cpp
@@ -28,11 +28,11 @@ public:
28 bool signaled; ///< Whether the event has already been signaled 28 bool signaled; ///< Whether the event has already been signaled
29 std::string name; ///< Name of event (optional) 29 std::string name; ///< Name of event (optional)
30 30
31 ResultVal<bool> Wait(unsigned index) override { 31 ResultVal<bool> Wait(bool wait_thread) override {
32 bool wait = !signaled; 32 bool wait = !signaled;
33 if (wait) { 33 if (wait && wait_thread) {
34 AddWaitingThread(GetCurrentThread()); 34 AddWaitingThread(GetCurrentThread());
35 Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_EVENT, this, index); 35 Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_EVENT, this);
36 } 36 }
37 return MakeResult<bool>(wait); 37 return MakeResult<bool>(wait);
38 } 38 }
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index d98fd0389..cfaf0c901 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -65,18 +65,18 @@ public:
65 virtual Kernel::HandleType GetHandleType() const = 0; 65 virtual Kernel::HandleType GetHandleType() const = 0;
66 66
67 /** 67 /**
68 * Wait the current thread for kernel object to synchronize. 68 * Check if this object is available, (optionally) wait the current thread if not
69 * @param index Index of wait object (only applies to WaitSynchronizationN) 69 * @param wait_thread If true, wait the current thread if this object is unavailable
70 * @return True if the current thread should wait as a result of the wait 70 * @return True if the current thread should wait due to this object being unavailable
71 */ 71 */
72 virtual ResultVal<bool> Wait(unsigned index = 0) { 72 virtual ResultVal<bool> Wait(bool wait_thread) {
73 LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); 73 LOG_ERROR(Kernel, "(UNIMPLEMENTED)");
74 return UnimplementedFunction(ErrorModule::Kernel); 74 return UnimplementedFunction(ErrorModule::Kernel);
75 } 75 }
76 76
77 /** 77 /**
78 * Acquire/lock the kernel object if it is available 78 * Acquire/lock the this object if it is available
79 * @return True if we were able to acquire the kernel object, otherwise false 79 * @return True if we were able to acquire this object, otherwise false
80 */ 80 */
81 virtual ResultVal<bool> Acquire() { 81 virtual ResultVal<bool> Acquire() {
82 LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); 82 LOG_ERROR(Kernel, "(UNIMPLEMENTED)");
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 37e7be4e7..f97c69a78 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -26,7 +26,7 @@ public:
26 Handle lock_thread; ///< Handle to thread that currently has mutex 26 Handle lock_thread; ///< Handle to thread that currently has mutex
27 std::string name; ///< Name of mutex (optional) 27 std::string name; ///< Name of mutex (optional)
28 28
29 ResultVal<bool> Wait(unsigned index) override; 29 ResultVal<bool> Wait(bool wait_thread) override;
30 ResultVal<bool> Acquire() override; 30 ResultVal<bool> Acquire() override;
31}; 31};
32 32
@@ -156,10 +156,10 @@ Handle CreateMutex(bool initial_locked, const std::string& name) {
156 return handle; 156 return handle;
157} 157}
158 158
159ResultVal<bool> Mutex::Wait(unsigned index) { 159ResultVal<bool> Mutex::Wait(bool wait_thread) {
160 if (locked) { 160 if (locked && wait_thread) {
161 AddWaitingThread(GetCurrentThread()); 161 AddWaitingThread(GetCurrentThread());
162 Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_MUTEX, this, index); 162 Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_MUTEX, this);
163 } 163 }
164 164
165 return MakeResult<bool>(locked); 165 return MakeResult<bool>(locked);
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp
index 6464b2580..42b5cf704 100644
--- a/src/core/hle/kernel/semaphore.cpp
+++ b/src/core/hle/kernel/semaphore.cpp
@@ -32,11 +32,11 @@ public:
32 return available_count > 0; 32 return available_count > 0;
33 } 33 }
34 34
35 ResultVal<bool> Wait(unsigned index) override { 35 ResultVal<bool> Wait(bool wait_thread) override {
36 bool wait = !IsAvailable(); 36 bool wait = !IsAvailable();
37 37
38 if (wait) { 38 if (wait && wait_thread) {
39 Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_SEMA, this, index); 39 Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_SEMA, this);
40 AddWaitingThread(GetCurrentThread()); 40 AddWaitingThread(GetCurrentThread());
41 } 41 }
42 42
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 6b0bdebb5..62b85f56a 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -22,11 +22,11 @@
22 22
23namespace Kernel { 23namespace Kernel {
24 24
25ResultVal<bool> Thread::Wait(unsigned index) { 25ResultVal<bool> Thread::Wait(bool wait_thread) {
26 const bool wait = status != THREADSTATUS_DORMANT; 26 const bool wait = status != THREADSTATUS_DORMANT;
27 if (wait) { 27 if (wait && wait_thread) {
28 AddWaitingThread(GetCurrentThread()); 28 AddWaitingThread(GetCurrentThread());
29 WaitCurrentThread_WaitSynchronization(WAITTYPE_THREADEND, this, index); 29 WaitCurrentThread_WaitSynchronization(WAITTYPE_THREADEND, this);
30 } 30 }
31 31
32 return MakeResult<bool>(wait); 32 return MakeResult<bool>(wait);
@@ -97,7 +97,7 @@ static bool CheckWaitType(const Thread* thread, WaitType type) {
97/// Check if a thread is blocking on a specified wait type with a specified handle 97/// Check if a thread is blocking on a specified wait type with a specified handle
98static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object) { 98static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object) {
99 for (auto itr = thread->wait_objects.begin(); itr != thread->wait_objects.end(); ++itr) { 99 for (auto itr = thread->wait_objects.begin(); itr != thread->wait_objects.end(); ++itr) {
100 if (itr->first == wait_object) 100 if (*itr == wait_object)
101 return CheckWaitType(thread, type); 101 return CheckWaitType(thread, type);
102 } 102 }
103 return false; 103 return false;
@@ -234,16 +234,7 @@ void WaitCurrentThread_WaitSynchronization(WaitType wait_type, WaitObject* wait_
234 Thread* thread = GetCurrentThread(); 234 Thread* thread = GetCurrentThread();
235 thread->wait_type = wait_type; 235 thread->wait_type = wait_type;
236 236
237 bool insert_wait_object = true; 237 thread->wait_objects.push_back(wait_object);
238 for (auto itr = thread->wait_objects.begin(); itr < thread->wait_objects.end(); ++itr) {
239 if (itr->first == wait_object) {
240 insert_wait_object = false;
241 break;
242 }
243 }
244
245 if (insert_wait_object)
246 thread->wait_objects.push_back(std::pair<SharedPtr<WaitObject>, unsigned>(wait_object, index));
247 238
248 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); 239 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
249} 240}
@@ -288,31 +279,35 @@ void Thread::ReleaseFromWait(WaitObject* wait_object) {
288 return; 279 return;
289 } 280 }
290 281
291 // Remove this thread from the wait_object 282 // Remove this thread from the waiting object's thread list
292 wait_object->RemoveWaitingThread(this); 283 wait_object->RemoveWaitingThread(this);
293 284
294 // Find the waiting object 285 unsigned index = 0;
295 auto itr = wait_objects.begin(); 286 bool wait_all_failed = false; // Will be set to true if any object is unavailable
296 for (; itr != wait_objects.end(); ++itr) {
297 if (wait_object == itr->first)
298 break;
299 }
300 unsigned index = itr->second;
301 287
302 // Remove the wait_object from this thread 288 // Iterate through all waiting objects to check availability...
303 if (itr != wait_objects.end()) 289 for (auto itr = wait_objects.begin(); itr != wait_objects.end(); ++itr) {
304 wait_objects.erase(itr); 290 auto res = (*itr)->Wait(false);
305 291
306 // If wait_all=false, resume the thread on a release wait_object from wait 292 if (*res && res.Succeeded())
307 if (!wait_all) { 293 wait_all_failed = true;
308 SetReturnValue(RESULT_SUCCESS, index); 294
309 ResumeFromWait(); 295 // The output should be the last index of wait_object
310 } else { 296 if (*itr == wait_object)
311 // Otherwise, wait_all=true, only resume the thread if all wait_object's have been released 297 index = itr - wait_objects.begin();
312 if (wait_objects.empty()) { 298 }
299
300 // If we are waiting on all objects...
301 if (wait_all) {
302 // Resume the thread only if all are available...
303 if (!wait_all_failed) {
313 SetReturnValue(RESULT_SUCCESS, -1); 304 SetReturnValue(RESULT_SUCCESS, -1);
314 ResumeFromWait(); 305 ResumeFromWait();
315 } 306 }
307 } else {
308 // Otherwise, resume
309 SetReturnValue(RESULT_SUCCESS, index);
310 ResumeFromWait();
316 } 311 }
317} 312}
318 313
@@ -324,7 +319,7 @@ void Thread::ResumeFromWait() {
324 319
325 // Remove this thread from all other WaitObjects 320 // Remove this thread from all other WaitObjects
326 for (auto wait_object : wait_objects) 321 for (auto wait_object : wait_objects)
327 wait_object.first->RemoveWaitingThread(this); 322 wait_object->RemoveWaitingThread(this);
328 323
329 wait_objects.clear(); 324 wait_objects.clear();
330 325
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 9faf89c15..dff6bbaec 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -70,7 +70,7 @@ public:
70 inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } 70 inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
71 inline bool IsIdle() const { return idle; } 71 inline bool IsIdle() const { return idle; }
72 72
73 ResultVal<bool> Wait(unsigned index) override; 73 ResultVal<bool> Wait(bool wait_thread) override;
74 ResultVal<bool> Acquire() override; 74 ResultVal<bool> Acquire() override;
75 75
76 s32 GetPriority() const { return current_priority; } 76 s32 GetPriority() const { return current_priority; }
@@ -117,7 +117,7 @@ public:
117 s32 processor_id; 117 s32 processor_id;
118 118
119 WaitType wait_type; 119 WaitType wait_type;
120 std::vector<std::pair<SharedPtr<WaitObject>, unsigned>> wait_objects; 120 std::vector<SharedPtr<WaitObject>> wait_objects;
121 VAddr wait_address; 121 VAddr wait_address;
122 122
123 std::string name; 123 std::string name;
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp
index 6497bb349..9f0fbafe2 100644
--- a/src/core/hle/kernel/timer.cpp
+++ b/src/core/hle/kernel/timer.cpp
@@ -29,11 +29,11 @@ public:
29 u64 initial_delay; ///< The delay until the timer fires for the first time 29 u64 initial_delay; ///< The delay until the timer fires for the first time
30 u64 interval_delay; ///< The delay until the timer fires after the first time 30 u64 interval_delay; ///< The delay until the timer fires after the first time
31 31
32 ResultVal<bool> Wait(unsigned index) override { 32 ResultVal<bool> Wait(bool wait_thread) override {
33 bool wait = !signaled; 33 bool wait = !signaled;
34 if (wait) { 34 if (wait && wait_thread) {
35 AddWaitingThread(GetCurrentThread()); 35 AddWaitingThread(GetCurrentThread());
36 Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_TIMER, this, index); 36 Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_TIMER, this);
37 } 37 }
38 return MakeResult<bool>(wait); 38 return MakeResult<bool>(wait);
39 } 39 }
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index a27aa6269..059451100 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -127,7 +127,7 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
127 LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, 127 LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle,
128 object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); 128 object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds);
129 129
130 ResultVal<bool> wait = object->Wait(); 130 ResultVal<bool> wait = object->Wait(true);
131 131
132 // Check for next thread to schedule 132 // Check for next thread to schedule
133 if (wait.Succeeded() && *wait) { 133 if (wait.Succeeded() && *wait) {
@@ -146,8 +146,7 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
146 146
147/// Wait for the given handles to synchronize, timeout after the specified nanoseconds 147/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
148static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, s64 nano_seconds) { 148static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, s64 nano_seconds) {
149 bool wait_thread = false; 149 bool wait_thread = !wait_all;
150 bool wait_all_succeeded = false;
151 int handle_index = 0; 150 int handle_index = 0;
152 151
153 // Handles pointer is invalid 152 // Handles pointer is invalid
@@ -158,40 +157,43 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count,
158 if (handle_count < 0) 157 if (handle_count < 0)
159 return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage).raw; 158 return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage).raw;
160 159
161 // If handle_count is non-zero, iterate through them and wait/acquire the objects as needed 160 // If handle_count is non-zero, iterate through them and wait the current thread on the objects
162 if (handle_count != 0) { 161 if (handle_count != 0) {
163 while (handle_index < handle_count) { 162 bool selected = false; // True once an object has been selected
164 SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handles[handle_index]); 163 for (int i = 0; i < handle_count; ++i) {
164 SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handles[i]);
165 if (object == nullptr) 165 if (object == nullptr)
166 return InvalidHandle(ErrorModule::Kernel).raw; 166 return InvalidHandle(ErrorModule::Kernel).raw;
167 167
168 ResultVal<bool> wait = object->Wait(handle_index); 168 ResultVal<bool> wait = object->Wait(true);
169 169
170 wait_thread = (wait.Succeeded() && *wait); 170 // Check if the current thread should wait on the object...
171 171 if (wait.Succeeded() && *wait) {
172 // If this object waited and we are waiting on all objects to synchronize 172 // Check we are waiting on all objects...
173 if (wait_thread && wait_all) 173 if (wait_all)
174 // Enforce later on that this thread does not continue 174 // Wait the thread
175 wait_all_succeeded = true; 175 wait_thread = true;
176 176 } else {
177 // If this object synchronized and we are not waiting on all objects to synchronize 177 // Do not wait on this object, check if this object should be selected...
178 if (!wait_thread && !wait_all) 178 if (!wait_all && !selected) {
179 // We're done, the thread will continue 179 // Do not wait the thread
180 break; 180 wait_thread = false;
181 181 handle_index = i;
182 handle_index++; 182 selected = true;
183 }
184 }
183 } 185 }
184 } else { 186 } else {
185 // If no handles were passed in, put the thread to sleep only when wait_all=false 187 // If no handles were passed in, put the thread to sleep only when wait_all=false
186 // NOTE: This is supposed to deadlock if no timeout was specified 188 // NOTE: This is supposed to deadlock the current thread if no timeout was specified
187 if (!wait_all) { 189 if (!wait_all) {
188 wait_thread = true; 190 wait_thread = true;
189 Kernel::WaitCurrentThread(WAITTYPE_SLEEP); 191 Kernel::WaitCurrentThread(WAITTYPE_SLEEP);
190 } 192 }
191 } 193 }
192 194
193 // Change the thread state to waiting if blocking on all handles... 195 // If thread should block, then set its state to waiting and then reschedule...
194 if (wait_thread || wait_all_succeeded) { 196 if (wait_thread) {
195 // Create an event to wake the thread up after the specified nanosecond delay has passed 197 // Create an event to wake the thread up after the specified nanosecond delay has passed
196 Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nano_seconds); 198 Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nano_seconds);
197 Kernel::GetCurrentThread()->SetWaitAll(wait_all); 199 Kernel::GetCurrentThread()->SetWaitAll(wait_all);
@@ -199,7 +201,7 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count,
199 HLE::Reschedule(__func__); 201 HLE::Reschedule(__func__);
200 202
201 // NOTE: output of this SVC will be set later depending on how the thread resumes 203 // NOTE: output of this SVC will be set later depending on how the thread resumes
202 return RESULT_DUMMY.raw; 204 return 0xDEADBEEF;
203 } 205 }
204 206
205 // Acquire objects if we did not wait... 207 // Acquire objects if we did not wait...