diff options
| -rw-r--r-- | .travis-deps.sh | 2 | ||||
| -rw-r--r-- | .travis.yml | 5 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.h | 12 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 56 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 4 | ||||
| -rw-r--r-- | src/core/hle/svc.cpp | 3 |
6 files changed, 58 insertions, 24 deletions
diff --git a/.travis-deps.sh b/.travis-deps.sh index 8e22a6358..b978e552e 100644 --- a/.travis-deps.sh +++ b/.travis-deps.sh | |||
| @@ -20,6 +20,8 @@ if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then | |||
| 20 | curl http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \ | 20 | curl http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \ |
| 21 | | sudo tar -xz -C /usr/local --strip-components=1 | 21 | | sudo tar -xz -C /usr/local --strip-components=1 |
| 22 | elif [ "$TRAVIS_OS_NAME" = osx ]; then | 22 | elif [ "$TRAVIS_OS_NAME" = osx ]; then |
| 23 | export HOMEBREW_CACHE="$PWD/.homebrew-cache" | ||
| 24 | mkdir -p "$HOMEBREW_CACHE" | ||
| 23 | brew tap homebrew/versions | 25 | brew tap homebrew/versions |
| 24 | brew install qt5 glfw3 pkgconfig | 26 | brew install qt5 glfw3 pkgconfig |
| 25 | fi | 27 | fi |
diff --git a/.travis.yml b/.travis.yml index 1cb369d5b..8c5aceb7c 100644 --- a/.travis.yml +++ b/.travis.yml | |||
| @@ -4,6 +4,11 @@ os: | |||
| 4 | 4 | ||
| 5 | language: cpp | 5 | language: cpp |
| 6 | 6 | ||
| 7 | cache: | ||
| 8 | apt: true | ||
| 9 | directories: | ||
| 10 | - .homebrew-cache | ||
| 11 | |||
| 7 | before_install: | 12 | before_install: |
| 8 | - sh .travis-deps.sh | 13 | - sh .travis-deps.sh |
| 9 | 14 | ||
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 7123485be..683fffeee 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -14,6 +14,10 @@ typedef s32 Result; | |||
| 14 | 14 | ||
| 15 | namespace Kernel { | 15 | namespace Kernel { |
| 16 | 16 | ||
| 17 | // From kernel.h. Declarations duplicated here to avoid a circular header dependency. | ||
| 18 | class Thread; | ||
| 19 | Thread* GetCurrentThread(); | ||
| 20 | |||
| 17 | enum KernelHandle { | 21 | enum KernelHandle { |
| 18 | CurrentThread = 0xFFFF8000, | 22 | CurrentThread = 0xFFFF8000, |
| 19 | CurrentProcess = 0xFFFF8001, | 23 | CurrentProcess = 0xFFFF8001, |
| @@ -81,6 +85,10 @@ public: | |||
| 81 | 85 | ||
| 82 | template <class T> | 86 | template <class T> |
| 83 | T* Get(Handle handle) { | 87 | T* Get(Handle handle) { |
| 88 | if (handle == CurrentThread) { | ||
| 89 | return reinterpret_cast<T*>(GetCurrentThread()); | ||
| 90 | } | ||
| 91 | |||
| 84 | if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) { | 92 | if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) { |
| 85 | if (handle != 0) { | 93 | if (handle != 0) { |
| 86 | LOG_ERROR(Kernel, "Bad object handle %08x", handle); | 94 | LOG_ERROR(Kernel, "Bad object handle %08x", handle); |
| @@ -99,6 +107,10 @@ public: | |||
| 99 | // ONLY use this when you know the handle is valid. | 107 | // ONLY use this when you know the handle is valid. |
| 100 | template <class T> | 108 | template <class T> |
| 101 | T *GetFast(Handle handle) { | 109 | T *GetFast(Handle handle) { |
| 110 | if (handle == CurrentThread) { | ||
| 111 | return reinterpret_cast<T*>(GetCurrentThread()); | ||
| 112 | } | ||
| 113 | |||
| 102 | const Handle realHandle = handle - HANDLE_OFFSET; | 114 | const Handle realHandle = handle - HANDLE_OFFSET; |
| 103 | _dbg_assert_(Kernel, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]); | 115 | _dbg_assert_(Kernel, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]); |
| 104 | return static_cast<T*>(pool[realHandle]); | 116 | return static_cast<T*>(pool[realHandle]); |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 1c04701de..834308926 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -83,8 +83,7 @@ static Thread* current_thread; | |||
| 83 | static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup | 83 | static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup |
| 84 | static u32 next_thread_id; ///< The next available thread id | 84 | static u32 next_thread_id; ///< The next available thread id |
| 85 | 85 | ||
| 86 | /// Gets the current thread | 86 | Thread* GetCurrentThread() { |
| 87 | inline Thread* GetCurrentThread() { | ||
| 88 | return current_thread; | 87 | return current_thread; |
| 89 | } | 88 | } |
| 90 | 89 | ||
| @@ -148,16 +147,19 @@ void ChangeReadyState(Thread* t, bool ready) { | |||
| 148 | } | 147 | } |
| 149 | } | 148 | } |
| 150 | 149 | ||
| 151 | /// Verify that a thread has not been released from waiting | 150 | /// Check if a thread is blocking on a specified wait type |
| 152 | static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) { | 151 | static bool CheckWaitType(const Thread* thread, WaitType type) { |
| 153 | _dbg_assert_(Kernel, thread != nullptr); | 152 | return (type == thread->wait_type) && (thread->IsWaiting()); |
| 154 | return (type == thread->wait_type) && (wait_handle == thread->wait_handle) && (thread->IsWaiting()); | ||
| 155 | } | 153 | } |
| 156 | 154 | ||
| 157 | /// Verify that a thread has not been released from waiting (with wait address) | 155 | /// Check if a thread is blocking on a specified wait type with a specified handle |
| 158 | static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) { | 156 | static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle) { |
| 159 | _dbg_assert_(Kernel, thread != nullptr); | 157 | return CheckWaitType(thread, type) && (wait_handle == thread->wait_handle); |
| 160 | return VerifyWait(thread, type, wait_handle) && (wait_address == thread->wait_address); | 158 | } |
| 159 | |||
| 160 | /// Check if a thread is blocking on a specified wait type with a specified handle and address | ||
| 161 | static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) { | ||
| 162 | return CheckWaitType(thread, type, wait_handle) && (wait_address == thread->wait_address); | ||
| 161 | } | 163 | } |
| 162 | 164 | ||
| 163 | /// Stops the current thread | 165 | /// Stops the current thread |
| @@ -172,9 +174,9 @@ ResultCode StopThread(Handle handle, const char* reason) { | |||
| 172 | thread->status = THREADSTATUS_DORMANT; | 174 | thread->status = THREADSTATUS_DORMANT; |
| 173 | for (Handle waiting_handle : thread->waiting_threads) { | 175 | for (Handle waiting_handle : thread->waiting_threads) { |
| 174 | Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle); | 176 | Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle); |
| 175 | if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) { | 177 | |
| 178 | if (CheckWaitType(waiting_thread, WAITTYPE_THREADEND, handle)) | ||
| 176 | ResumeThreadFromWait(waiting_handle); | 179 | ResumeThreadFromWait(waiting_handle); |
| 177 | } | ||
| 178 | } | 180 | } |
| 179 | thread->waiting_threads.clear(); | 181 | thread->waiting_threads.clear(); |
| 180 | 182 | ||
| @@ -210,7 +212,7 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) { | |||
| 210 | for (Handle handle : thread_queue) { | 212 | for (Handle handle : thread_queue) { |
| 211 | Thread* thread = g_object_pool.Get<Thread>(handle); | 213 | Thread* thread = g_object_pool.Get<Thread>(handle); |
| 212 | 214 | ||
| 213 | if (!VerifyWait(thread, WAITTYPE_ARB, arbiter, address)) | 215 | if (!CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) |
| 214 | continue; | 216 | continue; |
| 215 | 217 | ||
| 216 | if (thread == nullptr) | 218 | if (thread == nullptr) |
| @@ -235,7 +237,7 @@ void ArbitrateAllThreads(u32 arbiter, u32 address) { | |||
| 235 | for (Handle handle : thread_queue) { | 237 | for (Handle handle : thread_queue) { |
| 236 | Thread* thread = g_object_pool.Get<Thread>(handle); | 238 | Thread* thread = g_object_pool.Get<Thread>(handle); |
| 237 | 239 | ||
| 238 | if (VerifyWait(thread, WAITTYPE_ARB, arbiter, address)) | 240 | if (CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) |
| 239 | ResumeThreadFromWait(handle); | 241 | ResumeThreadFromWait(handle); |
| 240 | } | 242 | } |
| 241 | } | 243 | } |
| @@ -306,6 +308,8 @@ void ResumeThreadFromWait(Handle handle) { | |||
| 306 | Thread* thread = Kernel::g_object_pool.Get<Thread>(handle); | 308 | Thread* thread = Kernel::g_object_pool.Get<Thread>(handle); |
| 307 | if (thread) { | 309 | if (thread) { |
| 308 | thread->status &= ~THREADSTATUS_WAIT; | 310 | thread->status &= ~THREADSTATUS_WAIT; |
| 311 | thread->wait_handle = 0; | ||
| 312 | thread->wait_type = WAITTYPE_NONE; | ||
| 309 | if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { | 313 | if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { |
| 310 | ChangeReadyState(thread, true); | 314 | ChangeReadyState(thread, true); |
| 311 | } | 315 | } |
| @@ -469,19 +473,27 @@ void Reschedule() { | |||
| 469 | Thread* prev = GetCurrentThread(); | 473 | Thread* prev = GetCurrentThread(); |
| 470 | Thread* next = NextThread(); | 474 | Thread* next = NextThread(); |
| 471 | HLE::g_reschedule = false; | 475 | HLE::g_reschedule = false; |
| 472 | if (next > 0) { | ||
| 473 | LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); | ||
| 474 | 476 | ||
| 477 | if (next != nullptr) { | ||
| 478 | LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); | ||
| 475 | SwitchContext(next); | 479 | SwitchContext(next); |
| 480 | } else { | ||
| 481 | LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); | ||
| 476 | 482 | ||
| 477 | // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep | 483 | for (Handle handle : thread_queue) { |
| 478 | // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again. | 484 | Thread* thread = g_object_pool.Get<Thread>(handle); |
| 479 | // This results in the current thread yielding on a VBLANK once, and then it will be | 485 | LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X", |
| 480 | // immediately placed back in the queue for execution. | 486 | thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle); |
| 481 | if (prev->wait_type == WAITTYPE_VBLANK) { | ||
| 482 | ResumeThreadFromWait(prev->GetHandle()); | ||
| 483 | } | 487 | } |
| 484 | } | 488 | } |
| 489 | |||
| 490 | // TODO(bunnei): Hack - There is no timing mechanism yet to wake up a thread if it has been put | ||
| 491 | // to sleep. So, we'll just immediately set it to "ready" again after an attempted context | ||
| 492 | // switch has occurred. This results in the current thread yielding on a sleep once, and then it | ||
| 493 | // will immediately be placed back in the queue for execution. | ||
| 494 | |||
| 495 | if (CheckWaitType(prev, WAITTYPE_SLEEP)) | ||
| 496 | ResumeThreadFromWait(prev->GetHandle()); | ||
| 485 | } | 497 | } |
| 486 | 498 | ||
| 487 | ResultCode GetThreadId(u32* thread_id, Handle handle) { | 499 | ResultCode GetThreadId(u32* thread_id, Handle handle) { |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index be7adface..65e8ef554 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -40,7 +40,6 @@ enum WaitType { | |||
| 40 | WAITTYPE_SEMA, | 40 | WAITTYPE_SEMA, |
| 41 | WAITTYPE_EVENT, | 41 | WAITTYPE_EVENT, |
| 42 | WAITTYPE_THREADEND, | 42 | WAITTYPE_THREADEND, |
| 43 | WAITTYPE_VBLANK, | ||
| 44 | WAITTYPE_MUTEX, | 43 | WAITTYPE_MUTEX, |
| 45 | WAITTYPE_SYNCH, | 44 | WAITTYPE_SYNCH, |
| 46 | WAITTYPE_ARB, | 45 | WAITTYPE_ARB, |
| @@ -78,6 +77,9 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address); | |||
| 78 | /// Arbitrate all threads currently waiting... | 77 | /// Arbitrate all threads currently waiting... |
| 79 | void ArbitrateAllThreads(u32 arbiter, u32 address); | 78 | void ArbitrateAllThreads(u32 arbiter, u32 address); |
| 80 | 79 | ||
| 80 | /// Gets the current thread | ||
| 81 | Thread* GetCurrentThread(); | ||
| 82 | |||
| 81 | /// Gets the current thread handle | 83 | /// Gets the current thread handle |
| 82 | Handle GetCurrentThreadHandle(); | 84 | Handle GetCurrentThreadHandle(); |
| 83 | 85 | ||
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 47e9bf77e..70ef7839c 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -352,7 +352,8 @@ static Result ClearEvent(Handle evt) { | |||
| 352 | static void SleepThread(s64 nanoseconds) { | 352 | static void SleepThread(s64 nanoseconds) { |
| 353 | LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); | 353 | LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); |
| 354 | 354 | ||
| 355 | // Check for next thread to schedule | 355 | // Sleep current thread and check for next thread to schedule |
| 356 | Kernel::WaitCurrentThread(WAITTYPE_SLEEP); | ||
| 356 | HLE::Reschedule(__func__); | 357 | HLE::Reschedule(__func__); |
| 357 | } | 358 | } |
| 358 | 359 | ||