diff options
Diffstat (limited to 'src/core/hle/svc.cpp')
| -rw-r--r-- | src/core/hle/svc.cpp | 181 |
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 | } |