summaryrefslogtreecommitdiff
path: root/src/core/hle/svc.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2016-12-16 00:41:22 -0500
committerGravatar GitHub2016-12-16 00:41:22 -0500
commitcda7210fade53a96fcba5fe5cd6dfd7b604f8277 (patch)
treec4090e3871e717ee4d0a2edd837feffc2c877cb0 /src/core/hle/svc.cpp
parentMerge pull request #2316 from endrift/macos-gcc (diff)
parentFixed the codestyle to match our clang-format rules. (diff)
downloadyuzu-cda7210fade53a96fcba5fe5cd6dfd7b604f8277.tar.gz
yuzu-cda7210fade53a96fcba5fe5cd6dfd7b604f8277.tar.xz
yuzu-cda7210fade53a96fcba5fe5cd6dfd7b604f8277.zip
Merge pull request #2260 from Subv/scheduling
Threading: Reworked the way our scheduler works.
Diffstat (limited to 'src/core/hle/svc.cpp')
-rw-r--r--src/core/hle/svc.cpp187
1 files changed, 107 insertions, 80 deletions
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index e5ba9a484..ef25acc4a 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -43,6 +43,9 @@ const ResultCode ERR_PORT_NAME_TOO_LONG(ErrorDescription(30), ErrorModule::OS,
43 ErrorSummary::InvalidArgument, 43 ErrorSummary::InvalidArgument,
44 ErrorLevel::Usage); // 0xE0E0181E 44 ErrorLevel::Usage); // 0xE0E0181E
45 45
46const ResultCode ERR_SYNC_TIMEOUT(ErrorDescription::Timeout, ErrorModule::OS,
47 ErrorSummary::StatusChanged, ErrorLevel::Info);
48
46const ResultCode ERR_MISALIGNED_ADDRESS{// 0xE0E01BF1 49const ResultCode ERR_MISALIGNED_ADDRESS{// 0xE0E01BF1
47 ErrorDescription::MisalignedAddress, ErrorModule::OS, 50 ErrorDescription::MisalignedAddress, ErrorModule::OS,
48 ErrorSummary::InvalidArgument, ErrorLevel::Usage}; 51 ErrorSummary::InvalidArgument, ErrorLevel::Usage};
@@ -260,27 +263,30 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) {
260 auto object = Kernel::g_handle_table.GetWaitObject(handle); 263 auto object = Kernel::g_handle_table.GetWaitObject(handle);
261 Kernel::Thread* thread = Kernel::GetCurrentThread(); 264 Kernel::Thread* thread = Kernel::GetCurrentThread();
262 265
263 thread->waitsynch_waited = false;
264
265 if (object == nullptr) 266 if (object == nullptr)
266 return ERR_INVALID_HANDLE; 267 return ERR_INVALID_HANDLE;
267 268
268 LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, 269 LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle,
269 object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); 270 object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds);
270 271
271 HLE::Reschedule(__func__);
272
273 // Check for next thread to schedule
274 if (object->ShouldWait()) { 272 if (object->ShouldWait()) {
275 273
274 if (nano_seconds == 0)
275 return ERR_SYNC_TIMEOUT;
276
276 object->AddWaitingThread(thread); 277 object->AddWaitingThread(thread);
277 Kernel::WaitCurrentThread_WaitSynchronization({object}, false, false); 278 // TODO(Subv): Perform things like update the mutex lock owner's priority to
279 // prevent priority inversion. Currently this is done in Mutex::ShouldWait,
280 // but it should be moved to a function that is called from here.
281 thread->status = THREADSTATUS_WAIT_SYNCH;
278 282
279 // Create an event to wake the thread up after the specified nanosecond delay has passed 283 // Create an event to wake the thread up after the specified nanosecond delay has passed
280 thread->WakeAfterDelay(nano_seconds); 284 thread->WakeAfterDelay(nano_seconds);
281 285
282 // NOTE: output of this SVC will be set later depending on how the thread resumes 286 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread
283 return HLE::RESULT_INVALID; 287 // resumes due to a signal in its wait objects.
288 // Otherwise we retain the default value of timeout.
289 return ERR_SYNC_TIMEOUT;
284 } 290 }
285 291
286 object->Acquire(); 292 object->Acquire();
@@ -291,11 +297,7 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) {
291/// Wait for the given handles to synchronize, timeout after the specified nanoseconds 297/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
292static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, 298static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all,
293 s64 nano_seconds) { 299 s64 nano_seconds) {
294 bool wait_thread = !wait_all;
295 int handle_index = 0;
296 Kernel::Thread* thread = Kernel::GetCurrentThread(); 300 Kernel::Thread* thread = Kernel::GetCurrentThread();
297 bool was_waiting = thread->waitsynch_waited;
298 thread->waitsynch_waited = false;
299 301
300 // Check if 'handles' is invalid 302 // Check if 'handles' is invalid
301 if (handles == nullptr) 303 if (handles == nullptr)
@@ -311,90 +313,113 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou
311 return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, 313 return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS,
312 ErrorSummary::InvalidArgument, ErrorLevel::Usage); 314 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
313 315
314 // If 'handle_count' is non-zero, iterate through each handle and wait the current thread if 316 using ObjectPtr = Kernel::SharedPtr<Kernel::WaitObject>;
315 // necessary 317 std::vector<ObjectPtr> objects(handle_count);
316 if (handle_count != 0) { 318
317 bool selected = false; // True once an object has been selected 319 for (int i = 0; i < handle_count; ++i) {
318 320 auto object = Kernel::g_handle_table.GetWaitObject(handles[i]);
319 Kernel::SharedPtr<Kernel::WaitObject> wait_object; 321 if (object == nullptr)
320 322 return ERR_INVALID_HANDLE;
321 for (int i = 0; i < handle_count; ++i) { 323 objects[i] = object;
322 auto object = Kernel::g_handle_table.GetWaitObject(handles[i]);
323 if (object == nullptr)
324 return ERR_INVALID_HANDLE;
325
326 // Check if the current thread should wait on this object...
327 if (object->ShouldWait()) {
328
329 // Check we are waiting on all objects...
330 if (wait_all)
331 // Wait the thread
332 wait_thread = true;
333 } else {
334 // Do not wait on this object, check if this object should be selected...
335 if (!wait_all && (!selected || (wait_object == object && was_waiting))) {
336 // Do not wait the thread
337 wait_thread = false;
338 handle_index = i;
339 wait_object = object;
340 selected = true;
341 }
342 }
343 }
344 } else {
345 // If no handles were passed in, put the thread to sleep only when 'wait_all' is false
346 // NOTE: This should deadlock the current thread if no timeout was specified
347 if (!wait_all) {
348 wait_thread = true;
349 }
350 } 324 }
351 325
352 SCOPE_EXIT({ 326 // Clear the mapping of wait object indices.
353 HLE::Reschedule("WaitSynchronizationN"); 327 // We don't want any lingering state in this map.
354 }); // Reschedule after putting the threads to sleep. 328 // It will be repopulated later in the wait_all = false case.
329 thread->wait_objects_index.clear();
330
331 if (wait_all) {
332 bool all_available =
333 std::all_of(objects.begin(), objects.end(),
334 [](const ObjectPtr& object) { return !object->ShouldWait(); });
335 if (all_available) {
336 // We can acquire all objects right now, do so.
337 for (auto& object : objects)
338 object->Acquire();
339 // Note: In this case, the `out` parameter is not set,
340 // and retains whatever value it had before.
341 return RESULT_SUCCESS;
342 }
343
344 // Not all objects were available right now, prepare to suspend the thread.
355 345
356 // If thread should wait, then set its state to waiting 346 // If a timeout value of 0 was provided, just return the Timeout error code instead of
357 if (wait_thread) { 347 // suspending the thread.
348 if (nano_seconds == 0)
349 return ERR_SYNC_TIMEOUT;
358 350
359 // Actually wait the current thread on each object if we decided to wait... 351 // Put the thread to sleep
360 std::vector<SharedPtr<Kernel::WaitObject>> wait_objects; 352 thread->status = THREADSTATUS_WAIT_SYNCH;
361 wait_objects.reserve(handle_count);
362 353
363 for (int i = 0; i < handle_count; ++i) { 354 // Add the thread to each of the objects' waiting threads.
364 auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); 355 for (auto& object : objects) {
365 object->AddWaitingThread(Kernel::GetCurrentThread()); 356 object->AddWaitingThread(thread);
366 wait_objects.push_back(object); 357 // TODO(Subv): Perform things like update the mutex lock owner's priority to
358 // prevent priority inversion. Currently this is done in Mutex::ShouldWait,
359 // but it should be moved to a function that is called from here.
367 } 360 }
368 361
369 Kernel::WaitCurrentThread_WaitSynchronization(std::move(wait_objects), true, wait_all); 362 // Set the thread's waitlist to the list of objects passed to WaitSynchronizationN
363 thread->wait_objects = std::move(objects);
370 364
371 // Create an event to wake the thread up after the specified nanosecond delay has passed 365 // Create an event to wake the thread up after the specified nanosecond delay has passed
372 Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); 366 thread->WakeAfterDelay(nano_seconds);
373
374 // NOTE: output of this SVC will be set later depending on how the thread resumes
375 return HLE::RESULT_INVALID;
376 }
377 367
378 // Acquire objects if we did not wait... 368 // This value gets set to -1 by default in this case, it is not modified after this.
379 for (int i = 0; i < handle_count; ++i) { 369 *out = -1;
380 auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); 370 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to
371 // a signal in one of its wait objects.
372 return ERR_SYNC_TIMEOUT;
373 } else {
374 // Find the first object that is acquirable in the provided list of objects
375 auto itr = std::find_if(objects.begin(), objects.end(),
376 [](const ObjectPtr& object) { return !object->ShouldWait(); });
381 377
382 // Acquire the object if it is not waiting... 378 if (itr != objects.end()) {
383 if (!object->ShouldWait()) { 379 // We found a ready object, acquire it and set the result value
380 Kernel::WaitObject* object = itr->get();
384 object->Acquire(); 381 object->Acquire();
382 *out = std::distance(objects.begin(), itr);
383 return RESULT_SUCCESS;
384 }
385
386 // No objects were ready to be acquired, prepare to suspend the thread.
387
388 // If a timeout value of 0 was provided, just return the Timeout error code instead of
389 // suspending the thread.
390 if (nano_seconds == 0)
391 return ERR_SYNC_TIMEOUT;
392
393 // Put the thread to sleep
394 thread->status = THREADSTATUS_WAIT_SYNCH;
385 395
386 // If this was the first non-waiting object and 'wait_all' is false, don't acquire 396 // Clear the thread's waitlist, we won't use it for wait_all = false
387 // any other objects 397 thread->wait_objects.clear();
388 if (!wait_all) 398
389 break; 399 // Add the thread to each of the objects' waiting threads.
400 for (size_t i = 0; i < objects.size(); ++i) {
401 Kernel::WaitObject* object = objects[i].get();
402 // Set the index of this object in the mapping of Objects -> index for this thread.
403 thread->wait_objects_index[object->GetObjectId()] = static_cast<int>(i);
404 object->AddWaitingThread(thread);
405 // TODO(Subv): Perform things like update the mutex lock owner's priority to
406 // prevent priority inversion. Currently this is done in Mutex::ShouldWait,
407 // but it should be moved to a function that is called from here.
390 } 408 }
391 }
392 409
393 // TODO(bunnei): If 'wait_all' is true, this is probably wrong. However, real hardware does 410 // Note: If no handles and no timeout were given, then the thread will deadlock, this is
394 // not seem to set it to any meaningful value. 411 // consistent with hardware behavior.
395 *out = handle_count != 0 ? (wait_all ? -1 : handle_index) : 0;
396 412
397 return RESULT_SUCCESS; 413 // Create an event to wake the thread up after the specified nanosecond delay has passed
414 thread->WakeAfterDelay(nano_seconds);
415
416 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a
417 // signal in one of its wait objects.
418 // Otherwise we retain the default value of timeout, and -1 in the out parameter
419 thread->wait_set_output = true;
420 *out = -1;
421 return ERR_SYNC_TIMEOUT;
422 }
398} 423}
399 424
400/// Create an address arbiter (to allocate access to shared resources) 425/// Create an address arbiter (to allocate access to shared resources)
@@ -1159,6 +1184,8 @@ void CallSVC(u32 immediate) {
1159 if (info) { 1184 if (info) {
1160 if (info->func) { 1185 if (info->func) {
1161 info->func(); 1186 info->func();
1187 // TODO(Subv): Not all service functions should cause a reschedule in all cases.
1188 HLE::Reschedule(__func__);
1162 } else { 1189 } else {
1163 LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name); 1190 LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name);
1164 } 1191 }