summaryrefslogtreecommitdiff
path: root/src/core/hle/svc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/svc.cpp')
-rw-r--r--src/core/hle/svc.cpp181
1 files changed, 103 insertions, 78 deletions
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index c6b80dc50..061692af8 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -249,27 +249,30 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) {
249 auto object = Kernel::g_handle_table.GetWaitObject(handle); 249 auto object = Kernel::g_handle_table.GetWaitObject(handle);
250 Kernel::Thread* thread = Kernel::GetCurrentThread(); 250 Kernel::Thread* thread = Kernel::GetCurrentThread();
251 251
252 thread->waitsynch_waited = false;
253
254 if (object == nullptr) 252 if (object == nullptr)
255 return ERR_INVALID_HANDLE; 253 return ERR_INVALID_HANDLE;
256 254
257 LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, 255 LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle,
258 object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); 256 object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds);
259 257
260 HLE::Reschedule(__func__);
261
262 // Check for next thread to schedule
263 if (object->ShouldWait()) { 258 if (object->ShouldWait()) {
264 259
260 if (nano_seconds == 0)
261 return ResultCode(ErrorDescription::Timeout, ErrorModule::OS,
262 ErrorSummary::StatusChanged,
263 ErrorLevel::Info);
264
265 object->AddWaitingThread(thread); 265 object->AddWaitingThread(thread);
266 Kernel::WaitCurrentThread_WaitSynchronization({object}, false, false); 266 thread->status = THREADSTATUS_WAIT_SYNCH;
267 267
268 // Create an event to wake the thread up after the specified nanosecond delay has passed 268 // Create an event to wake the thread up after the specified nanosecond delay has passed
269 thread->WakeAfterDelay(nano_seconds); 269 thread->WakeAfterDelay(nano_seconds);
270 270
271 // NOTE: output of this SVC will be set later depending on how the thread resumes 271 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a signal in one of its wait objects.
272 return HLE::RESULT_INVALID; 272 // Otherwise we retain the default value of timeout.
273 return ResultCode(ErrorDescription::Timeout, ErrorModule::OS,
274 ErrorSummary::StatusChanged,
275 ErrorLevel::Info);
273 } 276 }
274 277
275 object->Acquire(); 278 object->Acquire();
@@ -283,8 +286,6 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou
283 bool wait_thread = !wait_all; 286 bool wait_thread = !wait_all;
284 int handle_index = 0; 287 int handle_index = 0;
285 Kernel::Thread* thread = Kernel::GetCurrentThread(); 288 Kernel::Thread* thread = Kernel::GetCurrentThread();
286 bool was_waiting = thread->waitsynch_waited;
287 thread->waitsynch_waited = false;
288 289
289 // Check if 'handles' is invalid 290 // Check if 'handles' is invalid
290 if (handles == nullptr) 291 if (handles == nullptr)
@@ -300,90 +301,113 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou
300 return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, 301 return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS,
301 ErrorSummary::InvalidArgument, ErrorLevel::Usage); 302 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
302 303
303 // If 'handle_count' is non-zero, iterate through each handle and wait the current thread if 304 using ObjectPtr = Kernel::SharedPtr<Kernel::WaitObject>;
304 // necessary 305
305 if (handle_count != 0) { 306 std::vector<ObjectPtr> objects(handle_count);
306 bool selected = false; // True once an object has been selected 307
307 308 for (int i = 0; i < handle_count; ++i) {
308 Kernel::SharedPtr<Kernel::WaitObject> wait_object; 309 auto object = Kernel::g_handle_table.GetWaitObject(handles[i]);
309 310 if (object == nullptr)
310 for (int i = 0; i < handle_count; ++i) { 311 return ERR_INVALID_HANDLE;
311 auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); 312 objects[i] = object;
312 if (object == nullptr)
313 return ERR_INVALID_HANDLE;
314
315 // Check if the current thread should wait on this object...
316 if (object->ShouldWait()) {
317
318 // Check we are waiting on all objects...
319 if (wait_all)
320 // Wait the thread
321 wait_thread = true;
322 } else {
323 // Do not wait on this object, check if this object should be selected...
324 if (!wait_all && (!selected || (wait_object == object && was_waiting))) {
325 // Do not wait the thread
326 wait_thread = false;
327 handle_index = i;
328 wait_object = object;
329 selected = true;
330 }
331 }
332 }
333 } else {
334 // If no handles were passed in, put the thread to sleep only when 'wait_all' is false
335 // NOTE: This should deadlock the current thread if no timeout was specified
336 if (!wait_all) {
337 wait_thread = true;
338 }
339 } 313 }
340 314
341 SCOPE_EXIT({ 315 // Clear the mapping of wait object indices
342 HLE::Reschedule("WaitSynchronizationN"); 316 thread->wait_objects_index.clear();
343 }); // Reschedule after putting the threads to sleep. 317
318 if (!wait_all) {
319 // Find the first object that is acquireable in the provided list of objects
320 auto itr = std::find_if(objects.begin(), objects.end(), [](const ObjectPtr& object) {
321 return !object->ShouldWait();
322 });
323
324 if (itr != objects.end()) {
325 // We found a ready object, acquire it and set the result value
326 ObjectPtr object = *itr;
327 object->Acquire();
328 *out = std::distance(objects.begin(), itr);
329 return RESULT_SUCCESS;
330 }
331
332 // No objects were ready to be acquired, prepare to suspend the thread.
333
334 // If a timeout value of 0 was provided, just return the Timeout error code instead of suspending the thread.
335 if (nano_seconds == 0) {
336 return ResultCode(ErrorDescription::Timeout, ErrorModule::OS,
337 ErrorSummary::StatusChanged,
338 ErrorLevel::Info);
339 }
344 340
345 // If thread should wait, then set its state to waiting 341 // Put the thread to sleep
346 if (wait_thread) { 342 thread->status = THREADSTATUS_WAIT_SYNCH;
347 343
348 // Actually wait the current thread on each object if we decided to wait... 344 // Clear the thread's waitlist, we won't use it for wait_all = false
349 std::vector<SharedPtr<Kernel::WaitObject>> wait_objects; 345 thread->wait_objects.clear();
350 wait_objects.reserve(handle_count);
351 346
352 for (int i = 0; i < handle_count; ++i) { 347 // Add the thread to each of the objects' waiting threads.
353 auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); 348 for (int i = 0; i < objects.size(); ++i) {
354 object->AddWaitingThread(Kernel::GetCurrentThread()); 349 ObjectPtr object = objects[i];
355 wait_objects.push_back(object); 350 // Set the index of this object in the mapping of Objects -> index for this thread.
351 thread->wait_objects_index[object->GetObjectId()] = i;
352 object->AddWaitingThread(thread);
353 // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion.
356 } 354 }
357 355
358 Kernel::WaitCurrentThread_WaitSynchronization(std::move(wait_objects), true, wait_all); 356 // Note: If no handles and no timeout were given, then the thread will deadlock, this is consistent with hardware behavior.
359 357
360 // Create an event to wake the thread up after the specified nanosecond delay has passed 358 // Create an event to wake the thread up after the specified nanosecond delay has passed
361 Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); 359 thread->WakeAfterDelay(nano_seconds);
362 360
363 // NOTE: output of this SVC will be set later depending on how the thread resumes 361 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a signal in one of its wait objects.
364 return HLE::RESULT_INVALID; 362 // Otherwise we retain the default value of timeout, and -1 in the out parameter
365 } 363 thread->wait_set_output = true;
364 *out = -1;
365 return ResultCode(ErrorDescription::Timeout, ErrorModule::OS,
366 ErrorSummary::StatusChanged,
367 ErrorLevel::Info);
368 } else {
369 bool all_available = std::all_of(objects.begin(), objects.end(), [](const ObjectPtr& object) {
370 return !object->ShouldWait();
371 });
372 if (all_available) {
373 // We can acquire all objects right now, do so.
374 for (auto object : objects)
375 object->Acquire();
376 // Note: In this case, the `out` parameter is not set, and retains whatever value it had before.
377 return RESULT_SUCCESS;
378 }
366 379
367 // Acquire objects if we did not wait... 380 // Not all objects were available right now, prepare to suspend the thread.
368 for (int i = 0; i < handle_count; ++i) {
369 auto object = Kernel::g_handle_table.GetWaitObject(handles[i]);
370 381
371 // Acquire the object if it is not waiting... 382 // If a timeout value of 0 was provided, just return the Timeout error code instead of suspending the thread.
372 if (!object->ShouldWait()) { 383 if (nano_seconds == 0) {
373 object->Acquire(); 384 return ResultCode(ErrorDescription::Timeout, ErrorModule::OS,
385 ErrorSummary::StatusChanged,
386 ErrorLevel::Info);
387 }
388
389 // Put the thread to sleep
390 thread->status = THREADSTATUS_WAIT_SYNCH;
374 391
375 // If this was the first non-waiting object and 'wait_all' is false, don't acquire 392 // Set the thread's waitlist to the list of objects passed to WaitSynchronizationN
376 // any other objects 393 thread->wait_objects = objects;
377 if (!wait_all) 394
378 break; 395 // Add the thread to each of the objects' waiting threads.
396 for (auto object : objects) {
397 object->AddWaitingThread(thread);
398 // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion.
379 } 399 }
380 }
381 400
382 // TODO(bunnei): If 'wait_all' is true, this is probably wrong. However, real hardware does 401 // Create an event to wake the thread up after the specified nanosecond delay has passed
383 // not seem to set it to any meaningful value. 402 thread->WakeAfterDelay(nano_seconds);
384 *out = handle_count != 0 ? (wait_all ? -1 : handle_index) : 0;
385 403
386 return RESULT_SUCCESS; 404 // This value gets set to -1 by default in this case, it is not modified after this.
405 *out = -1;
406 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a signal in one of its wait objects.
407 return ResultCode(ErrorDescription::Timeout, ErrorModule::OS,
408 ErrorSummary::StatusChanged,
409 ErrorLevel::Info);
410 }
387} 411}
388 412
389/// Create an address arbiter (to allocate access to shared resources) 413/// Create an address arbiter (to allocate access to shared resources)
@@ -1148,6 +1172,7 @@ void CallSVC(u32 immediate) {
1148 if (info) { 1172 if (info) {
1149 if (info->func) { 1173 if (info->func) {
1150 info->func(); 1174 info->func();
1175 HLE::Reschedule(__func__);
1151 } else { 1176 } else {
1152 LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name); 1177 LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name);
1153 } 1178 }