summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel/kernel.h
diff options
context:
space:
mode:
authorGravatar Yuri Kunde Schlesner2014-12-21 10:04:08 -0200
committerGravatar Yuri Kunde Schlesner2014-12-28 11:52:55 -0200
commit7e2903cb74050d846f2da951dff7e84aee13761b (patch)
tree621c9245d2dd393a9569b1b4192f50a27d831972 /src/core/hle/kernel/kernel.h
parentKernel: Replace GetStaticHandleType by HANDLE_TYPE constants (diff)
downloadyuzu-7e2903cb74050d846f2da951dff7e84aee13761b.tar.gz
yuzu-7e2903cb74050d846f2da951dff7e84aee13761b.tar.xz
yuzu-7e2903cb74050d846f2da951dff7e84aee13761b.zip
Kernel: New handle manager
This handle manager more closely mirrors the behaviour of the CTR-OS one. In addition object ref-counts and support for DuplicateHandle have been added. Note that support for DuplicateHandle is still experimental, since parts of the kernel still use Handles internally, which will likely cause troubles if two different handles to the same object are used to e.g. wait on a synchronization primitive.
Diffstat (limited to 'src/core/hle/kernel/kernel.h')
-rw-r--r--src/core/hle/kernel/kernel.h190
1 files changed, 109 insertions, 81 deletions
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 27c406ad4..7f86fd07d 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -12,13 +12,17 @@
12typedef u32 Handle; 12typedef u32 Handle;
13typedef s32 Result; 13typedef s32 Result;
14 14
15const Handle INVALID_HANDLE = 0;
16
15namespace Kernel { 17namespace Kernel {
16 18
17// From kernel.h. Declarations duplicated here to avoid a circular header dependency. 19// TODO: Verify code
18class Thread; 20const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel,
19Thread* GetCurrentThread(); 21 ErrorSummary::OutOfResource, ErrorLevel::Temporary);
22// TOOD: Verify code
23const ResultCode ERR_INVALID_HANDLE = InvalidHandle(ErrorModule::Kernel);
20 24
21enum KernelHandle { 25enum KernelHandle : Handle {
22 CurrentThread = 0xFFFF8000, 26 CurrentThread = 0xFFFF8000,
23 CurrentProcess = 0xFFFF8001, 27 CurrentProcess = 0xFFFF8001,
24}; 28};
@@ -61,103 +65,127 @@ public:
61 LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); 65 LOG_ERROR(Kernel, "(UNIMPLEMENTED)");
62 return UnimplementedFunction(ErrorModule::Kernel); 66 return UnimplementedFunction(ErrorModule::Kernel);
63 } 67 }
64};
65 68
66class HandleTable : NonCopyable { 69private:
67public: 70 friend void intrusive_ptr_add_ref(Object*);
68 HandleTable(); 71 friend void intrusive_ptr_release(Object*);
69 ~HandleTable() {}
70 72
71 // Allocates a handle within the range and inserts the object into the map. 73 unsigned int ref_count = 0;
72 Handle Create(Object* obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF); 74};
73 75
74 static Object* CreateByIDType(int type); 76// Special functions that will later be used by boost::instrusive_ptr to do automatic ref-counting
77inline void intrusive_ptr_add_ref(Object* object) {
78 ++object->ref_count;
79}
75 80
76 template <class T> 81inline void intrusive_ptr_release(Object* object) {
77 void Destroy(Handle handle) { 82 if (--object->ref_count == 0) {
78 if (Get<T>(handle)) { 83 delete object;
79 occupied[handle - HANDLE_OFFSET] = false;
80 delete pool[handle - HANDLE_OFFSET];
81 }
82 } 84 }
85}
83 86
84 bool IsValid(Handle handle) const; 87/**
88 * This class allows the creation of Handles, which are references to objects that can be tested
89 * for validity and looked up. Here they are used to pass references to kernel objects to/from the
90 * emulated process. it has been designed so that it follows the same handle format and has
91 * approximately the same restrictions as the handle manager in the CTR-OS.
92 *
93 * Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0).
94 * The slot index is used to index into the arrays in this class to access the data corresponding
95 * to the Handle.
96 *
97 * To prevent accidental use of a freed Handle whose slot has already been reused, a global counter
98 * is kept and incremented every time a Handle is created. This is the Handle's "generation". The
99 * value of the counter is stored into the Handle as well as in the handle table (in the
100 * "generations" array). When looking up a handle, the Handle's generation must match with the
101 * value stored on the class, otherwise the Handle is considered invalid.
102 *
103 * To find free slots when allocating a Handle without needing to scan the entire object array, the
104 * generations field of unallocated slots is re-purposed as a linked list of indices to free slots.
105 * When a Handle is created, an index is popped off the list and used for the new Handle. When it
106 * is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is
107 * likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been
108 * verified and isn't likely to cause any problems.
109 */
110class HandleTable final : NonCopyable {
111public:
112 HandleTable();
85 113
86 template <class T> 114 /**
87 T* Get(Handle handle) { 115 * Allocates a handle for the given object.
88 if (handle == CurrentThread) { 116 * @return The created Handle or one of the following errors:
89 return reinterpret_cast<T*>(GetCurrentThread()); 117 * - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded.
90 } 118 */
119 ResultVal<Handle> Create(Object* obj);
91 120
92 if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) { 121 /**
93 if (handle != 0) { 122 * Returns a new handle that points to the same object as the passed in handle.
94 LOG_ERROR(Kernel, "Bad object handle %08x", handle); 123 * @return The duplicated Handle or one of the following errors:
95 } 124 * - `ERR_INVALID_HANDLE`: an invalid handle was passed in.
96 return nullptr; 125 * - Any errors returned by `Create()`.
97 } else { 126 */
98 Object* t = pool[handle - HANDLE_OFFSET]; 127 ResultVal<Handle> Duplicate(Handle handle);
99 if (t->GetHandleType() != T::HANDLE_TYPE) {
100 LOG_ERROR(Kernel, "Wrong object type for %08x", handle);
101 return nullptr;
102 }
103 return static_cast<T*>(t);
104 }
105 }
106 128
107 // ONLY use this when you know the handle is valid. 129 /**
108 template <class T> 130 * Closes a handle, removing it from the table and decreasing the object's ref-count.
109 T *GetFast(Handle handle) { 131 * @return `RESULT_SUCCESS` or one of the following errors:
110 if (handle == CurrentThread) { 132 * - `ERR_INVALID_HANDLE`: an invalid handle was passed in.
111 return reinterpret_cast<T*>(GetCurrentThread()); 133 */
112 } 134 ResultCode Close(Handle handle);
113 135
114 const Handle realHandle = handle - HANDLE_OFFSET; 136 /// Checks if a handle is valid and points to an existing object.
115 _dbg_assert_(Kernel, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]); 137 bool IsValid(Handle handle) const;
116 return static_cast<T*>(pool[realHandle]);
117 }
118 138
119 template <class T, typename ArgT> 139 /**
120 void Iterate(bool func(T*, ArgT), ArgT arg) { 140 * Looks up a handle.
121 int type = T::GetStaticIDType(); 141 * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid.
122 for (int i = 0; i < MAX_COUNT; i++) 142 */
123 { 143 Object* GetGeneric(Handle handle) const;
124 if (!occupied[i])
125 continue;
126 T* t = static_cast<T*>(pool[i]);
127 if (t->GetIDType() == type) {
128 if (!func(t, arg))
129 break;
130 }
131 }
132 }
133 144
134 bool GetIDType(Handle handle, HandleType* type) const { 145 /**
135 if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) || 146 * Looks up a handle while verifying its type.
136 !occupied[handle - HANDLE_OFFSET]) { 147 * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid or its
137 LOG_ERROR(Kernel, "Bad object handle %08X", handle); 148 * type differs from the handle type `T::HANDLE_TYPE`.
138 return false; 149 */
150 template <class T>
151 T* Get(Handle handle) const {
152 Object* object = GetGeneric(handle);
153 if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
154 return static_cast<T*>(object);
139 } 155 }
140 Object* t = pool[handle - HANDLE_OFFSET]; 156 return nullptr;
141 *type = t->GetHandleType();
142 return true;
143 } 157 }
144 158
145 Object* &operator [](Handle handle); 159 /// Closes all handles held in this table.
146 void List();
147 void Clear(); 160 void Clear();
148 int GetCount() const;
149 161
150private: 162private:
163 /**
164 * This is the maximum limit of handles allowed per process in CTR-OS. It can be further
165 * reduced by ExHeader values, but this is not emulated here.
166 */
167 static const size_t MAX_COUNT = 4096;
168
169 static size_t GetSlot(Handle handle) { return handle >> 15; }
170 static u16 GetGeneration(Handle handle) { return handle & 0x7FFF; }
171
172 /// Stores the Object referenced by the handle or null if the slot is empty.
173 std::array<Object*, MAX_COUNT> objects;
151 174
152 enum { 175 /**
153 MAX_COUNT = 0x1000, 176 * The value of `next_generation` when the handle was created, used to check for validity. For
154 HANDLE_OFFSET = 0x100, 177 * empty slots, contains the index of the next free slot in the list.
155 INITIAL_NEXT_ID = 0x10, 178 */
156 }; 179 std::array<u16, MAX_COUNT> generations;
180
181 /**
182 * Global counter of the number of created handles. Stored in `generations` when a handle is
183 * created, and wraps around to 1 when it hits 0x8000.
184 */
185 u16 next_generation;
157 186
158 std::array<Object*, MAX_COUNT> pool; 187 /// Head of the free slots linked list.
159 std::array<bool, MAX_COUNT> occupied; 188 u16 next_free_slot;
160 int next_id;
161}; 189};
162 190
163extern HandleTable g_handle_table; 191extern HandleTable g_handle_table;