diff options
Diffstat (limited to 'src/core/hle/svc.cpp')
| -rw-r--r-- | src/core/hle/svc.cpp | 105 |
1 files changed, 45 insertions, 60 deletions
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index c06df84b3..14da09883 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -41,6 +41,9 @@ const ResultCode ERR_PORT_NAME_TOO_LONG(ErrorDescription(30), ErrorModule::OS, | |||
| 41 | ErrorSummary::InvalidArgument, | 41 | ErrorSummary::InvalidArgument, |
| 42 | ErrorLevel::Usage); // 0xE0E0181E | 42 | ErrorLevel::Usage); // 0xE0E0181E |
| 43 | 43 | ||
| 44 | const ResultCode ERR_SYNC_TIMEOUT(ErrorDescription::Timeout, ErrorModule::OS, | ||
| 45 | ErrorSummary::StatusChanged, ErrorLevel::Info); | ||
| 46 | |||
| 44 | const ResultCode ERR_MISALIGNED_ADDRESS{// 0xE0E01BF1 | 47 | const ResultCode ERR_MISALIGNED_ADDRESS{// 0xE0E01BF1 |
| 45 | ErrorDescription::MisalignedAddress, ErrorModule::OS, | 48 | ErrorDescription::MisalignedAddress, ErrorModule::OS, |
| 46 | ErrorSummary::InvalidArgument, ErrorLevel::Usage}; | 49 | ErrorSummary::InvalidArgument, ErrorLevel::Usage}; |
| @@ -257,11 +260,8 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { | |||
| 257 | 260 | ||
| 258 | if (object->ShouldWait()) { | 261 | if (object->ShouldWait()) { |
| 259 | 262 | ||
| 260 | if (nano_seconds == 0) { | 263 | if (nano_seconds == 0) |
| 261 | return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, | 264 | return ERR_SYNC_TIMEOUT; |
| 262 | ErrorSummary::StatusChanged, | ||
| 263 | ErrorLevel::Info); | ||
| 264 | } | ||
| 265 | 265 | ||
| 266 | object->AddWaitingThread(thread); | 266 | object->AddWaitingThread(thread); |
| 267 | // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. | 267 | // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. |
| @@ -273,9 +273,7 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { | |||
| 273 | 273 | ||
| 274 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a signal in its wait objects. | 274 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a signal in its wait objects. |
| 275 | // Otherwise we retain the default value of timeout. | 275 | // Otherwise we retain the default value of timeout. |
| 276 | return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, | 276 | return ERR_SYNC_TIMEOUT; |
| 277 | ErrorSummary::StatusChanged, | ||
| 278 | ErrorLevel::Info); | ||
| 279 | } | 277 | } |
| 280 | 278 | ||
| 281 | object->Acquire(); | 279 | object->Acquire(); |
| @@ -286,8 +284,6 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { | |||
| 286 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds | 284 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds |
| 287 | static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, | 285 | static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, |
| 288 | s64 nano_seconds) { | 286 | s64 nano_seconds) { |
| 289 | bool wait_thread = !wait_all; | ||
| 290 | int handle_index = 0; | ||
| 291 | Kernel::Thread* thread = Kernel::GetCurrentThread(); | 287 | Kernel::Thread* thread = Kernel::GetCurrentThread(); |
| 292 | 288 | ||
| 293 | // Check if 'handles' is invalid | 289 | // Check if 'handles' is invalid |
| @@ -305,7 +301,6 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou | |||
| 305 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); | 301 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); |
| 306 | 302 | ||
| 307 | using ObjectPtr = Kernel::SharedPtr<Kernel::WaitObject>; | 303 | using ObjectPtr = Kernel::SharedPtr<Kernel::WaitObject>; |
| 308 | |||
| 309 | std::vector<ObjectPtr> objects(handle_count); | 304 | std::vector<ObjectPtr> objects(handle_count); |
| 310 | 305 | ||
| 311 | for (int i = 0; i < handle_count; ++i) { | 306 | for (int i = 0; i < handle_count; ++i) { |
| @@ -320,100 +315,90 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou | |||
| 320 | // It will be repopulated later in the wait_all = false case. | 315 | // It will be repopulated later in the wait_all = false case. |
| 321 | thread->wait_objects_index.clear(); | 316 | thread->wait_objects_index.clear(); |
| 322 | 317 | ||
| 323 | if (!wait_all) { | 318 | if (wait_all) { |
| 324 | // Find the first object that is acquireable in the provided list of objects | 319 | bool all_available = std::all_of(objects.begin(), objects.end(), [](const ObjectPtr& object) { |
| 325 | auto itr = std::find_if(objects.begin(), objects.end(), [](const ObjectPtr& object) { | ||
| 326 | return !object->ShouldWait(); | 320 | return !object->ShouldWait(); |
| 327 | }); | 321 | }); |
| 328 | 322 | if (all_available) { | |
| 329 | if (itr != objects.end()) { | 323 | // We can acquire all objects right now, do so. |
| 330 | // We found a ready object, acquire it and set the result value | 324 | for (auto object : objects) |
| 331 | ObjectPtr object = *itr; | 325 | object->Acquire(); |
| 332 | object->Acquire(); | 326 | // Note: In this case, the `out` parameter is not set, and retains whatever value it had before. |
| 333 | *out = std::distance(objects.begin(), itr); | ||
| 334 | return RESULT_SUCCESS; | 327 | return RESULT_SUCCESS; |
| 335 | } | 328 | } |
| 336 | 329 | ||
| 337 | // No objects were ready to be acquired, prepare to suspend the thread. | 330 | // Not all objects were available right now, prepare to suspend the thread. |
| 338 | 331 | ||
| 339 | // If a timeout value of 0 was provided, just return the Timeout error code instead of suspending the thread. | 332 | // If a timeout value of 0 was provided, just return the Timeout error code instead of suspending the thread. |
| 340 | if (nano_seconds == 0) { | 333 | if (nano_seconds == 0) |
| 341 | return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, | 334 | return ERR_SYNC_TIMEOUT; |
| 342 | ErrorSummary::StatusChanged, | ||
| 343 | ErrorLevel::Info); | ||
| 344 | } | ||
| 345 | 335 | ||
| 346 | // Put the thread to sleep | 336 | // Put the thread to sleep |
| 347 | thread->status = THREADSTATUS_WAIT_SYNCH; | 337 | thread->status = THREADSTATUS_WAIT_SYNCH; |
| 348 | 338 | ||
| 349 | // Clear the thread's waitlist, we won't use it for wait_all = false | ||
| 350 | thread->wait_objects.clear(); | ||
| 351 | |||
| 352 | // Add the thread to each of the objects' waiting threads. | 339 | // Add the thread to each of the objects' waiting threads. |
| 353 | for (size_t i = 0; i < objects.size(); ++i) { | 340 | for (auto& object : objects) { |
| 354 | ObjectPtr object = objects[i]; | ||
| 355 | // Set the index of this object in the mapping of Objects -> index for this thread. | ||
| 356 | thread->wait_objects_index[object->GetObjectId()] = static_cast<int>(i); | ||
| 357 | object->AddWaitingThread(thread); | 341 | object->AddWaitingThread(thread); |
| 358 | // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. | 342 | // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. |
| 359 | // Currently this is done in Mutex::ShouldWait, but it should be moved to a function that is called from here. | 343 | // Currently this is done in Mutex::ShouldWait, but it should be moved to a function that is called from here. |
| 360 | } | 344 | } |
| 361 | 345 | ||
| 362 | // Note: If no handles and no timeout were given, then the thread will deadlock, this is consistent with hardware behavior. | 346 | // Set the thread's waitlist to the list of objects passed to WaitSynchronizationN |
| 347 | thread->wait_objects = std::move(objects); | ||
| 363 | 348 | ||
| 364 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 349 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 365 | thread->WakeAfterDelay(nano_seconds); | 350 | thread->WakeAfterDelay(nano_seconds); |
| 366 | 351 | ||
| 367 | // 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. | 352 | // This value gets set to -1 by default in this case, it is not modified after this. |
| 368 | // Otherwise we retain the default value of timeout, and -1 in the out parameter | ||
| 369 | thread->wait_set_output = true; | ||
| 370 | *out = -1; | 353 | *out = -1; |
| 371 | return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, | 354 | // 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. |
| 372 | ErrorSummary::StatusChanged, | 355 | return ERR_SYNC_TIMEOUT; |
| 373 | ErrorLevel::Info); | ||
| 374 | } else { | 356 | } else { |
| 375 | bool all_available = std::all_of(objects.begin(), objects.end(), [](const ObjectPtr& object) { | 357 | // Find the first object that is acquirable in the provided list of objects |
| 358 | auto itr = std::find_if(objects.begin(), objects.end(), [](const ObjectPtr& object) { | ||
| 376 | return !object->ShouldWait(); | 359 | return !object->ShouldWait(); |
| 377 | }); | 360 | }); |
| 378 | if (all_available) { | 361 | |
| 379 | // We can acquire all objects right now, do so. | 362 | if (itr != objects.end()) { |
| 380 | for (auto object : objects) | 363 | // We found a ready object, acquire it and set the result value |
| 381 | object->Acquire(); | 364 | Kernel::WaitObject* object = itr->get(); |
| 382 | // Note: In this case, the `out` parameter is not set, and retains whatever value it had before. | 365 | object->Acquire(); |
| 366 | *out = std::distance(objects.begin(), itr); | ||
| 383 | return RESULT_SUCCESS; | 367 | return RESULT_SUCCESS; |
| 384 | } | 368 | } |
| 385 | 369 | ||
| 386 | // Not all objects were available right now, prepare to suspend the thread. | 370 | // No objects were ready to be acquired, prepare to suspend the thread. |
| 387 | 371 | ||
| 388 | // If a timeout value of 0 was provided, just return the Timeout error code instead of suspending the thread. | 372 | // If a timeout value of 0 was provided, just return the Timeout error code instead of suspending the thread. |
| 389 | if (nano_seconds == 0) { | 373 | if (nano_seconds == 0) |
| 390 | return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, | 374 | return ERR_SYNC_TIMEOUT; |
| 391 | ErrorSummary::StatusChanged, | ||
| 392 | ErrorLevel::Info); | ||
| 393 | } | ||
| 394 | 375 | ||
| 395 | // Put the thread to sleep | 376 | // Put the thread to sleep |
| 396 | thread->status = THREADSTATUS_WAIT_SYNCH; | 377 | thread->status = THREADSTATUS_WAIT_SYNCH; |
| 397 | 378 | ||
| 398 | // Set the thread's waitlist to the list of objects passed to WaitSynchronizationN | 379 | // Clear the thread's waitlist, we won't use it for wait_all = false |
| 399 | thread->wait_objects = objects; | 380 | thread->wait_objects.clear(); |
| 400 | 381 | ||
| 401 | // Add the thread to each of the objects' waiting threads. | 382 | // Add the thread to each of the objects' waiting threads. |
| 402 | for (auto object : objects) { | 383 | for (size_t i = 0; i < objects.size(); ++i) { |
| 384 | Kernel::WaitObject* object = objects[i].get(); | ||
| 385 | // Set the index of this object in the mapping of Objects -> index for this thread. | ||
| 386 | thread->wait_objects_index[object->GetObjectId()] = static_cast<int>(i); | ||
| 403 | object->AddWaitingThread(thread); | 387 | object->AddWaitingThread(thread); |
| 404 | // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. | 388 | // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. |
| 405 | // Currently this is done in Mutex::ShouldWait, but it should be moved to a function that is called from here. | 389 | // Currently this is done in Mutex::ShouldWait, but it should be moved to a function that is called from here. |
| 406 | } | 390 | } |
| 407 | 391 | ||
| 392 | // Note: If no handles and no timeout were given, then the thread will deadlock, this is consistent with hardware behavior. | ||
| 393 | |||
| 408 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 394 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 409 | thread->WakeAfterDelay(nano_seconds); | 395 | thread->WakeAfterDelay(nano_seconds); |
| 410 | 396 | ||
| 411 | // This value gets set to -1 by default in this case, it is not modified after this. | ||
| 412 | *out = -1; | ||
| 413 | // 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. | 397 | // 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. |
| 414 | return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, | 398 | // Otherwise we retain the default value of timeout, and -1 in the out parameter |
| 415 | ErrorSummary::StatusChanged, | 399 | thread->wait_set_output = true; |
| 416 | ErrorLevel::Info); | 400 | *out = -1; |
| 401 | return ERR_SYNC_TIMEOUT; | ||
| 417 | } | 402 | } |
| 418 | } | 403 | } |
| 419 | 404 | ||