diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1745edbf3..84facd82b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1215,6 +1215,7 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/HLE/HLETables.cpp
Core/HLE/HLETables.h
Core/HLE/KernelWaitHelpers.h
+ Core/HLE/ThreadQueueList.h
Core/HLE/__sceAudio.cpp
Core/HLE/__sceAudio.h
Core/HLE/sceAdler.cpp
diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj
index a7c8394b8..8573613f0 100644
--- a/Core/Core.vcxproj
+++ b/Core/Core.vcxproj
@@ -605,6 +605,7 @@
+
diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters
index 88d02d163..f62a0aaa9 100644
--- a/Core/Core.vcxproj.filters
+++ b/Core/Core.vcxproj.filters
@@ -1164,6 +1164,9 @@
FileLoaders
+
+ HLE
+
diff --git a/Core/HLE/ThreadQueueList.h b/Core/HLE/ThreadQueueList.h
new file mode 100644
index 000000000..9c1b1e5da
--- /dev/null
+++ b/Core/HLE/ThreadQueueList.h
@@ -0,0 +1,278 @@
+// Copyright (c) 2012- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#pragma once
+
+#include "Core/HLE/sceKernel.h"
+#include "Common/ChunkFile.h"
+
+struct ThreadQueueList {
+ // Number of queues (number of priority levels starting at 0.)
+ static const int NUM_QUEUES = 128;
+ // Initial number of threads a single queue can handle.
+ static const int INITIAL_CAPACITY = 32;
+
+ struct Queue {
+ // Next ever-been-used queue (worse priority.)
+ Queue *next;
+ // First valid item in data.
+ int first;
+ // One after last valid item in data.
+ int end;
+ // A too-large array with room on the front and end.
+ SceUID *data;
+ // Size of data array.
+ int capacity;
+
+ inline int size() const {
+ return end - first;
+ }
+ inline bool empty() const {
+ return first == end;
+ }
+ inline int full() const {
+ return end == capacity;
+ }
+ };
+
+ ThreadQueueList() {
+ memset(queues, 0, sizeof(queues));
+ first = invalid();
+ }
+
+ ~ThreadQueueList() {
+ clear();
+ }
+
+ // Only for debugging, returns priority level.
+ int contains(const SceUID uid) {
+ for (int i = 0; i < NUM_QUEUES; ++i) {
+ if (queues[i].data == nullptr)
+ continue;
+
+ Queue *cur = &queues[i];
+ for (int j = cur->first; j < cur->end; ++j) {
+ if (cur->data[j] == uid)
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ inline SceUID pop_first() {
+ Queue *cur = first;
+ while (cur != invalid()) {
+ if (cur->size() > 0)
+ return cur->data[cur->first++];
+ cur = cur->next;
+ }
+
+ _dbg_assert_msg_(SCEKERNEL, false, "ThreadQueueList should not be empty.");
+ return 0;
+ }
+
+ inline SceUID pop_first_better(u32 priority) {
+ Queue *cur = first;
+ // Don't bother looking past (worse than) this priority.
+ Queue *stop = &queues[priority];
+ while (cur < stop) {
+ if (cur->size() > 0)
+ return cur->data[cur->first++];
+ cur = cur->next;
+ }
+
+ return 0;
+ }
+
+ inline void push_front(u32 priority, const SceUID threadID) {
+ Queue *cur = &queues[priority];
+ cur->data[--cur->first] = threadID;
+ // If we ran out of room toward the front, add more room for next time.
+ if (cur->first == 0)
+ rebalance(priority);
+ }
+
+ inline void push_back(u32 priority, const SceUID threadID) {
+ Queue *cur = &queues[priority];
+ cur->data[cur->end++] = threadID;
+ if (cur->full())
+ rebalance(priority);
+ }
+
+ inline void remove(u32 priority, const SceUID threadID) {
+ Queue *cur = &queues[priority];
+ _dbg_assert_msg_(SCEKERNEL, cur->next != nullptr, "ThreadQueueList::Queue should already be linked up.");
+
+ for (int i = cur->first; i < cur->end; ++i) {
+ if (cur->data[i] == threadID) {
+ // How many more after this one?
+ int remaining = cur->end - i;
+ // If there are more, move them into place.
+ if (remaining > 0)
+ memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(SceUID));
+
+ // Now we're one shorter.
+ --cur->end;
+ return;
+ }
+ }
+
+ // Wasn't there.
+ }
+
+ inline void rotate(u32 priority) {
+ Queue *cur = &queues[priority];
+ _dbg_assert_msg_(SCEKERNEL, cur->next != nullptr, "ThreadQueueList::Queue should already be linked up.");
+
+ if (cur->size() > 1) {
+ // Grab the front and push it on the end.
+ cur->data[cur->end++] = cur->data[cur->first++];
+ if (cur->full())
+ rebalance(priority);
+ }
+ }
+
+ inline void clear() {
+ for (int i = 0; i < NUM_QUEUES; ++i) {
+ if (queues[i].data != nullptr)
+ free(queues[i].data);
+ }
+ memset(queues, 0, sizeof(queues));
+ first = invalid();
+ }
+
+ inline bool empty(u32 priority) const {
+ const Queue *cur = &queues[priority];
+ return cur->empty();
+ }
+
+ inline void prepare(u32 priority) {
+ Queue *cur = &queues[priority];
+ if (cur->next == nullptr)
+ link(priority, INITIAL_CAPACITY);
+ }
+
+ void DoState(PointerWrap &p) {
+ auto s = p.Section("ThreadQueueList", 1);
+ if (!s)
+ return;
+
+ int numQueues = NUM_QUEUES;
+ p.Do(numQueues);
+ if (numQueues != NUM_QUEUES) {
+ p.SetError(p.ERROR_FAILURE);
+ ERROR_LOG(SCEKERNEL, "Savestate loading error: invalid data");
+ return;
+ }
+
+ if (p.mode == p.MODE_READ)
+ clear();
+
+ for (int i = 0; i < NUM_QUEUES; ++i) {
+ Queue *cur = &queues[i];
+ int size = cur->size();
+ p.Do(size);
+ int capacity = cur->capacity;
+ p.Do(capacity);
+
+ if (capacity == 0)
+ continue;
+
+ if (p.mode == p.MODE_READ) {
+ link(i, capacity);
+ cur->first = (cur->capacity - size) / 2;
+ cur->end = cur->first + size;
+ }
+
+ if (size != 0)
+ p.DoArray(&cur->data[cur->first], size);
+ }
+ }
+
+private:
+ Queue *invalid() const {
+ return (Queue *)-1;
+ }
+
+ // Initialize a priority level and link to other queues.
+ void link(u32 priority, int size) {
+ _dbg_assert_msg_(SCEKERNEL, queues[priority].data == nullptr, "ThreadQueueList::Queue should only be initialized once.");
+
+ // Make sure we stay a multiple of INITIAL_CAPACITY.
+ if (size <= INITIAL_CAPACITY)
+ size = INITIAL_CAPACITY;
+ else {
+ int goal = size;
+ size = INITIAL_CAPACITY;
+ while (size < goal)
+ size *= 2;
+ }
+
+ // Allocate the queue.
+ Queue *cur = &queues[priority];
+ cur->data = (SceUID *)malloc(sizeof(SceUID) * size);
+ cur->capacity = size;
+ // Start smack in the middle so it can move both directions.
+ cur->first = size / 2;
+ cur->end = size / 2;
+
+ for (int i = (int)priority - 1; i >= 0; --i) {
+ // This queue is before ours, and points past us.
+ // We'll have it point to our new queue, inserting into the chain.
+ if (queues[i].next != nullptr) {
+ cur->next = queues[i].next;
+ queues[i].next = cur;
+ return;
+ }
+ }
+
+ // Never found above - that means there's no better queue yet.
+ // The new one is now first, and whoever was first is after it.
+ cur->next = first;
+ first = cur;
+ }
+
+ // Move or allocate as necessary to maintain free space on both sides.
+ void rebalance(u32 priority) {
+ Queue *cur = &queues[priority];
+ int size = cur->size();
+ // Basically full. Time for a larger queue?
+ if (size >= cur->capacity - 2) {
+ int new_capacity = cur->capacity * 2;
+ SceUID *new_data = (SceUID *)realloc(cur->data, new_capacity * sizeof(SceUID));
+ if (new_data != nullptr) {
+ // Success, it's bigger now.
+ cur->capacity = new_capacity;
+ cur->data = new_data;
+ }
+ }
+
+ // If we center all the items, it should start here.
+ int newFirst = (cur->capacity - size) / 2;
+ if (newFirst != cur->first) {
+ memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(SceUID));
+ cur->first = newFirst;
+ cur->end = newFirst + size;
+ }
+ }
+
+ // The first queue that's ever been used.
+ Queue *first;
+ // The priority level queues of thread ids.
+ Queue queues[NUM_QUEUES];
+};
diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp
index 098bb729c..4044cb898 100644
--- a/Core/HLE/sceKernelThread.cpp
+++ b/Core/HLE/sceKernelThread.cpp
@@ -41,6 +41,7 @@
#include "Core/HLE/sceKernelModule.h"
#include "Core/HLE/sceKernelInterrupt.h"
#include "Core/HLE/KernelWaitHelpers.h"
+#include "Core/HLE/ThreadQueueList.h"
typedef struct
{
@@ -600,298 +601,6 @@ public:
std::map pausedWaits;
};
-struct ThreadQueueList
-{
- // Number of queues (number of priority levels starting at 0.)
- static const int NUM_QUEUES = 128;
- // Initial number of threads a single queue can handle.
- static const int INITIAL_CAPACITY = 32;
-
- struct Queue
- {
- // Next ever-been-used queue (worse priority.)
- Queue *next;
- // First valid item in data.
- int first;
- // One after last valid item in data.
- int end;
- // A too-large array with room on the front and end.
- SceUID *data;
- // Size of data array.
- int capacity;
-
- inline int size() const {
- return end - first;
- }
- inline bool empty() const {
- return first == end;
- }
- inline int full() const {
- return end == capacity;
- }
- };
-
- ThreadQueueList()
- {
- memset(queues, 0, sizeof(queues));
- first = invalid();
- }
-
- ~ThreadQueueList()
- {
- clear();
- }
-
- // Only for debugging, returns priority level.
- int contains(const SceUID uid)
- {
- for (int i = 0; i < NUM_QUEUES; ++i)
- {
- if (queues[i].data == nullptr)
- continue;
-
- Queue *cur = &queues[i];
- for (int j = cur->first; j < cur->end; ++j)
- {
- if (cur->data[j] == uid)
- return i;
- }
- }
-
- return -1;
- }
-
- inline SceUID pop_first()
- {
- Queue *cur = first;
- while (cur != invalid())
- {
- if (cur->size() > 0)
- return cur->data[cur->first++];
- cur = cur->next;
- }
-
- _dbg_assert_msg_(SCEKERNEL, false, "ThreadQueueList should not be empty.");
- return 0;
- }
-
- inline SceUID pop_first_better(u32 priority)
- {
- Queue *cur = first;
- // Don't bother looking past (worse than) this priority.
- Queue *stop = &queues[priority];
- while (cur < stop)
- {
- if (cur->size() > 0)
- return cur->data[cur->first++];
- cur = cur->next;
- }
-
- return 0;
- }
-
- inline void push_front(u32 priority, const SceUID threadID)
- {
- Queue *cur = &queues[priority];
- cur->data[--cur->first] = threadID;
- // If we ran out of room toward the front, add more room for next time.
- if (cur->first == 0)
- rebalance(priority);
- }
-
- inline void push_back(u32 priority, const SceUID threadID)
- {
- Queue *cur = &queues[priority];
- cur->data[cur->end++] = threadID;
- if (cur->full())
- rebalance(priority);
- }
-
- inline void remove(u32 priority, const SceUID threadID)
- {
- Queue *cur = &queues[priority];
- _dbg_assert_msg_(SCEKERNEL, cur->next != nullptr, "ThreadQueueList::Queue should already be linked up.");
-
- for (int i = cur->first; i < cur->end; ++i)
- {
- if (cur->data[i] == threadID)
- {
- // How many more after this one?
- int remaining = cur->end - i;
- // If there are more, move them into place.
- if (remaining > 0)
- memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(SceUID));
-
- // Now we're one shorter.
- --cur->end;
- return;
- }
- }
-
- // Wasn't there.
- }
-
- inline void rotate(u32 priority)
- {
- Queue *cur = &queues[priority];
- _dbg_assert_msg_(SCEKERNEL, cur->next != nullptr, "ThreadQueueList::Queue should already be linked up.");
-
- if (cur->size() > 1)
- {
- // Grab the front and push it on the end.
- cur->data[cur->end++] = cur->data[cur->first++];
- if (cur->full())
- rebalance(priority);
- }
- }
-
- inline void clear()
- {
- for (int i = 0; i < NUM_QUEUES; ++i)
- {
- if (queues[i].data != nullptr)
- free(queues[i].data);
- }
- memset(queues, 0, sizeof(queues));
- first = invalid();
- }
-
- inline bool empty(u32 priority) const
- {
- const Queue *cur = &queues[priority];
- return cur->empty();
- }
-
- inline void prepare(u32 priority)
- {
- Queue *cur = &queues[priority];
- if (cur->next == nullptr)
- link(priority, INITIAL_CAPACITY);
- }
-
- void DoState(PointerWrap &p)
- {
- auto s = p.Section("ThreadQueueList", 1);
- if (!s)
- return;
-
- int numQueues = NUM_QUEUES;
- p.Do(numQueues);
- if (numQueues != NUM_QUEUES)
- {
- p.SetError(p.ERROR_FAILURE);
- ERROR_LOG(SCEKERNEL, "Savestate loading error: invalid data");
- return;
- }
-
- if (p.mode == p.MODE_READ)
- clear();
-
- for (int i = 0; i < NUM_QUEUES; ++i)
- {
- Queue *cur = &queues[i];
- int size = cur->size();
- p.Do(size);
- int capacity = cur->capacity;
- p.Do(capacity);
-
- if (capacity == 0)
- continue;
-
- if (p.mode == p.MODE_READ)
- {
- link(i, capacity);
- cur->first = (cur->capacity - size) / 2;
- cur->end = cur->first + size;
- }
-
- if (size != 0)
- p.DoArray(&cur->data[cur->first], size);
- }
- }
-
-private:
- Queue *invalid() const
- {
- return (Queue *) -1;
- }
-
- // Initialize a priority level and link to other queues.
- void link(u32 priority, int size)
- {
- _dbg_assert_msg_(SCEKERNEL, queues[priority].data == nullptr, "ThreadQueueList::Queue should only be initialized once.");
-
- // Make sure we stay a multiple of INITIAL_CAPACITY.
- if (size <= INITIAL_CAPACITY)
- size = INITIAL_CAPACITY;
- else
- {
- int goal = size;
- size = INITIAL_CAPACITY;
- while (size < goal)
- size *= 2;
- }
-
- // Allocate the queue.
- Queue *cur = &queues[priority];
- cur->data = (SceUID *) malloc(sizeof(SceUID) * size);
- cur->capacity = size;
- // Start smack in the middle so it can move both directions.
- cur->first = size / 2;
- cur->end = size / 2;
-
- for (int i = (int) priority - 1; i >= 0; --i)
- {
- // This queue is before ours, and points past us.
- // We'll have it point to our new queue, inserting into the chain.
- if (queues[i].next != nullptr)
- {
- cur->next = queues[i].next;
- queues[i].next = cur;
- return;
- }
- }
-
- // Never found above - that means there's no better queue yet.
- // The new one is now first, and whoever was first is after it.
- cur->next = first;
- first = cur;
- }
-
- // Move or allocate as necessary to maintain free space on both sides.
- void rebalance(u32 priority)
- {
- Queue *cur = &queues[priority];
- int size = cur->size();
- // Basically full. Time for a larger queue?
- if (size >= cur->capacity - 2)
- {
- int new_capacity = cur->capacity * 2;
- SceUID *new_data = (SceUID *)realloc(cur->data, new_capacity * sizeof(SceUID));
- if (new_data != nullptr)
- {
- // Success, it's bigger now.
- cur->capacity = new_capacity;
- cur->data = new_data;
- }
- }
-
- // If we center all the items, it should start here.
- int newFirst = (cur->capacity - size) / 2;
- if (newFirst != cur->first)
- {
- memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(SceUID));
- cur->first = newFirst;
- cur->end = newFirst + size;
- }
- }
-
- // The first queue that's ever been used.
- Queue *first;
- // The priority level queues of thread ids.
- Queue queues[NUM_QUEUES];
-};
-
struct WaitTypeFuncs
{
WaitBeginCallbackFunc beginFunc;