From 978e1d4653cd12a68d6bfa05af57edb1645da0f5 Mon Sep 17 00:00:00 2001
From: bunnei <ericbunnie@gmail.com>
Date: Tue, 20 May 2014 23:03:45 -0400
Subject: [PATCH] mutex: initial commit of HLE module

---
 src/core/CMakeLists.txt       |   1 +
 src/core/core.vcxproj         |   2 +
 src/core/core.vcxproj.filters |   6 ++
 src/core/hle/kernel/mutex.cpp | 122 ++++++++++++++++++++++++++++++++++
 src/core/hle/kernel/mutex.h   |  26 ++++++++
 src/core/hle/service/apt.cpp  |  10 ++-
 src/core/hle/svc.cpp          |   9 +--
 7 files changed, 166 insertions(+), 10 deletions(-)
 create mode 100644 src/core/hle/kernel/mutex.cpp
 create mode 100644 src/core/hle/kernel/mutex.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index e006502da..6ad308798 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -35,6 +35,7 @@ set(SRCS    core.cpp
             hle/coprocessor.cpp
             hle/svc.cpp
             hle/kernel/kernel.cpp
+            hle/kernel/mutex.cpp
             hle/kernel/thread.cpp
             hle/service/apt.cpp
             hle/service/gsp.cpp
diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj
index 6eb58a636..f271d336e 100644
--- a/src/core/core.vcxproj
+++ b/src/core/core.vcxproj
@@ -169,6 +169,7 @@
     <ClCompile Include="hle\coprocessor.cpp" />
     <ClCompile Include="hle\hle.cpp" />
     <ClCompile Include="hle\kernel\kernel.cpp" />
+    <ClCompile Include="hle\kernel\mutex.cpp" />
     <ClCompile Include="hle\kernel\thread.cpp" />
     <ClCompile Include="hle\service\apt.cpp" />
     <ClCompile Include="hle\service\gsp.cpp" />
@@ -217,6 +218,7 @@
     <ClInclude Include="hle\function_wrappers.h" />
     <ClInclude Include="hle\hle.h" />
     <ClInclude Include="hle\kernel\kernel.h" />
+    <ClInclude Include="hle\kernel\mutex.h" />
     <ClInclude Include="hle\kernel\thread.h" />
     <ClInclude Include="hle\service\apt.h" />
     <ClInclude Include="hle\service\gsp.h" />
diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters
index fc4e35edb..f664debec 100644
--- a/src/core/core.vcxproj.filters
+++ b/src/core/core.vcxproj.filters
@@ -162,6 +162,9 @@
     <ClCompile Include="hle\svc.cpp">
       <Filter>hle</Filter>
     </ClCompile>
+    <ClCompile Include="hle\kernel\mutex.cpp">
+      <Filter>hle\kernel</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="arm\disassembler\arm_disasm.h">
@@ -289,6 +292,9 @@
     <ClInclude Include="hle\svc.h">
       <Filter>hle</Filter>
     </ClInclude>
+    <ClInclude Include="hle\kernel\mutex.h">
+      <Filter>hle\kernel</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <Text Include="CMakeLists.txt" />
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
new file mode 100644
index 000000000..2b2cff4ea
--- /dev/null
+++ b/src/core/hle/kernel/mutex.cpp
@@ -0,0 +1,122 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.  
+
+#include <map>
+#include <vector>
+
+#include "common/common.h"
+
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/thread.h"
+
+namespace Kernel {
+
+class Mutex : public Object {
+public:
+    const char* GetTypeName() { return "Mutex"; }
+
+    static Kernel::HandleType GetStaticHandleType() {  return Kernel::HandleType::Mutex; }
+    Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Mutex; }
+
+    bool initial_locked;                        ///< Initial lock state when mutex was created
+    bool locked;                                ///< Current locked state
+    Handle lock_thread;                         ///< Handle to thread that currently has mutex
+    std::vector<Handle> waiting_threads;        ///< Threads that are waiting for the mutex
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+typedef std::multimap<Handle, Handle> MutexMap;
+static MutexMap g_mutex_held_locks;
+
+void __MutexAcquireLock(Mutex* mutex, Handle thread) {
+    g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle()));
+    mutex->lock_thread = thread;
+}
+
+void __MutexAcquireLock(Mutex* mutex) {
+    Handle thread = GetCurrentThread();
+    __MutexAcquireLock(mutex, thread);
+}
+
+void __MutexEraseLock(Mutex* mutex) {
+    Handle handle = mutex->GetHandle();
+    auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread);
+    for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
+        if ((*iter).second == handle) {
+            g_mutex_held_locks.erase(iter);
+            break;
+        }
+    }
+    mutex->lock_thread = -1;
+}
+
+bool __LockMutex(Mutex* mutex) {
+    // Mutex alread locked?
+    if (mutex->locked) {
+        return false;
+    }
+    __MutexAcquireLock(mutex);
+    return true;
+}
+
+bool __ReleaseMutexForThread(Mutex* mutex, Handle thread) {
+    __MutexAcquireLock(mutex, thread);
+    Kernel::ResumeThreadFromWait(thread);
+    return true;
+}
+
+bool __ReleaseMutex(Mutex* mutex) {
+    __MutexEraseLock(mutex);
+    bool woke_threads = false;
+    auto iter = mutex->waiting_threads.begin();
+
+    // Find the next waiting thread for the mutex...
+    while (!woke_threads && !mutex->waiting_threads.empty()) {
+        woke_threads |= __ReleaseMutexForThread(mutex, *iter);
+        mutex->waiting_threads.erase(iter);
+    }
+    // Reset mutex lock thread handle, nothing is waiting
+    if (!woke_threads) {
+        mutex->locked = false;
+        mutex->lock_thread = -1;
+    }
+    return woke_threads;
+}
+
+/**
+ * Releases a mutex
+ * @param handle Handle to mutex to release
+ */
+Result ReleaseMutex(Handle handle) {
+    Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle);
+    if (!__ReleaseMutex(mutex)) {
+        return -1;
+    }
+    return 0;
+}
+
+/**
+ * Creates a mutex
+ * @param handle Reference to handle for the newly created mutex
+ * @param initial_locked Specifies if the mutex should be locked initially
+ */
+Result CreateMutex(Handle& handle, bool initial_locked) {
+    Mutex* mutex = new Mutex;
+    handle = Kernel::g_object_pool.Create(mutex);
+
+    mutex->locked = mutex->initial_locked = initial_locked;
+
+    // Acquire mutex with current thread if initialized as locked...
+    if (mutex->locked) {
+        __MutexAcquireLock(mutex);
+
+    // Otherwise, reset lock thread handle
+    } else {
+        mutex->lock_thread = -1;
+    }
+    return 0;
+}
+
+} // namespace
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
new file mode 100644
index 000000000..1f843e979
--- /dev/null
+++ b/src/core/hle/kernel/mutex.h
@@ -0,0 +1,26 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.  
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/hle/kernel/kernel.h"
+
+namespace Kernel {
+
+/**
+ * Releases a mutex
+ * @param handle Handle to mutex to release
+ */
+Result ReleaseMutex(Handle handle);
+
+/**
+ * Creates a mutex
+ * @param handle Reference to handle for the newly created mutex
+ * @param initial_locked Specifies if the mutex should be locked initially
+ */
+Result CreateMutex(Handle& handle, bool initial_locked);
+
+} // namespace
diff --git a/src/core/hle/service/apt.cpp b/src/core/hle/service/apt.cpp
index 1f6a70eab..ecec4da00 100644
--- a/src/core/hle/service/apt.cpp
+++ b/src/core/hle/service/apt.cpp
@@ -3,9 +3,10 @@
 // Refer to the license.txt file included.
 
 
-#include "common/log.h"
+#include "common/common.h"
 
 #include "core/hle/hle.h"
+#include "core/hle/kernel/mutex.h"
 #include "core/hle/service/apt.h"
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -19,11 +20,8 @@ void Initialize(Service::Interface* self) {
 
 void GetLockHandle(Service::Interface* self) {
     u32* cmd_buff = Service::GetCommandBuffer();
-    u32 flags = cmd_buff[1];
-
-    // TODO: This should be an actual mutex handle. Games will check that this is not non-zero 
-    // (NULL), and fail if such. A faked non-zero value will at least enable further booting. 
-    cmd_buff[5] = 0x12345678;
+    u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field
+    cmd_buff[1] = Kernel::CreateMutex(cmd_buff[5], false);
 }
 
 const Interface::FunctionInfo FunctionTable[] = {
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 3674a08c5..73ab073f5 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -187,15 +187,16 @@ Result CreateThread(void* thread, u32 priority, u32 entry_point, u32 arg, u32 st
 /// Create a mutex
 Result CreateMutex(void* _mutex, u32 initial_locked) {
     Handle* mutex = (Handle*)_mutex;
-    DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateMutex called initial_locked=%s", 
-        initial_locked ? "true" : "false");
-    Core::g_app_core->SetReg(1, 0xF00D0BAD);
+    DEBUG_LOG(SVC, "CreateMutex called initial_locked=%s", initial_locked ? "true" : "false");
+    Kernel::CreateMutex(*mutex, (bool)initial_locked);
+    Core::g_app_core->SetReg(1, *mutex);
     return 0;
 }
 
 /// Release a mutex
 Result ReleaseMutex(Handle handle) {
-    DEBUG_LOG(SVC, "(UNIMPLEMENTED) ReleaseMutex called handle=0x%08X", handle);
+    DEBUG_LOG(SVC, "ReleaseMutex called handle=0x%08X", handle);
+    Kernel::ReleaseMutex(handle);
     return 0;
 }