diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/common.h | 2 | ||||
-rw-r--r-- | src/common/common.vcxproj | 4 | ||||
-rw-r--r-- | src/common/common.vcxproj.filters | 6 | ||||
-rw-r--r-- | src/common/common_funcs.h | 5 | ||||
-rw-r--r-- | src/common/console_listener.cpp | 16 | ||||
-rw-r--r-- | src/common/log.h | 45 | ||||
-rw-r--r-- | src/common/log_manager.cpp | 36 | ||||
-rw-r--r-- | src/common/log_manager.h | 6 | ||||
-rw-r--r-- | src/common/platform.h | 4 | ||||
-rw-r--r-- | src/common/register_set.h | 161 | ||||
-rw-r--r-- | src/common/thread_queue_list.h | 216 |
11 files changed, 445 insertions, 56 deletions
diff --git a/src/common/common.h b/src/common/common.h index 2578d0010..09027cae1 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -96,8 +96,6 @@ private: // Windows compatibility #ifndef _WIN32 -#include <limits.h> -#define MAX_PATH PATH_MAX #ifdef _LP64 #define _M_X64 1 #else diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index 5dc6ff790..1f5c714c3 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj @@ -182,6 +182,7 @@ <ClInclude Include="mem_arena.h" /> <ClInclude Include="msg_handler.h" /> <ClInclude Include="platform.h" /> + <ClInclude Include="register_set.h" /> <ClInclude Include="scm_rev.h" /> <ClInclude Include="std_condition_variable.h" /> <ClInclude Include="std_mutex.h" /> @@ -190,6 +191,7 @@ <ClInclude Include="swap.h" /> <ClInclude Include="symbols.h" /> <ClInclude Include="thread.h" /> + <ClInclude Include="thread_queue_list.h" /> <ClInclude Include="thunk.h" /> <ClInclude Include="timer.h" /> <ClInclude Include="utf8.h" /> @@ -220,4 +222,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project>
\ No newline at end of file +</Project> diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters index 268730228..e8c4ce360 100644 --- a/src/common/common.vcxproj.filters +++ b/src/common/common.vcxproj.filters @@ -4,6 +4,7 @@ <ClInclude Include="atomic.h" /> <ClInclude Include="atomic_gcc.h" /> <ClInclude Include="atomic_win32.h" /> + <ClInclude Include="bit_field.h" /> <ClInclude Include="break_points.h" /> <ClInclude Include="chunk_file.h" /> <ClInclude Include="common.h" /> @@ -28,6 +29,7 @@ <ClInclude Include="memory_util.h" /> <ClInclude Include="msg_handler.h" /> <ClInclude Include="platform.h" /> + <ClInclude Include="register_set.h" /> <ClInclude Include="std_condition_variable.h" /> <ClInclude Include="std_mutex.h" /> <ClInclude Include="std_thread.h" /> @@ -39,7 +41,7 @@ <ClInclude Include="utf8.h" /> <ClInclude Include="symbols.h" /> <ClInclude Include="scm_rev.h" /> - <ClInclude Include="bit_field.h" /> + <ClInclude Include="thread_queue_list.h" /> </ItemGroup> <ItemGroup> <ClCompile Include="break_points.cpp" /> @@ -64,4 +66,4 @@ <ItemGroup> <Text Include="CMakeLists.txt" /> </ItemGroup> -</Project>
\ No newline at end of file +</Project> diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index f8d10eb3e..dca4dc47f 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -22,6 +22,11 @@ template<> struct CompileTimeAssert<true> {}; #define b32(x) (b16(x) | (b16(x) >>16) ) #define ROUND_UP_POW2(x) (b32(x - 1) + 1) +#define MIN(a, b) ((a)<(b)?(a):(b)) +#define MAX(a, b) ((a)>(b)?(a):(b)) + +#define CLAMP(x, min, max) (((x) > max) ? max : (((x) < min) ? min : (x))) + #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #ifndef _WIN32 diff --git a/src/common/console_listener.cpp b/src/common/console_listener.cpp index b5f32d1bd..db48abbf6 100644 --- a/src/common/console_listener.cpp +++ b/src/common/console_listener.cpp @@ -259,14 +259,17 @@ void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text) switch (Level) { + case OS_LEVEL: // light yellow + Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; + break; case NOTICE_LEVEL: // light green Color = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; case ERROR_LEVEL: // light red Color = FOREGROUND_RED | FOREGROUND_INTENSITY; break; - case WARNING_LEVEL: // light yellow - Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; + case WARNING_LEVEL: // light purple + Color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break; case INFO_LEVEL: // cyan Color = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; @@ -278,15 +281,8 @@ void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text) Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break; } - if (strlen(Text) > 10) - { - // First 10 chars white - SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); - WriteConsole(hConsole, Text, 10, &cCharsWritten, NULL); - Text += 10; - } SetConsoleTextAttribute(hConsole, Color); - WriteConsole(hConsole, Text, (DWORD)strlen(Text), &cCharsWritten, NULL); + printf(Text); #else char ColorAttr[16] = ""; char ResetAttr[16] = ""; diff --git a/src/common/log.h b/src/common/log.h index d95f51f56..e923224ed 100644 --- a/src/common/log.h +++ b/src/common/log.h @@ -5,11 +5,16 @@ #ifndef _LOG_H_ #define _LOG_H_ -#define NOTICE_LEVEL 1 // VERY important information that is NOT errors. Like startup and OSReports. -#define ERROR_LEVEL 2 // Critical errors -#define WARNING_LEVEL 3 // Something is suspicious. -#define INFO_LEVEL 4 // General information. -#define DEBUG_LEVEL 5 // Detailed debugging - might make things slow. +#define LOGGING + +enum { + OS_LEVEL, // Printed by the emulated operating system + NOTICE_LEVEL, // VERY important information that is NOT errors. Like startup and OSReports. + ERROR_LEVEL, // Critical errors + WARNING_LEVEL, // Something is suspicious. + INFO_LEVEL, // General information. + DEBUG_LEVEL, // Detailed debugging - might make things slow. +}; namespace LogTypes { @@ -53,12 +58,12 @@ enum LOG_TYPE { WII_IPC_ES, WII_IPC_FILEIO, WII_IPC_HID, - WII_IPC_HLE, + KERNEL, SVC, NDMA, HLE, RENDER, - LCD, + GPU, HW, TIME, NETPLAY, @@ -68,6 +73,7 @@ enum LOG_TYPE { // FIXME: should this be removed? enum LOG_LEVELS { + LOS = OS_LEVEL, LNOTICE = NOTICE_LEVEL, LERROR = ERROR_LEVEL, LWARNING = WARNING_LEVEL, @@ -80,31 +86,34 @@ enum LOG_LEVELS { } // namespace -void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, - const char *file, int line, const char *fmt, ...) +void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int line, + const char* function, const char* fmt, ...) #ifdef __GNUC__ - __attribute__((format(printf, 5, 6))) + __attribute__((format(printf, 6, 7))) #endif ; #if defined LOGGING || defined _DEBUG || defined DEBUGFAST -#define MAX_LOGLEVEL DEBUG_LEVEL +#define MAX_LOGLEVEL LDEBUG #else #ifndef MAX_LOGLEVEL -#define MAX_LOGLEVEL WARNING_LEVEL +#define MAX_LOGLEVEL LWARNING #endif // loglevel #endif // logging -#ifdef GEKKO -#define GENERIC_LOG(t, v, ...) -#else +#ifdef _WIN32 +#ifndef __func__ +#define __func__ __FUNCTION__ +#endif +#endif + // Let the compiler optimize this out #define GENERIC_LOG(t, v, ...) { \ - if (v <= MAX_LOGLEVEL) \ - GenericLog(v, t, __FILE__, __LINE__, __VA_ARGS__); \ + if (v <= LogTypes::MAX_LOGLEVEL) \ + GenericLog(v, t, __FILE__, __LINE__, __func__, __VA_ARGS__); \ } -#endif +#define OS_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LOS, __VA_ARGS__) } while (0) #define ERROR_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LERROR, __VA_ARGS__) } while (0) #define WARN_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LWARNING, __VA_ARGS__) } while (0) #define NOTICE_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LNOTICE, __VA_ARGS__) } while (0) diff --git a/src/common/log_manager.cpp b/src/common/log_manager.cpp index 80fd473b9..4e1cb60bd 100644 --- a/src/common/log_manager.cpp +++ b/src/common/log_manager.cpp @@ -10,14 +10,16 @@ #include "common/thread.h" #include "common/file_util.h" -void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, - const char *file, int line, const char* fmt, ...) +void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line, + const char* function, const char* fmt, ...) { va_list args; va_start(args, fmt); - if (LogManager::GetInstance()) + + if (LogManager::GetInstance()) { LogManager::GetInstance()->Log(level, type, - file, line, fmt, args); + file, line, function, fmt, args); + } va_end(args); } @@ -60,13 +62,13 @@ LogManager::LogManager() m_Log[LogTypes::LOADER] = new LogContainer("Loader", "Loader"); m_Log[LogTypes::FILESYS] = new LogContainer("FileSys", "File System"); m_Log[LogTypes::WII_IPC_HID] = new LogContainer("WII_IPC_HID", "WII IPC HID"); - m_Log[LogTypes::WII_IPC_HLE] = new LogContainer("WII_IPC_HLE", "WII IPC HLE"); + m_Log[LogTypes::KERNEL] = new LogContainer("KERNEL", "KERNEL HLE"); m_Log[LogTypes::WII_IPC_DVD] = new LogContainer("WII_IPC_DVD", "WII IPC DVD"); m_Log[LogTypes::WII_IPC_ES] = new LogContainer("WII_IPC_ES", "WII IPC ES"); m_Log[LogTypes::WII_IPC_FILEIO] = new LogContainer("WII_IPC_FILEIO", "WII IPC FILEIO"); m_Log[LogTypes::RENDER] = new LogContainer("RENDER", "RENDER"); - m_Log[LogTypes::LCD] = new LogContainer("LCD", "LCD"); - m_Log[LogTypes::SVC] = new LogContainer("SVC", "Supervisor Call"); + m_Log[LogTypes::GPU] = new LogContainer("GPU", "GPU"); + m_Log[LogTypes::SVC] = new LogContainer("SVC", "Supervisor Call HLE"); m_Log[LogTypes::NDMA] = new LogContainer("NDMA", "NDMA"); m_Log[LogTypes::HLE] = new LogContainer("HLE", "High Level Emulation"); m_Log[LogTypes::HW] = new LogContainer("HW", "Hardware"); @@ -88,6 +90,8 @@ LogManager::LogManager() m_Log[i]->AddListener(m_debuggerLog); #endif } + + m_consoleLog->Open(); } LogManager::~LogManager() @@ -107,8 +111,8 @@ LogManager::~LogManager() delete m_debuggerLog; } -void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, - const char *file, int line, const char *format, va_list args) +void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, + int line, const char* function, const char *fmt, va_list args) { char temp[MAX_MSGLEN]; char msg[MAX_MSGLEN * 2]; @@ -117,17 +121,15 @@ void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, if (!log->IsEnabled() || level > log->GetLevel() || ! log->HasListeners()) return; - CharArrayFromFormatV(temp, MAX_MSGLEN, format, args); + CharArrayFromFormatV(temp, MAX_MSGLEN, fmt, args); - static const char level_to_char[7] = "-NEWID"; - sprintf(msg, "%s %s:%u %c[%s]: %s\n", - Common::Timer::GetTimeFormatted().c_str(), - file, line, level_to_char[(int)level], - log->GetShortName(), temp); + static const char level_to_char[7] = "ONEWID"; + sprintf(msg, "%s %s:%u %c[%s] %s: %s\n", Common::Timer::GetTimeFormatted().c_str(), file, line, + level_to_char[(int)level], log->GetShortName(), function, temp); + #ifdef ANDROID Host_SysMessage(msg); #endif - printf(msg); // TODO(ShizZy): RemoveMe when I no longer need this log->Trigger(level, msg); } @@ -147,7 +149,7 @@ LogContainer::LogContainer(const char* shortName, const char* fullName, bool ena { strncpy(m_fullName, fullName, 128); strncpy(m_shortName, shortName, 32); - m_level = (LogTypes::LOG_LEVELS)MAX_LOGLEVEL; + m_level = LogTypes::MAX_LOGLEVEL; } // LogContainer diff --git a/src/common/log_manager.h b/src/common/log_manager.h index 580860b4d..6d3d7c7ff 100644 --- a/src/common/log_manager.h +++ b/src/common/log_manager.h @@ -97,10 +97,10 @@ private: ~LogManager(); public: - static u32 GetMaxLevel() { return MAX_LOGLEVEL; } + static u32 GetMaxLevel() { return LogTypes::MAX_LOGLEVEL; } - void Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, - const char *file, int line, const char *fmt, va_list args); + void Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line, + const char* function, const char *fmt, va_list args); void SetLogLevel(LogTypes::LOG_TYPE type, LogTypes::LOG_LEVELS level) { diff --git a/src/common/platform.h b/src/common/platform.h index 1e8dffbd4..b02b52cd2 100644 --- a/src/common/platform.h +++ b/src/common/platform.h @@ -47,7 +47,7 @@ #define EMU_PLATFORM PLATFORM_WINDOWS #elif defined( __APPLE__ ) || defined( __APPLE_CC__ ) -#define EMU_PLATFORM PLATFORM_MAXOSX +#define EMU_PLATFORM PLATFORM_MACOSX #elif defined(__linux__) #define EMU_PLATFORM PLATFORM_LINUX @@ -87,7 +87,6 @@ inline struct tm* localtime_r(const time_t *clock, struct tm *result) { #define __stdcall #define __cdecl -#define LONG long #define BOOL bool #define DWORD u32 @@ -97,7 +96,6 @@ inline struct tm* localtime_r(const time_t *clock, struct tm *result) { // TODO: Hacks.. #include <limits.h> -#define MAX_PATH PATH_MAX #include <strings.h> #define stricmp(str1, str2) strcasecmp(str1, str2) diff --git a/src/common/register_set.h b/src/common/register_set.h new file mode 100644 index 000000000..0418551b3 --- /dev/null +++ b/src/common/register_set.h @@ -0,0 +1,161 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +// Copyright 2014 Tony Wasserka +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the owner nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* + * Standardized way to define a group of registers and corresponding data structures. To define + * a new register set, first define struct containing an enumeration called "Id" containing + * all register IDs and a template union called "Struct". Specialize the Struct union for any + * register ID which needs to be accessed in a specialized way. You can then declare the object + * containing all register values using the RegisterSet<BaseType, DefiningStruct> type, where + * BaseType is the underlying type of each register (e.g. u32). + * Of course, you'll usually want to implement the Struct template such that they are of the same + * size as BaseType. However, it's also possible to make it larger, e.g. when you want to describe + * multiple registers with the same structure. + * + * Example: + * + * struct Regs { + * enum Id : u32 { + * Value1 = 0, + * Value2 = 1, + * Value3 = 2, + * NumIds = 3 + * }; + * + * // declare register definition structures + * template<Id id> + * union Struct; + * }; + * + * // Define register set object + * RegisterSet<u32, CommandIds> registers; + * + * // define register definition structures + * template<> + * union Regs::Struct<Regs::Value1> { + * BitField<0, 4, u32> some_field; + * BitField<4, 3, u32> some_other_field; + * }; + * + * Usage in external code (within SomeNamespace scope): + * + * For a register which maps to a single index: + * registers.Get<Regs::Value1>().some_field = some_value; + * + * For a register which maps to different indices, e.g. a group of similar registers + * registers.Get<Regs::Value1>(index).some_field = some_value; + * + * + * @tparam BaseType Base type used for storing individual registers, e.g. u32 + * @tparam RegDefinition Class defining an enumeration called "Id" and a template<Id id> union, as described above. + * @note RegDefinition::Id needs to have an enum value called NumIds defining the number of registers to be allocated. + */ +template<typename BaseType, typename RegDefinition> +struct RegisterSet { + // Register IDs + using Id = typename RegDefinition::Id; + + // type used for *this + using ThisType = RegisterSet<BaseType, RegDefinition>; + + // Register definition structs, defined in RegDefinition + template<Id id> + using Struct = typename RegDefinition::template Struct<id>; + + + /* + * Lookup register with the given id and return it as the corresponding structure type. + * @note This just forwards the arguments to Get(Id). + */ + template<Id id> + const Struct<id>& Get() const { + return Get<id>(id); + } + + /* + * Lookup register with the given id and return it as the corresponding structure type. + * @note This just forwards the arguments to Get(Id). + */ + template<Id id> + Struct<id>& Get() { + return Get<id>(id); + } + + /* + * Lookup register with the given index and return it as the corresponding structure type. + * @todo Is this portable with regards to structures larger than BaseType? + * @note if index==id, you don't need to specify the function parameter. + */ + template<Id id> + const Struct<id>& Get(const Id& index) const { + const int idx = static_cast<size_t>(index); + return *reinterpret_cast<const Struct<id>*>(&raw[idx]); + } + + /* + * Lookup register with the given index and return it as the corresponding structure type. + * @note This just forwards the arguments to the const version of Get(Id). + * @note if index==id, you don't need to specify the function parameter. + */ + template<Id id> + Struct<id>& Get(const Id& index) { + return const_cast<Struct<id>&>(GetThis().Get<id>(index)); + } + + /* + * Plain array access. + * @note If you want to have this casted to a register defininition struct, use Get() instead. + */ + const BaseType& operator[] (const Id& id) const { + return raw[static_cast<size_t>(id)]; + } + + /* + * Plain array access. + * @note If you want to have this casted to a register defininition struct, use Get() instead. + * @note This operator just forwards its argument to the const version. + */ + BaseType& operator[] (const Id& id) { + return const_cast<BaseType&>(GetThis()[id]); + } + +private: + /* + * Returns a const reference to "this". + */ + const ThisType& GetThis() const { + return static_cast<const ThisType&>(*this); + } + + BaseType raw[Id::NumIds]; +}; diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h new file mode 100644 index 000000000..4a89572f6 --- /dev/null +++ b/src/common/thread_queue_list.h @@ -0,0 +1,216 @@ +// Copyright 2014 Citra Emulator Project / PPSSPP Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "common/common.h" + +namespace Common { + +template<class IdType> +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. + IdType *data; + // Size of data array. + int capacity; + }; + + ThreadQueueList() { + memset(queues, 0, sizeof(queues)); + first = invalid(); + } + + ~ThreadQueueList() { + for (int i = 0; i < NUM_QUEUES; ++i) + { + if (queues[i].data != NULL) + free(queues[i].data); + } + } + + // Only for debugging, returns priority level. + int contains(const IdType uid) { + for (int i = 0; i < NUM_QUEUES; ++i) + { + if (queues[i].data == NULL) + continue; + + Queue *cur = &queues[i]; + for (int j = cur->first; j < cur->end; ++j) + { + if (cur->data[j] == uid) + return i; + } + } + + return -1; + } + + inline IdType pop_first() { + Queue *cur = first; + while (cur != invalid()) + { + if (cur->end - cur->first > 0) + return cur->data[cur->first++]; + cur = cur->next; + } + + //_dbg_assert_msg_(SCEKERNEL, false, "ThreadQueueList should not be empty."); + return 0; + } + + inline IdType pop_first_better(u32 priority) { + Queue *cur = first; + Queue *stop = &queues[priority]; + while (cur < stop) + { + if (cur->end - cur->first > 0) + return cur->data[cur->first++]; + cur = cur->next; + } + + return 0; + } + + inline void push_front(u32 priority, const IdType threadID) { + Queue *cur = &queues[priority]; + cur->data[--cur->first] = threadID; + if (cur->first == 0) + rebalance(priority); + } + + inline void push_back(u32 priority, const IdType threadID) { + Queue *cur = &queues[priority]; + cur->data[cur->end++] = threadID; + if (cur->end == cur->capacity) + rebalance(priority); + } + + inline void remove(u32 priority, const IdType threadID) { + Queue *cur = &queues[priority]; + //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); + + for (int i = cur->first; i < cur->end; ++i) + { + if (cur->data[i] == threadID) + { + int remaining = --cur->end - i; + if (remaining > 0) + memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(IdType)); + return; + } + } + + // Wasn't there. + } + + inline void rotate(u32 priority) { + Queue *cur = &queues[priority]; + //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); + + if (cur->end - cur->first > 1) + { + cur->data[cur->end++] = cur->data[cur->first++]; + if (cur->end == cur->capacity) + rebalance(priority); + } + } + + inline void clear() { + for (int i = 0; i < NUM_QUEUES; ++i) + { + if (queues[i].data != NULL) + free(queues[i].data); + } + memset(queues, 0, sizeof(queues)); + first = invalid(); + } + + inline bool empty(u32 priority) const { + const Queue *cur = &queues[priority]; + return cur->first == cur->end; + } + + inline void prepare(u32 priority) { + Queue *cur = &queues[priority]; + if (cur->next == NULL) + link(priority, INITIAL_CAPACITY); + } + +private: + Queue *invalid() const { + return (Queue *) -1; + } + + void link(u32 priority, int size) { + //_dbg_assert_msg_(SCEKERNEL, queues[priority].data == NULL, "ThreadQueueList::Queue should only be initialized once."); + + if (size <= INITIAL_CAPACITY) + size = INITIAL_CAPACITY; + else + { + int goal = size; + size = INITIAL_CAPACITY; + while (size < goal) + size *= 2; + } + Queue *cur = &queues[priority]; + cur->data = (IdType *) malloc(sizeof(IdType) * size); + cur->capacity = size; + cur->first = size / 2; + cur->end = size / 2; + + for (int i = (int) priority - 1; i >= 0; --i) + { + if (queues[i].next != NULL) + { + cur->next = queues[i].next; + queues[i].next = cur; + return; + } + } + + cur->next = first; + first = cur; + } + + void rebalance(u32 priority) { + Queue *cur = &queues[priority]; + int size = cur->end - cur->first; + if (size >= cur->capacity - 2) { + IdType *new_data = (IdType *)realloc(cur->data, cur->capacity * 2 * sizeof(IdType)); + if (new_data != NULL) { + cur->capacity *= 2; + cur->data = new_data; + } + } + + int newFirst = (cur->capacity - size) / 2; + if (newFirst != cur->first) { + memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(IdType)); + 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]; +}; + +} // namespace |