summaryrefslogtreecommitdiff
path: root/src/core/hle/svc.cpp
diff options
context:
space:
mode:
authorGravatar Sebastian Valle2017-01-05 12:55:01 -0500
committerGravatar GitHub2017-01-05 12:55:01 -0500
commitf20d872643654c574f73a263f032613046900f07 (patch)
tree021284c18034d053c81928fa19d2efb6658451fb /src/core/hle/svc.cpp
parentMerge pull request #2407 from jroweboy/nightly-deploy (diff)
parentKernel: Add some asserts to enforce the invariants in the scheduler. (diff)
downloadyuzu-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.cpp69
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 }