diff options
| author | 2017-01-05 12:55:01 -0500 | |
|---|---|---|
| committer | 2017-01-05 12:55:01 -0500 | |
| commit | f20d872643654c574f73a263f032613046900f07 (patch) | |
| tree | 021284c18034d053c81928fa19d2efb6658451fb /src/core/hle/svc.cpp | |
| parent | Merge pull request #2407 from jroweboy/nightly-deploy (diff) | |
| parent | Kernel: Add some asserts to enforce the invariants in the scheduler. (diff) | |
| download | yuzu-f20d872643654c574f73a263f032613046900f07.tar.gz yuzu-f20d872643654c574f73a263f032613046900f07.tar.xz yuzu-f20d872643654c574f73a263f032613046900f07.zip | |
Merge pull request #2393 from Subv/synch
Kernel: Mutex priority inheritance and synchronization improvements.
Diffstat (limited to 'src/core/hle/svc.cpp')
| -rw-r--r-- | src/core/hle/svc.cpp | 69 |
1 files changed, 37 insertions, 32 deletions
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 2ca270de3..855f3af82 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -248,6 +248,8 @@ static ResultCode SendSyncRequest(Kernel::Handle handle) { | |||
| 248 | 248 | ||
| 249 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str()); | 249 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str()); |
| 250 | 250 | ||
| 251 | Core::System::GetInstance().PrepareReschedule(); | ||
| 252 | |||
| 251 | // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server | 253 | // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server |
| 252 | // responds and cause a reschedule. | 254 | // responds and cause a reschedule. |
| 253 | return session->SendSyncRequest(); | 255 | return session->SendSyncRequest(); |
| @@ -270,27 +272,27 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) | |||
| 270 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, | 272 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, |
| 271 | object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); | 273 | object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); |
| 272 | 274 | ||
| 273 | if (object->ShouldWait()) { | 275 | if (object->ShouldWait(thread)) { |
| 274 | 276 | ||
| 275 | if (nano_seconds == 0) | 277 | if (nano_seconds == 0) |
| 276 | return ERR_SYNC_TIMEOUT; | 278 | return ERR_SYNC_TIMEOUT; |
| 277 | 279 | ||
| 280 | thread->wait_objects = {object}; | ||
| 278 | object->AddWaitingThread(thread); | 281 | object->AddWaitingThread(thread); |
| 279 | // TODO(Subv): Perform things like update the mutex lock owner's priority to | 282 | thread->status = THREADSTATUS_WAIT_SYNCH_ANY; |
| 280 | // prevent priority inversion. Currently this is done in Mutex::ShouldWait, | ||
| 281 | // but it should be moved to a function that is called from here. | ||
| 282 | thread->status = THREADSTATUS_WAIT_SYNCH; | ||
| 283 | 283 | ||
| 284 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 284 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 285 | thread->WakeAfterDelay(nano_seconds); | 285 | thread->WakeAfterDelay(nano_seconds); |
| 286 | 286 | ||
| 287 | Core::System::GetInstance().PrepareReschedule(); | ||
| 288 | |||
| 287 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread | 289 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread |
| 288 | // resumes due to a signal in its wait objects. | 290 | // resumes due to a signal in its wait objects. |
| 289 | // Otherwise we retain the default value of timeout. | 291 | // Otherwise we retain the default value of timeout. |
| 290 | return ERR_SYNC_TIMEOUT; | 292 | return ERR_SYNC_TIMEOUT; |
| 291 | } | 293 | } |
| 292 | 294 | ||
| 293 | object->Acquire(); | 295 | object->Acquire(thread); |
| 294 | 296 | ||
| 295 | return RESULT_SUCCESS; | 297 | return RESULT_SUCCESS; |
| 296 | } | 298 | } |
| @@ -324,19 +326,14 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha | |||
| 324 | objects[i] = object; | 326 | objects[i] = object; |
| 325 | } | 327 | } |
| 326 | 328 | ||
| 327 | // Clear the mapping of wait object indices. | ||
| 328 | // We don't want any lingering state in this map. | ||
| 329 | // It will be repopulated later in the wait_all = false case. | ||
| 330 | thread->wait_objects_index.clear(); | ||
| 331 | |||
| 332 | if (wait_all) { | 329 | if (wait_all) { |
| 333 | bool all_available = | 330 | bool all_available = |
| 334 | std::all_of(objects.begin(), objects.end(), | 331 | std::all_of(objects.begin(), objects.end(), |
| 335 | [](const ObjectPtr& object) { return !object->ShouldWait(); }); | 332 | [thread](const ObjectPtr& object) { return !object->ShouldWait(thread); }); |
| 336 | if (all_available) { | 333 | if (all_available) { |
| 337 | // We can acquire all objects right now, do so. | 334 | // We can acquire all objects right now, do so. |
| 338 | for (auto& object : objects) | 335 | for (auto& object : objects) |
| 339 | object->Acquire(); | 336 | object->Acquire(thread); |
| 340 | // Note: In this case, the `out` parameter is not set, | 337 | // Note: In this case, the `out` parameter is not set, |
| 341 | // and retains whatever value it had before. | 338 | // and retains whatever value it had before. |
| 342 | return RESULT_SUCCESS; | 339 | return RESULT_SUCCESS; |
| @@ -350,22 +347,20 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha | |||
| 350 | return ERR_SYNC_TIMEOUT; | 347 | return ERR_SYNC_TIMEOUT; |
| 351 | 348 | ||
| 352 | // Put the thread to sleep | 349 | // Put the thread to sleep |
| 353 | thread->status = THREADSTATUS_WAIT_SYNCH; | 350 | thread->status = THREADSTATUS_WAIT_SYNCH_ALL; |
| 354 | 351 | ||
| 355 | // Add the thread to each of the objects' waiting threads. | 352 | // Add the thread to each of the objects' waiting threads. |
| 356 | for (auto& object : objects) { | 353 | for (auto& object : objects) { |
| 357 | object->AddWaitingThread(thread); | 354 | object->AddWaitingThread(thread); |
| 358 | // TODO(Subv): Perform things like update the mutex lock owner's priority to | ||
| 359 | // prevent priority inversion. Currently this is done in Mutex::ShouldWait, | ||
| 360 | // but it should be moved to a function that is called from here. | ||
| 361 | } | 355 | } |
| 362 | 356 | ||
| 363 | // Set the thread's waitlist to the list of objects passed to WaitSynchronizationN | ||
| 364 | thread->wait_objects = std::move(objects); | 357 | thread->wait_objects = std::move(objects); |
| 365 | 358 | ||
| 366 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 359 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 367 | thread->WakeAfterDelay(nano_seconds); | 360 | thread->WakeAfterDelay(nano_seconds); |
| 368 | 361 | ||
| 362 | Core::System::GetInstance().PrepareReschedule(); | ||
| 363 | |||
| 369 | // This value gets set to -1 by default in this case, it is not modified after this. | 364 | // This value gets set to -1 by default in this case, it is not modified after this. |
| 370 | *out = -1; | 365 | *out = -1; |
| 371 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to | 366 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to |
| @@ -373,13 +368,14 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha | |||
| 373 | return ERR_SYNC_TIMEOUT; | 368 | return ERR_SYNC_TIMEOUT; |
| 374 | } else { | 369 | } else { |
| 375 | // Find the first object that is acquirable in the provided list of objects | 370 | // Find the first object that is acquirable in the provided list of objects |
| 376 | auto itr = std::find_if(objects.begin(), objects.end(), | 371 | auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) { |
| 377 | [](const ObjectPtr& object) { return !object->ShouldWait(); }); | 372 | return !object->ShouldWait(thread); |
| 373 | }); | ||
| 378 | 374 | ||
| 379 | if (itr != objects.end()) { | 375 | if (itr != objects.end()) { |
| 380 | // We found a ready object, acquire it and set the result value | 376 | // We found a ready object, acquire it and set the result value |
| 381 | Kernel::WaitObject* object = itr->get(); | 377 | Kernel::WaitObject* object = itr->get(); |
| 382 | object->Acquire(); | 378 | object->Acquire(thread); |
| 383 | *out = std::distance(objects.begin(), itr); | 379 | *out = std::distance(objects.begin(), itr); |
| 384 | return RESULT_SUCCESS; | 380 | return RESULT_SUCCESS; |
| 385 | } | 381 | } |
| @@ -392,28 +388,24 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha | |||
| 392 | return ERR_SYNC_TIMEOUT; | 388 | return ERR_SYNC_TIMEOUT; |
| 393 | 389 | ||
| 394 | // Put the thread to sleep | 390 | // Put the thread to sleep |
| 395 | thread->status = THREADSTATUS_WAIT_SYNCH; | 391 | thread->status = THREADSTATUS_WAIT_SYNCH_ANY; |
| 396 | |||
| 397 | // Clear the thread's waitlist, we won't use it for wait_all = false | ||
| 398 | thread->wait_objects.clear(); | ||
| 399 | 392 | ||
| 400 | // Add the thread to each of the objects' waiting threads. | 393 | // Add the thread to each of the objects' waiting threads. |
| 401 | for (size_t i = 0; i < objects.size(); ++i) { | 394 | for (size_t i = 0; i < objects.size(); ++i) { |
| 402 | Kernel::WaitObject* object = objects[i].get(); | 395 | Kernel::WaitObject* object = objects[i].get(); |
| 403 | // Set the index of this object in the mapping of Objects -> index for this thread. | ||
| 404 | thread->wait_objects_index[object->GetObjectId()] = static_cast<int>(i); | ||
| 405 | object->AddWaitingThread(thread); | 396 | object->AddWaitingThread(thread); |
| 406 | // TODO(Subv): Perform things like update the mutex lock owner's priority to | ||
| 407 | // prevent priority inversion. Currently this is done in Mutex::ShouldWait, | ||
| 408 | // but it should be moved to a function that is called from here. | ||
| 409 | } | 397 | } |
| 410 | 398 | ||
| 399 | thread->wait_objects = std::move(objects); | ||
| 400 | |||
| 411 | // Note: If no handles and no timeout were given, then the thread will deadlock, this is | 401 | // Note: If no handles and no timeout were given, then the thread will deadlock, this is |
| 412 | // consistent with hardware behavior. | 402 | // consistent with hardware behavior. |
| 413 | 403 | ||
| 414 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 404 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 415 | thread->WakeAfterDelay(nano_seconds); | 405 | thread->WakeAfterDelay(nano_seconds); |
| 416 | 406 | ||
| 407 | Core::System::GetInstance().PrepareReschedule(); | ||
| 408 | |||
| 417 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a | 409 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a |
| 418 | // signal in one of its wait objects. | 410 | // signal in one of its wait objects. |
| 419 | // Otherwise we retain the default value of timeout, and -1 in the out parameter | 411 | // Otherwise we retain the default value of timeout, and -1 in the out parameter |
| @@ -448,6 +440,9 @@ static ResultCode ArbitrateAddress(Kernel::Handle handle, u32 address, u32 type, | |||
| 448 | auto res = arbiter->ArbitrateAddress(static_cast<Kernel::ArbitrationType>(type), address, value, | 440 | auto res = arbiter->ArbitrateAddress(static_cast<Kernel::ArbitrationType>(type), address, value, |
| 449 | nanoseconds); | 441 | nanoseconds); |
| 450 | 442 | ||
| 443 | // TODO(Subv): Identify in which specific cases this call should cause a reschedule. | ||
| 444 | Core::System::GetInstance().PrepareReschedule(); | ||
| 445 | |||
| 451 | return res; | 446 | return res; |
| 452 | } | 447 | } |
| 453 | 448 | ||
| @@ -574,6 +569,8 @@ static ResultCode CreateThread(Kernel::Handle* out_handle, s32 priority, u32 ent | |||
| 574 | 569 | ||
| 575 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread))); | 570 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread))); |
| 576 | 571 | ||
| 572 | Core::System::GetInstance().PrepareReschedule(); | ||
| 573 | |||
| 577 | LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " | 574 | LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " |
| 578 | "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", | 575 | "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", |
| 579 | entry_point, name.c_str(), arg, stack_top, priority, processor_id, *out_handle); | 576 | entry_point, name.c_str(), arg, stack_top, priority, processor_id, *out_handle); |
| @@ -586,6 +583,7 @@ static void ExitThread() { | |||
| 586 | LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::CPU().GetPC()); | 583 | LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::CPU().GetPC()); |
| 587 | 584 | ||
| 588 | Kernel::ExitCurrentThread(); | 585 | Kernel::ExitCurrentThread(); |
| 586 | Core::System::GetInstance().PrepareReschedule(); | ||
| 589 | } | 587 | } |
| 590 | 588 | ||
| 591 | /// Gets the priority for the specified thread | 589 | /// Gets the priority for the specified thread |
| @@ -605,6 +603,13 @@ static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) { | |||
| 605 | return ERR_INVALID_HANDLE; | 603 | return ERR_INVALID_HANDLE; |
| 606 | 604 | ||
| 607 | thread->SetPriority(priority); | 605 | thread->SetPriority(priority); |
| 606 | thread->UpdatePriority(); | ||
| 607 | |||
| 608 | // Update the mutexes that this thread is waiting for | ||
| 609 | for (auto& mutex : thread->pending_mutexes) | ||
| 610 | mutex->UpdatePriority(); | ||
| 611 | |||
| 612 | Core::System::GetInstance().PrepareReschedule(); | ||
| 608 | return RESULT_SUCCESS; | 613 | return RESULT_SUCCESS; |
| 609 | } | 614 | } |
| 610 | 615 | ||
| @@ -849,6 +854,8 @@ static void SleepThread(s64 nanoseconds) { | |||
| 849 | 854 | ||
| 850 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 855 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 851 | Kernel::GetCurrentThread()->WakeAfterDelay(nanoseconds); | 856 | Kernel::GetCurrentThread()->WakeAfterDelay(nanoseconds); |
| 857 | |||
| 858 | Core::System::GetInstance().PrepareReschedule(); | ||
| 852 | } | 859 | } |
| 853 | 860 | ||
| 854 | /// This returns the total CPU ticks elapsed since the CPU was powered-on | 861 | /// This returns the total CPU ticks elapsed since the CPU was powered-on |
| @@ -1184,8 +1191,6 @@ void CallSVC(u32 immediate) { | |||
| 1184 | if (info) { | 1191 | if (info) { |
| 1185 | if (info->func) { | 1192 | if (info->func) { |
| 1186 | info->func(); | 1193 | info->func(); |
| 1187 | // TODO(Subv): Not all service functions should cause a reschedule in all cases. | ||
| 1188 | Core::System::GetInstance().PrepareReschedule(); | ||
| 1189 | } else { | 1194 | } else { |
| 1190 | LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name); | 1195 | LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name); |
| 1191 | } | 1196 | } |