summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/citra_qt/debugger/graphics_breakpoints_p.h2
-rw-r--r--src/citra_qt/debugger/profiler.h2
-rw-r--r--src/citra_qt/util/spinbox.cpp3
-rw-r--r--src/common/thread.h81
-rw-r--r--src/common/thread_queue_list.h18
-rw-r--r--src/core/arm/dyncom/arm_dyncom.h2
-rw-r--r--src/core/arm/skyeye_common/vfp/vfp_helper.h3
-rw-r--r--src/core/arm/skyeye_common/vfp/vfpsingle.cpp72
-rw-r--r--src/core/file_sys/disk_archive.h2
-rw-r--r--src/core/hle/function_wrappers.h7
-rw-r--r--src/core/hle/hle.cpp1
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp4
-rw-r--r--src/core/hle/kernel/kernel.cpp2
-rw-r--r--src/core/hle/kernel/mutex.cpp10
-rw-r--r--src/core/hle/kernel/shared_memory.cpp1
-rw-r--r--src/core/hle/kernel/thread.cpp50
-rw-r--r--src/core/hle/kernel/thread.h32
-rw-r--r--src/core/hle/kernel/timer.cpp2
-rw-r--r--src/core/hle/service/apt/apt.cpp13
-rw-r--r--src/core/hle/service/service.cpp43
-rw-r--r--src/core/hle/service/service.h55
-rw-r--r--src/core/hle/svc.cpp52
22 files changed, 256 insertions, 201 deletions
diff --git a/src/citra_qt/debugger/graphics_breakpoints_p.h b/src/citra_qt/debugger/graphics_breakpoints_p.h
index 232bfc863..34e72e859 100644
--- a/src/citra_qt/debugger/graphics_breakpoints_p.h
+++ b/src/citra_qt/debugger/graphics_breakpoints_p.h
@@ -25,7 +25,7 @@ public:
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
- bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
+ bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
public slots:
void OnBreakPointHit(Pica::DebugContext::Event event);
diff --git a/src/citra_qt/debugger/profiler.h b/src/citra_qt/debugger/profiler.h
index a6d87aa0f..fabf279b8 100644
--- a/src/citra_qt/debugger/profiler.h
+++ b/src/citra_qt/debugger/profiler.h
@@ -18,7 +18,7 @@ class ProfilerModel : public QAbstractItemModel
public:
ProfilerModel(QObject* parent);
- QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex& child) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
diff --git a/src/citra_qt/util/spinbox.cpp b/src/citra_qt/util/spinbox.cpp
index 2e2076a27..de4060116 100644
--- a/src/citra_qt/util/spinbox.cpp
+++ b/src/citra_qt/util/spinbox.cpp
@@ -29,6 +29,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <cstdlib>
#include <QLineEdit>
#include <QRegExpValidator>
@@ -206,7 +207,7 @@ QString CSpinBox::TextFromValue()
{
return prefix
+ QString(HasSign() ? ((value < 0) ? "-" : "+") : "")
- + QString("%1").arg(abs(value), num_digits, base, QLatin1Char('0')).toUpper()
+ + QString("%1").arg(std::abs(value), num_digits, base, QLatin1Char('0')).toUpper()
+ suffix;
}
diff --git a/src/common/thread.h b/src/common/thread.h
index a45728e1e..5fdb6baeb 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -51,109 +51,60 @@ int CurrentThreadId();
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
void SetCurrentThreadAffinity(u32 mask);
-class Event
-{
+class Event {
public:
- Event()
- : is_set(false)
- {}
+ Event() : is_set(false) {}
- void Set()
- {
+ void Set() {
std::lock_guard<std::mutex> lk(m_mutex);
- if (!is_set)
- {
+ if (!is_set) {
is_set = true;
m_condvar.notify_one();
}
}
- void Wait()
- {
+ void Wait() {
std::unique_lock<std::mutex> lk(m_mutex);
- m_condvar.wait(lk, IsSet(this));
+ m_condvar.wait(lk, [&]{ return is_set; });
is_set = false;
}
- void Reset()
- {
+ void Reset() {
std::unique_lock<std::mutex> lk(m_mutex);
// no other action required, since wait loops on the predicate and any lingering signal will get cleared on the first iteration
is_set = false;
}
private:
- class IsSet
- {
- public:
- IsSet(const Event* ev)
- : m_event(ev)
- {}
-
- bool operator()()
- {
- return m_event->is_set;
- }
-
- private:
- const Event* const m_event;
- };
-
- volatile bool is_set;
+ bool is_set;
std::condition_variable m_condvar;
std::mutex m_mutex;
};
-// TODO: doesn't work on windows with (count > 2)
-class Barrier
-{
+class Barrier {
public:
- Barrier(size_t count)
- : m_count(count), m_waiting(0)
- {}
+ Barrier(size_t count) : m_count(count), m_waiting(0) {}
- // block until "count" threads call Sync()
- bool Sync()
- {
+ /// Blocks until all "count" threads have called Sync()
+ void Sync() {
std::unique_lock<std::mutex> lk(m_mutex);
// TODO: broken when next round of Sync()s
// is entered before all waiting threads return from the notify_all
- if (m_count == ++m_waiting)
- {
+ if (++m_waiting == m_count) {
m_waiting = 0;
m_condvar.notify_all();
- return true;
- }
- else
- {
- m_condvar.wait(lk, IsDoneWating(this));
- return false;
+ } else {
+ m_condvar.wait(lk, [&]{ return m_waiting == 0; });
}
}
private:
- class IsDoneWating
- {
- public:
- IsDoneWating(const Barrier* bar)
- : m_bar(bar)
- {}
-
- bool operator()()
- {
- return (0 == m_bar->m_waiting);
- }
-
- private:
- const Barrier* const m_bar;
- };
-
std::condition_variable m_condvar;
std::mutex m_mutex;
const size_t m_count;
- volatile size_t m_waiting;
+ size_t m_waiting;
};
void SleepCurrentThread(int ms);
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h
index 444abf115..4f27fc899 100644
--- a/src/common/thread_queue_list.h
+++ b/src/common/thread_queue_list.h
@@ -40,6 +40,18 @@ struct ThreadQueueList {
return -1;
}
+ T get_first() {
+ Queue *cur = first;
+ while (cur != nullptr) {
+ if (!cur->data.empty()) {
+ return cur->data.front();
+ }
+ cur = cur->next_nonempty;
+ }
+
+ return T();
+ }
+
T pop_first() {
Queue *cur = first;
while (cur != nullptr) {
@@ -79,6 +91,12 @@ struct ThreadQueueList {
cur->data.push_back(thread_id);
}
+ void move(const T& thread_id, Priority old_priority, Priority new_priority) {
+ remove(old_priority, thread_id);
+ prepare(new_priority);
+ push_back(new_priority, thread_id);
+ }
+
void remove(Priority priority, const T& thread_id) {
Queue *cur = &queues[priority];
boost::remove_erase(cur->data, thread_id);
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h
index 822b3bbb9..2488c879c 100644
--- a/src/core/arm/dyncom/arm_dyncom.h
+++ b/src/core/arm/dyncom/arm_dyncom.h
@@ -27,7 +27,7 @@ public:
void AddTicks(u64 ticks) override;
- void ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg);
+ void ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg) override;
void SaveContext(Core::ThreadContext& ctx) override;
void LoadContext(const Core::ThreadContext& ctx) override;
diff --git a/src/core/arm/skyeye_common/vfp/vfp_helper.h b/src/core/arm/skyeye_common/vfp/vfp_helper.h
index 5d1b4e53f..6b3dae280 100644
--- a/src/core/arm/skyeye_common/vfp/vfp_helper.h
+++ b/src/core/arm/skyeye_common/vfp/vfp_helper.h
@@ -36,9 +36,6 @@
#include "common/common_types.h"
#include "core/arm/skyeye_common/armdefs.h"
-#define pr_info //printf
-#define pr_debug //printf
-
#define do_div(n, base) {n/=base;}
enum : u32 {
diff --git a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
index 8b2dfa388..a78bdc430 100644
--- a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
+++ b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
@@ -51,6 +51,8 @@
* ===========================================================================
*/
+#include "common/logging/log.h"
+
#include "core/arm/skyeye_common/vfp/vfp_helper.h"
#include "core/arm/skyeye_common/vfp/asm_vfp.h"
#include "core/arm/skyeye_common/vfp/vfp.h"
@@ -63,8 +65,8 @@ static struct vfp_single vfp_single_default_qnan = {
static void vfp_single_dump(const char *str, struct vfp_single *s)
{
- pr_debug("VFP: %s: sign=%d exponent=%d significand=%08x\n",
- str, s->sign != 0, s->exponent, s->significand);
+ LOG_DEBUG(Core_ARM11, "%s: sign=%d exponent=%d significand=%08x",
+ str, s->sign != 0, s->exponent, s->significand);
}
static void vfp_single_normalise_denormal(struct vfp_single *vs)
@@ -154,7 +156,7 @@ u32 vfp_single_normaliseround(ARMul_State* state, int sd, struct vfp_single *vs,
} else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vs->sign != 0))
incr = (1 << (VFP_SINGLE_LOW_BITS + 1)) - 1;
- pr_debug("VFP: rounding increment = 0x%08x\n", incr);
+ LOG_DEBUG(Core_ARM11, "rounding increment = 0x%08x", incr);
/*
* Is our rounding going to overflow?
@@ -209,10 +211,8 @@ pack:
vfp_single_dump("pack: final", vs);
{
s32 d = vfp_single_pack(vs);
-#if 1
- pr_debug("VFP: %s: d(s%d)=%08x exceptions=%08x\n", func,
- sd, d, exceptions);
-#endif
+ LOG_DEBUG(Core_ARM11, "%s: d(s%d)=%08x exceptions=%08x", func,
+ sd, d, exceptions);
vfp_put_float(state, d, sd);
}
@@ -302,7 +302,7 @@ u32 vfp_estimate_sqrt_significand(u32 exponent, u32 significand)
u32 z, a;
if ((significand & 0xc0000000) != 0x40000000) {
- pr_debug("VFP: estimate_sqrt: invalid significand\n");
+ LOG_DEBUG(Core_ARM11, "invalid significand");
}
a = significand << 1;
@@ -392,7 +392,7 @@ sqrt_invalid:
term = (u64)vsd.significand * vsd.significand;
rem = ((u64)vsm.significand << 32) - term;
- pr_debug("VFP: term=%016llx rem=%016llx\n", term, rem);
+ LOG_DEBUG(Core_ARM11, "term=%016lx rem=%016lx", term, rem);
while (rem < 0) {
vsd.significand -= 1;
@@ -624,7 +624,7 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
}
}
- pr_debug("VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
+ LOG_DEBUG(Core_ARM11, "ftoui: d(s%d)=%08x exceptions=%08x", sd, d, exceptions);
vfp_put_float(state, d, sd);
@@ -703,7 +703,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
}
}
- pr_debug("VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
+ LOG_DEBUG(Core_ARM11, "ftosi: d(s%d)=%08x exceptions=%08x", sd, d, exceptions);
vfp_put_float(state, (s32)d, sd);
@@ -800,7 +800,7 @@ vfp_single_add(struct vfp_single *vsd, struct vfp_single *vsn,
if (vsn->significand & 0x80000000 ||
vsm->significand & 0x80000000) {
- pr_info("VFP: bad FP values in %s\n", __func__);
+ LOG_WARNING(Core_ARM11, "bad FP values");
vfp_single_dump("VSN", vsn);
vfp_single_dump("VSM", vsm);
}
@@ -871,7 +871,7 @@ vfp_single_multiply(struct vfp_single *vsd, struct vfp_single *vsn, struct vfp_s
struct vfp_single *t = vsn;
vsn = vsm;
vsm = t;
- pr_debug("VFP: swapping M <-> N\n");
+ LOG_DEBUG(Core_ARM11, "swapping M <-> N");
}
vsd->sign = vsn->sign ^ vsm->sign;
@@ -924,7 +924,7 @@ vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fp
s32 v;
v = vfp_get_float(state, sn);
- pr_debug("VFP: s%u = %08x\n", sn, v);
+ LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, v);
vfp_single_unpack(&vsn, v);
if (vsn.exponent == 0 && vsn.significand)
vfp_single_normalise_denormal(&vsn);
@@ -939,7 +939,7 @@ vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fp
vsp.sign = vfp_sign_negate(vsp.sign);
v = vfp_get_float(state, sd);
- pr_debug("VFP: s%u = %08x\n", sd, v);
+ LOG_DEBUG(Core_ARM11, "s%u = %08x", sd, v);
vfp_single_unpack(&vsn, v);
if (vsn.exponent == 0 && vsn.significand != 0)
vfp_single_normalise_denormal(&vsn);
@@ -961,7 +961,7 @@ vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fp
*/
static u32 vfp_single_fmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
{
- pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, sd);
+ LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, sd);
return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, 0, "fmac");
}
@@ -970,7 +970,8 @@ static u32 vfp_single_fmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
*/
static u32 vfp_single_fnmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
{
- pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sd, sn);
+ // TODO: this one has its arguments inverted, investigate.
+ LOG_DEBUG(Core_ARM11, "s%u = %08x", sd, sn);
return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, NEG_MULTIPLY, "fnmac");
}
@@ -979,7 +980,7 @@ static u32 vfp_single_fnmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr
*/
static u32 vfp_single_fmsc(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
{
- pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, sd);
+ LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, sd);
return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, NEG_SUBTRACT, "fmsc");
}
@@ -988,7 +989,7 @@ static u32 vfp_single_fmsc(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
*/
static u32 vfp_single_fnmsc(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
{
- pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, sd);
+ LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, sd);
return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc");
}
@@ -1001,7 +1002,7 @@ static u32 vfp_single_fmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
u32 exceptions;
s32 n = vfp_get_float(state, sn);
- pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, n);
+ LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, n);
vfp_single_unpack(&vsn, n);
if (vsn.exponent == 0 && vsn.significand)
@@ -1024,7 +1025,7 @@ static u32 vfp_single_fnmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr
u32 exceptions;
s32 n = vfp_get_float(state, sn);
- pr_debug("VFP: s%u = %08x\n", sn, n);
+ LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, n);
vfp_single_unpack(&vsn, n);
if (vsn.exponent == 0 && vsn.significand)
@@ -1048,7 +1049,7 @@ static u32 vfp_single_fadd(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
u32 exceptions;
s32 n = vfp_get_float(state, sn);
- pr_debug("VFP: s%u = %08x\n", sn, n);
+ LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, n);
/*
* Unpack and normalise denormals.
@@ -1071,7 +1072,7 @@ static u32 vfp_single_fadd(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
*/
static u32 vfp_single_fsub(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
{
- pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, sd);
+ LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, sd);
/*
* Subtraction is addition with one sign inverted.
*/
@@ -1091,7 +1092,7 @@ static u32 vfp_single_fdiv(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
s32 n = vfp_get_float(state, sn);
int tm, tn;
- pr_debug("VFP: s%u = %08x\n", sn, n);
+ LOG_DEBUG(Core_ARM11, "s%u = %08x", sn, n);
vfp_single_unpack(&vsn, n);
vfp_single_unpack(&vsm, m);
@@ -1213,7 +1214,6 @@ u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr)
unsigned int sm = vfp_get_sm(inst);
unsigned int vecitr, veclen, vecstride;
struct op *fop;
- pr_debug("In %s\n", __FUNCTION__);
vecstride = 1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK);
@@ -1239,11 +1239,11 @@ u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr)
else
veclen = fpscr & FPSCR_LENGTH_MASK;
- pr_debug("VFP: vecstride=%u veclen=%u\n", vecstride,
- (veclen >> FPSCR_LENGTH_BIT) + 1);
+ LOG_DEBUG(Core_ARM11, "vecstride=%u veclen=%u", vecstride,
+ (veclen >> FPSCR_LENGTH_BIT) + 1);
if (!fop->fn) {
- printf("VFP: could not find single op %d, inst=0x%x@0x%x\n", FEXT_TO_IDX(inst), inst, state->Reg[15]);
+ LOG_CRITICAL(Core_ARM11, "could not find single op %d, inst=0x%x@0x%x", FEXT_TO_IDX(inst), inst, state->Reg[15]);
exit(-1);
goto invalid;
}
@@ -1255,17 +1255,17 @@ u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr)
type = (fop->flags & OP_DD) ? 'd' : 's';
if (op == FOP_EXT)
- pr_debug("VFP: itr%d (%c%u) = op[%u] (s%u=%08x)\n",
- vecitr >> FPSCR_LENGTH_BIT, type, dest, sn,
- sm, m);
+ LOG_DEBUG(Core_ARM11, "itr%d (%c%u) = op[%u] (s%u=%08x)",
+ vecitr >> FPSCR_LENGTH_BIT, type, dest, sn,
+ sm, m);
else
- pr_debug("VFP: itr%d (%c%u) = (s%u) op[%u] (s%u=%08x)\n",
- vecitr >> FPSCR_LENGTH_BIT, type, dest, sn,
- FOP_TO_IDX(op), sm, m);
+ LOG_DEBUG(Core_ARM11, "itr%d (%c%u) = (s%u) op[%u] (s%u=%08x)",
+ vecitr >> FPSCR_LENGTH_BIT, type, dest, sn,
+ FOP_TO_IDX(op), sm, m);
except = fop->fn(state, dest, sn, m, fpscr);
- pr_debug("VFP: itr%d: exceptions=%08x\n",
- vecitr >> FPSCR_LENGTH_BIT, except);
+ LOG_DEBUG(Core_ARM11, "itr%d: exceptions=%08x",
+ vecitr >> FPSCR_LENGTH_BIT, except);
exceptions |= except;
diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h
index dbbdced74..770bd715e 100644
--- a/src/core/file_sys/disk_archive.h
+++ b/src/core/file_sys/disk_archive.h
@@ -24,7 +24,7 @@ class DiskArchive : public ArchiveBackend {
public:
DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
- virtual std::string GetName() const { return "DiskArchive: " + mount_point; }
+ virtual std::string GetName() const override { return "DiskArchive: " + mount_point; }
std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override;
bool DeleteFile(const Path& path) const override;
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 0b6b6f518..be2626eef 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -46,6 +46,13 @@ template<ResultCode func(u32*, u32, u32, u32, u32, u32)> void Wrap(){
FuncReturn(retval);
}
+template<ResultCode func(u32*, s32, u32, u32, u32, s32)> void Wrap() {
+ u32 param_1 = 0;
+ u32 retval = func(&param_1, PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)).raw;
+ Core::g_app_core->SetReg(1, param_1);
+ FuncReturn(retval);
+}
+
template<ResultCode func(s32*, u32*, s32, bool, s64)> void Wrap() {
s32 param_1 = 0;
s32 retval = func(&param_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2),
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
index 1aaeaa9c9..c645d6563 100644
--- a/src/core/hle/hle.cpp
+++ b/src/core/hle/hle.cpp
@@ -13,6 +13,7 @@
#include "core/hle/shared_page.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/service.h"
+#include "core/hle/svc.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 42f8ce2d9..19135266c 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -46,14 +46,12 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address,
case ArbitrationType::WaitIfLessThan:
if ((s32)Memory::Read32(address) <= value) {
Kernel::WaitCurrentThread_ArbitrateAddress(address);
- HLE::Reschedule(__func__);
}
break;
case ArbitrationType::WaitIfLessThanWithTimeout:
if ((s32)Memory::Read32(address) <= value) {
Kernel::WaitCurrentThread_ArbitrateAddress(address);
GetCurrentThread()->WakeAfterDelay(nanoseconds);
- HLE::Reschedule(__func__);
}
break;
case ArbitrationType::DecrementAndWaitIfLessThan:
@@ -62,7 +60,6 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address,
Memory::Write32(address, memory_value);
if (memory_value <= value) {
Kernel::WaitCurrentThread_ArbitrateAddress(address);
- HLE::Reschedule(__func__);
}
break;
}
@@ -73,7 +70,6 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address,
if (memory_value <= value) {
Kernel::WaitCurrentThread_ArbitrateAddress(address);
GetCurrentThread()->WakeAfterDelay(nanoseconds);
- HLE::Reschedule(__func__);
}
break;
}
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 498b2ec98..6261b82b6 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -154,7 +154,7 @@ void Shutdown() {
*/
bool LoadExec(u32 entry_point) {
// 0x30 is the typical main thread priority I've seen used so far
- g_main_thread = Kernel::SetupMainThread(Kernel::DEFAULT_STACK_SIZE, entry_point, 0x30);
+ g_main_thread = Kernel::SetupMainThread(Kernel::DEFAULT_STACK_SIZE, entry_point, THREADPRIO_DEFAULT);
return true;
}
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index be2c49706..ebc9e79d7 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -56,7 +56,15 @@ SharedPtr<Mutex> Mutex::Create(bool initial_locked, std::string name) {
}
bool Mutex::ShouldWait() {
- return lock_count > 0 && holding_thread != GetCurrentThread();;
+ auto thread = GetCurrentThread();
+ bool wait = lock_count > 0 && holding_thread != thread;
+
+ // If the holding thread of the mutex is lower priority than this thread, that thread should
+ // temporarily inherit this thread's priority
+ if (wait && thread->current_priority < holding_thread->current_priority)
+ holding_thread->BoostPriority(thread->current_priority);
+
+ return wait;
}
void Mutex::Acquire() {
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index 4211fcf04..9b2511b53 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -16,6 +16,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(std::string name) {
SharedPtr<SharedMemory> shared_memory(new SharedMemory);
shared_memory->name = std::move(name);
+ shared_memory->base_address = 0x0;
return shared_memory;
}
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index be1aed615..33d66b986 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -140,6 +140,28 @@ void ArbitrateAllThreads(u32 address) {
}
}
+/// Boost low priority threads (temporarily) that have been starved
+static void PriorityBoostStarvedThreads() {
+ u64 current_ticks = CoreTiming::GetTicks();
+
+ for (auto& thread : thread_list) {
+ // TODO(bunnei): Threads that have been waiting to be scheduled for `boost_ticks` (or
+ // longer) will have their priority temporarily adjusted to 1 higher than the highest
+ // priority thread to prevent thread starvation. This general behavior has been verified
+ // on hardware. However, this is almost certainly not perfect, and the real CTR OS scheduler
+ // should probably be reversed to verify this.
+
+ const u64 boost_timeout = 2000000; // Boost threads that have been ready for > this long
+
+ u64 delta = current_ticks - thread->last_running_ticks;
+
+ if (thread->status == THREADSTATUS_READY && delta > boost_timeout && !thread->idle) {
+ const s32 priority = std::max(ready_queue.get_first()->current_priority - 1, 0);
+ thread->BoostPriority(priority);
+ }
+ }
+}
+
/**
* Switches the CPU's active thread context to that of the specified thread
* @param new_thread The thread to switch to
@@ -151,6 +173,7 @@ static void SwitchContext(Thread* new_thread) {
// Save context for previous thread
if (previous_thread) {
+ previous_thread->last_running_ticks = CoreTiming::GetTicks();
Core::g_app_core->SaveContext(previous_thread->context);
if (previous_thread->status == THREADSTATUS_RUNNING) {
@@ -168,6 +191,9 @@ static void SwitchContext(Thread* new_thread) {
ready_queue.remove(new_thread->current_priority, new_thread);
new_thread->status = THREADSTATUS_RUNNING;
+ // Restores thread to its nominal priority if it has been temporarily changed
+ new_thread->current_priority = new_thread->nominal_priority;
+
Core::g_app_core->LoadContext(new_thread->context);
} else {
current_thread = nullptr;
@@ -364,7 +390,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
thread->status = THREADSTATUS_DORMANT;
thread->entry_point = entry_point;
thread->stack_top = stack_top;
- thread->initial_priority = thread->current_priority = priority;
+ thread->nominal_priority = thread->current_priority = priority;
+ thread->last_running_ticks = CoreTiming::GetTicks();
thread->processor_id = processor_id;
thread->wait_set_output = false;
thread->wait_all = false;
@@ -400,17 +427,15 @@ static void ClampPriority(const Thread* thread, s32* priority) {
void Thread::SetPriority(s32 priority) {
ClampPriority(this, &priority);
- if (current_priority == priority) {
- return;
- }
+ // If thread was ready, adjust queues
+ if (status == THREADSTATUS_READY)
+ ready_queue.move(this, current_priority, priority);
- if (status == THREADSTATUS_READY) {
- // If thread was ready, adjust queues
- ready_queue.remove(current_priority, this);
- ready_queue.prepare(priority);
- ready_queue.push_back(priority, this);
- }
-
+ nominal_priority = current_priority = priority;
+}
+
+void Thread::BoostPriority(s32 priority) {
+ ready_queue.move(this, current_priority, priority);
current_priority = priority;
}
@@ -440,6 +465,9 @@ SharedPtr<Thread> SetupMainThread(u32 stack_size, u32 entry_point, s32 priority)
void Reschedule() {
Thread* prev = GetCurrentThread();
+
+ PriorityBoostStarvedThreads();
+
Thread* next = PopNextReadyThread();
HLE::g_reschedule = false;
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index cfd073a70..233bcbdbd 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -17,17 +17,19 @@
#include "core/hle/kernel/kernel.h"
#include "core/hle/result.h"
-enum ThreadPriority {
- THREADPRIO_HIGHEST = 0, ///< Highest thread priority
- THREADPRIO_DEFAULT = 16, ///< Default thread priority for userland apps
- THREADPRIO_LOW = 31, ///< Low range of thread priority for userland apps
- THREADPRIO_LOWEST = 63, ///< Thread priority max checked by svcCreateThread
+enum ThreadPriority : s32{
+ THREADPRIO_HIGHEST = 0, ///< Highest thread priority
+ THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps
+ THREADPRIO_DEFAULT = 48, ///< Default thread priority for userland apps
+ THREADPRIO_LOWEST = 63, ///< Lowest thread priority
};
-enum ThreadProcessorId {
- THREADPROCESSORID_0 = 0xFFFFFFFE, ///< Enables core appcode
- THREADPROCESSORID_1 = 0xFFFFFFFD, ///< Enables core syscore
- THREADPROCESSORID_ALL = 0xFFFFFFFC, ///< Enables both cores
+enum ThreadProcessorId : s32 {
+ THREADPROCESSORID_DEFAULT = -2, ///< Run thread on default core specified by exheader
+ THREADPROCESSORID_ALL = -1, ///< Run thread on either core
+ THREADPROCESSORID_0 = 0, ///< Run thread on core 0 (AppCore)
+ THREADPROCESSORID_1 = 1, ///< Run thread on core 1 (SysCore)
+ THREADPROCESSORID_MAX = 2, ///< Processor ID must be less than this
};
enum ThreadStatus {
@@ -88,6 +90,12 @@ public:
void SetPriority(s32 priority);
/**
+ * Temporarily boosts the thread's priority until the next time it is scheduled
+ * @param priority The new priority
+ */
+ void BoostPriority(s32 priority);
+
+ /**
* Gets the thread's thread ID
* @return The thread's ID
*/
@@ -135,8 +143,10 @@ public:
u32 entry_point;
u32 stack_top;
- s32 initial_priority;
- s32 current_priority;
+ s32 nominal_priority; ///< Nominal thread priority, as set by the emulated application
+ s32 current_priority; ///< Current thread priority, can be temporarily changed
+
+ u64 last_running_ticks; ///< CPU tick when thread was last running
s32 processor_id;
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp
index 610e26a3c..1ec2a4b10 100644
--- a/src/core/hle/kernel/timer.cpp
+++ b/src/core/hle/kernel/timer.cpp
@@ -66,7 +66,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) {
SharedPtr<Timer> timer = timer_callback_handle_table.Get<Timer>(static_cast<Handle>(timer_handle));
if (timer == nullptr) {
- LOG_CRITICAL(Kernel, "Callback fired for invalid timer %08X", timer_handle);
+ LOG_CRITICAL(Kernel, "Callback fired for invalid timer %08lX", timer_handle);
return;
}
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index 4861d9e5f..190c5df7a 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -32,7 +32,8 @@ static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem = nullptr;
static Kernel::SharedPtr<Kernel::Mutex> lock = nullptr;
static Kernel::SharedPtr<Kernel::Event> notification_event = nullptr; ///< APT notification event
-static Kernel::SharedPtr<Kernel::Event> pause_event = nullptr; ///< APT pause event
+static Kernel::SharedPtr<Kernel::Event> start_event = nullptr; ///< APT start event
+
static std::vector<u8> shared_font;
static u32 cpu_percent = 0; ///< CPU time available to the running application
@@ -44,11 +45,11 @@ void Initialize(Service::Interface* self) {
cmd_buff[2] = 0x04000000; // According to 3dbrew, this value should be 0x04000000
cmd_buff[3] = Kernel::g_handle_table.Create(notification_event).MoveFrom();
- cmd_buff[4] = Kernel::g_handle_table.Create(pause_event).MoveFrom();
+ cmd_buff[4] = Kernel::g_handle_table.Create(start_event).MoveFrom();
- // TODO(bunnei): Check if these events are cleared/signaled every time Initialize is called.
+ // TODO(bunnei): Check if these events are cleared every time Initialize is called.
notification_event->Clear();
- pause_event->Signal(); // Fire start event
+ start_event->Clear();
ASSERT_MSG((nullptr != lock), "Cannot initialize without lock");
lock->Release();
@@ -81,7 +82,7 @@ void NotifyToWait(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 app_id = cmd_buff[1];
// TODO(Subv): Verify this, it seems to get SWKBD and Home Menu further.
- pause_event->Signal();
+ start_event->Signal();
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id);
@@ -312,7 +313,7 @@ void Init() {
// TODO(bunnei): Check if these are created in Initialize or on APT process startup.
notification_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Notification");
- pause_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Pause");
+ start_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Start");
}
void Shutdown() {
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 134ff1740..d50327cb9 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -51,6 +51,49 @@ namespace Service {
std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_kernel_named_ports;
std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_srv_services;
+/**
+ * Creates a function string for logging, complete with the name (or header code, depending
+ * on what's passed in) the port name, and all the cmd_buff arguments.
+ */
+static std::string MakeFunctionString(const char* name, const char* port_name, const u32* cmd_buff) {
+ // Number of params == bits 0-5 + bits 6-11
+ int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
+
+ std::string function_string = Common::StringFromFormat("function '%s': port=%s", name, port_name);
+ for (int i = 1; i <= num_params; ++i) {
+ function_string += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]);
+ }
+ return function_string;
+}
+
+ResultVal<bool> Interface::SyncRequest() {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ auto itr = m_functions.find(cmd_buff[0]);
+
+ if (itr == m_functions.end() || itr->second.func == nullptr) {
+ std::string function_name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name;
+ LOG_ERROR(Service, "unknown / unimplemented %s", MakeFunctionString(function_name.c_str(), GetPortName().c_str(), cmd_buff).c_str());
+
+ // TODO(bunnei): Hack - ignore error
+ cmd_buff[1] = 0;
+ return MakeResult<bool>(false);
+ } else {
+ LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName().c_str(), cmd_buff).c_str());
+ }
+
+ itr->second.func(this);
+
+ return MakeResult<bool>(false); // TODO: Implement return from actual function
+}
+
+void Interface::Register(const FunctionInfo* functions, size_t n) {
+ m_functions.reserve(n);
+ for (size_t i = 0; i < n; ++i) {
+ // Usually this array is sorted by id already, so hint to instead at the end
+ m_functions.emplace_hint(m_functions.cend(), functions[i].id, functions[i]);
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
// Module interface
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index bfe16ebad..21ada67b5 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -4,20 +4,15 @@
#pragma once
-#include <algorithm>
#include <string>
#include <unordered_map>
-#include <vector>
#include <boost/container/flat_map.hpp>
#include "common/common.h"
-#include "common/string_util.h"
-#include "core/mem_map.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/session.h"
-#include "core/hle/svc.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace Service
@@ -26,31 +21,11 @@ namespace Service {
static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters)
-class Manager;
-
/// Interface to a CTROS service
class Interface : public Kernel::Session {
// TODO(yuriks): An "Interface" being a Kernel::Object is mostly non-sense. Interface should be
// just something that encapsulates a session and acts as a helper to implement service
// processes.
-
- friend class Manager;
-
- /**
- * Creates a function string for logging, complete with the name (or header code, depending
- * on what's passed in) the port name, and all the cmd_buff arguments.
- */
- std::string MakeFunctionString(const char* name, const char* port_name, const u32* cmd_buff) {
- // Number of params == bits 0-5 + bits 6-11
- int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
-
- std::string function_string = Common::StringFromFormat("function '%s': port=%s", name, port_name);
- for (int i = 1; i <= num_params; ++i) {
- function_string += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]);
- }
- return function_string;
- }
-
public:
std::string GetName() const override { return GetPortName(); }
@@ -70,25 +45,7 @@ public:
return "[UNKNOWN SERVICE PORT]";
}
- ResultVal<bool> SyncRequest() override {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- auto itr = m_functions.find(cmd_buff[0]);
-
- if (itr == m_functions.end() || itr->second.func == nullptr) {
- std::string function_name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name;
- LOG_ERROR(Service, "unknown / unimplemented %s", MakeFunctionString(function_name.c_str(), GetPortName().c_str(), cmd_buff).c_str());
-
- // TODO(bunnei): Hack - ignore error
- cmd_buff[1] = 0;
- return MakeResult<bool>(false);
- } else {
- LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName().c_str(), cmd_buff).c_str());
- }
-
- itr->second.func(this);
-
- return MakeResult<bool>(false); // TODO: Implement return from actual function
- }
+ ResultVal<bool> SyncRequest() override;
protected:
@@ -96,14 +53,12 @@ protected:
* Registers the functions in the service
*/
template <size_t N>
- void Register(const FunctionInfo (&functions)[N]) {
- m_functions.reserve(N);
- for (auto& fn : functions) {
- // Usually this array is sorted by id already, so hint to instead at the end
- m_functions.emplace_hint(m_functions.cend(), fn.id, fn);
- }
+ inline void Register(const FunctionInfo (&functions)[N]) {
+ Register(functions, N);
}
+ void Register(const FunctionInfo* functions, size_t n);
+
private:
boost::container::flat_map<u32, FunctionInfo> m_functions;
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index bbb4eb9cd..76e9b171a 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -283,8 +283,13 @@ static ResultCode ArbitrateAddress(Handle handle, u32 address, u32 type, u32 val
if (arbiter == nullptr)
return ERR_INVALID_HANDLE;
- return arbiter->ArbitrateAddress(static_cast<Kernel::ArbitrationType>(type),
- address, value, nanoseconds);
+ auto res = arbiter->ArbitrateAddress(static_cast<Kernel::ArbitrationType>(type),
+ address, value, nanoseconds);
+
+ if (res == RESULT_SUCCESS)
+ HLE::Reschedule(__func__);
+
+ return res;
}
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
@@ -312,7 +317,7 @@ static ResultCode GetResourceLimitCurrentValues(s64* values, Handle resource_lim
}
/// Creates a new thread
-static ResultCode CreateThread(u32* out_handle, u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) {
+static ResultCode CreateThread(Handle* out_handle, s32 priority, u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) {
using Kernel::Thread;
std::string name;
@@ -323,6 +328,27 @@ static ResultCode CreateThread(u32* out_handle, u32 priority, u32 entry_point, u
name = Common::StringFromFormat("unknown-%08x", entry_point);
}
+ // TODO(bunnei): Implement resource limits to return an error code instead of the below assert.
+ // The error code should be: Description::NotAuthorized, Module::OS, Summary::WrongArgument,
+ // Level::Permanent
+ ASSERT_MSG(priority >= THREADPRIO_USERLAND_MAX, "Unexpected thread priority!");
+
+ if (priority > THREADPRIO_LOWEST) {
+ return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS,
+ ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+ }
+
+ switch (processor_id) {
+ case THREADPROCESSORID_DEFAULT:
+ case THREADPROCESSORID_0:
+ case THREADPROCESSORID_1:
+ break;
+ default:
+ // TODO(bunnei): Implement support for other processor IDs
+ ASSERT_MSG(false, "Unsupported thread processor ID: %d", processor_id);
+ break;
+ }
+
CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create(
name, entry_point, priority, arg, processor_id, stack_top));
CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread)));
@@ -331,10 +357,7 @@ static ResultCode CreateThread(u32* out_handle, u32 priority, u32 entry_point, u
"threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point,
name.c_str(), arg, stack_top, priority, processor_id, *out_handle);
- if (THREADPROCESSORID_1 == processor_id) {
- LOG_WARNING(Kernel_SVC,
- "thread designated for system CPU core (UNIMPLEMENTED) will be run with app core scheduling");
- }
+ HLE::Reschedule(__func__);
return RESULT_SUCCESS;
}
@@ -374,8 +397,11 @@ static ResultCode CreateMutex(Handle* out_handle, u32 initial_locked) {
SharedPtr<Mutex> mutex = Mutex::Create(initial_locked != 0);
CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(mutex)));
+ HLE::Reschedule(__func__);
+
LOG_TRACE(Kernel_SVC, "called initial_locked=%s : created handle=0x%08X",
initial_locked ? "true" : "false", *out_handle);
+
return RESULT_SUCCESS;
}
@@ -390,6 +416,9 @@ static ResultCode ReleaseMutex(Handle handle) {
return ERR_INVALID_HANDLE;
mutex->Release();
+
+ HLE::Reschedule(__func__);
+
return RESULT_SUCCESS;
}
@@ -428,6 +457,9 @@ static ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count)
return ERR_INVALID_HANDLE;
CASCADE_RESULT(*count, semaphore->Release(release_count));
+
+ HLE::Reschedule(__func__);
+
return RESULT_SUCCESS;
}
@@ -520,6 +552,9 @@ static ResultCode SetTimer(Handle handle, s64 initial, s64 interval) {
return ERR_INVALID_HANDLE;
timer->Set(initial, interval);
+
+ HLE::Reschedule(__func__);
+
return RESULT_SUCCESS;
}
@@ -534,6 +569,9 @@ static ResultCode CancelTimer(Handle handle) {
return ERR_INVALID_HANDLE;
timer->Cancel();
+
+ HLE::Reschedule(__func__);
+
return RESULT_SUCCESS;
}