CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/HLE/ThreadQueueList.h
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#pragma once1819#include "Core/HLE/sceKernel.h"20#include "Common/Serialize/Serializer.h"2122struct ThreadQueueList {23// Number of queues (number of priority levels starting at 0.)24static const int NUM_QUEUES = 128;25// Initial number of threads a single queue can handle.26static const int INITIAL_CAPACITY = 32;2728struct Queue {29// Next ever-been-used queue (worse priority.)30Queue *next;31// First valid item in data.32int first;33// One after last valid item in data.34int end;35// A too-large array with room on the front and end.36SceUID *data;37// Size of data array.38int capacity;3940inline int size() const {41return end - first;42}43inline bool empty() const {44return first == end;45}46inline int full() const {47return end == capacity;48}49};5051ThreadQueueList() {52memset(queues, 0, sizeof(queues));53first = invalid();54}5556~ThreadQueueList() {57clear();58}5960// Only for debugging, returns priority level.61int contains(const SceUID uid) {62for (int i = 0; i < NUM_QUEUES; ++i) {63if (queues[i].data == nullptr)64continue;6566Queue *cur = &queues[i];67for (int j = cur->first; j < cur->end; ++j) {68if (cur->data[j] == uid)69return i;70}71}7273return -1;74}7576inline SceUID pop_first() {77Queue *cur = first;78while (cur != invalid()) {79if (cur->size() > 0)80return cur->data[cur->first++];81cur = cur->next;82}8384_dbg_assert_msg_(false, "ThreadQueueList should not be empty.");85return 0;86}8788inline SceUID pop_first_better(u32 priority) {89Queue *cur = first;90// Don't bother looking past (worse than) this priority.91Queue *stop = &queues[priority];92while (cur < stop) {93if (cur->size() > 0)94return cur->data[cur->first++];95cur = cur->next;96}9798return 0;99}100101inline SceUID peek_first() {102Queue *cur = first;103while (cur != invalid()) {104if (cur->size() > 0)105return cur->data[cur->first];106cur = cur->next;107}108109return 0;110}111112inline void push_front(u32 priority, const SceUID threadID) {113Queue *cur = &queues[priority];114cur->data[--cur->first] = threadID;115// If we ran out of room toward the front, add more room for next time.116if (cur->first == 0)117rebalance(priority);118}119120inline void push_back(u32 priority, const SceUID threadID) {121Queue *cur = &queues[priority];122cur->data[cur->end++] = threadID;123if (cur->full())124rebalance(priority);125}126127inline void remove(u32 priority, const SceUID threadID) {128Queue *cur = &queues[priority];129_dbg_assert_msg_(cur->next != nullptr, "ThreadQueueList::Queue should already be linked up.");130131for (int i = cur->first; i < cur->end; ++i) {132if (cur->data[i] == threadID) {133// How many more after this one?134int remaining = cur->end - i;135// If there are more, move them into place.136if (remaining > 0)137memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(SceUID));138139// Now we're one shorter.140--cur->end;141return;142}143}144145// Wasn't there.146}147148inline void rotate(u32 priority) {149Queue *cur = &queues[priority];150_dbg_assert_msg_(cur->next != nullptr, "ThreadQueueList::Queue should already be linked up.");151152if (cur->size() > 1) {153// Grab the front and push it on the end.154cur->data[cur->end++] = cur->data[cur->first++];155if (cur->full())156rebalance(priority);157}158}159160inline void clear() {161for (int i = 0; i < NUM_QUEUES; ++i) {162free(queues[i].data);163}164memset(queues, 0, sizeof(queues));165first = invalid();166}167168inline bool empty(u32 priority) const {169const Queue *cur = &queues[priority];170return cur->empty();171}172173inline void prepare(u32 priority) {174Queue *cur = &queues[priority];175if (cur->next == nullptr)176link(priority, INITIAL_CAPACITY);177}178179void DoState(PointerWrap &p) {180auto s = p.Section("ThreadQueueList", 1);181if (!s)182return;183184int numQueues = NUM_QUEUES;185Do(p, numQueues);186if (numQueues != NUM_QUEUES) {187p.SetError(p.ERROR_FAILURE);188ERROR_LOG(Log::sceKernel, "Savestate loading error: invalid data");189return;190}191192if (p.mode == p.MODE_READ)193clear();194195for (int i = 0; i < NUM_QUEUES; ++i) {196Queue *cur = &queues[i];197int size = cur->size();198Do(p, size);199int capacity = cur->capacity;200Do(p, capacity);201202if (capacity == 0)203continue;204205if (p.mode == p.MODE_READ) {206link(i, capacity);207cur->first = (cur->capacity - size) / 2;208cur->end = cur->first + size;209}210211if (size != 0)212DoArray(p, &cur->data[cur->first], size);213}214}215216private:217Queue *invalid() const {218return (Queue *)-1;219}220221// Initialize a priority level and link to other queues.222void link(u32 priority, int size) {223_dbg_assert_msg_(queues[priority].data == nullptr, "ThreadQueueList::Queue should only be initialized once.");224225// Make sure we stay a multiple of INITIAL_CAPACITY.226if (size <= INITIAL_CAPACITY)227size = INITIAL_CAPACITY;228else {229int goal = size;230size = INITIAL_CAPACITY;231while (size < goal)232size *= 2;233}234235// Allocate the queue.236Queue *cur = &queues[priority];237cur->data = (SceUID *)malloc(sizeof(SceUID) * size);238cur->capacity = size;239// Start smack in the middle so it can move both directions.240cur->first = size / 2;241cur->end = size / 2;242243for (int i = (int)priority - 1; i >= 0; --i) {244// This queue is before ours, and points past us.245// We'll have it point to our new queue, inserting into the chain.246if (queues[i].next != nullptr) {247cur->next = queues[i].next;248queues[i].next = cur;249return;250}251}252253// Never found above - that means there's no better queue yet.254// The new one is now first, and whoever was first is after it.255cur->next = first;256first = cur;257}258259// Move or allocate as necessary to maintain free space on both sides.260void rebalance(u32 priority) {261Queue *cur = &queues[priority];262int size = cur->size();263// Basically full. Time for a larger queue?264if (size >= cur->capacity - 2) {265int new_capacity = cur->capacity * 2;266SceUID *new_data = (SceUID *)realloc(cur->data, new_capacity * sizeof(SceUID));267if (new_data != nullptr) {268// Success, it's bigger now.269cur->capacity = new_capacity;270cur->data = new_data;271}272}273274// If we center all the items, it should start here.275int newFirst = (cur->capacity - size) / 2;276if (newFirst != cur->first) {277memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(SceUID));278cur->first = newFirst;279cur->end = newFirst + size;280}281}282283// The first queue that's ever been used.284Queue *first;285// The priority level queues of thread ids.286Queue queues[NUM_QUEUES];287};288289290