summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
authorGravatar darkf2014-12-29 19:47:41 -0800
committerGravatar darkf2014-12-29 19:47:41 -0800
commit8ba9ac0f74abb0408a26207a76a0c1808bad8de0 (patch)
treef1c7c3393fa726435b5b90bf335567c93e528ef1 /src/core/hle/kernel
parentAdd comment regarding __WIN32__ in SkyEye code (diff)
parentMerge pull request #367 from bunnei/usat_ssat (diff)
downloadyuzu-8ba9ac0f74abb0408a26207a76a0c1808bad8de0.tar.gz
yuzu-8ba9ac0f74abb0408a26207a76a0c1808bad8de0.tar.xz
yuzu-8ba9ac0f74abb0408a26207a76a0c1808bad8de0.zip
Fix merge conflicts
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp19
-rw-r--r--src/core/hle/kernel/address_arbiter.h2
-rw-r--r--src/core/hle/kernel/archive.cpp429
-rw-r--r--src/core/hle/kernel/archive.h85
-rw-r--r--src/core/hle/kernel/event.cpp17
-rw-r--r--src/core/hle/kernel/event.h2
-rw-r--r--src/core/hle/kernel/kernel.cpp139
-rw-r--r--src/core/hle/kernel/kernel.h217
-rw-r--r--src/core/hle/kernel/mutex.cpp108
-rw-r--r--src/core/hle/kernel/mutex.h8
-rw-r--r--src/core/hle/kernel/semaphore.cpp95
-rw-r--r--src/core/hle/kernel/semaphore.h32
-rw-r--r--src/core/hle/kernel/session.h58
-rw-r--r--src/core/hle/kernel/shared_memory.cpp23
-rw-r--r--src/core/hle/kernel/shared_memory.h16
-rw-r--r--src/core/hle/kernel/thread.cpp136
-rw-r--r--src/core/hle/kernel/thread.h22
17 files changed, 604 insertions, 804 deletions
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index db571b895..38705e3cd 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/common_types.h" 5#include "common/common_types.h"
@@ -20,16 +20,10 @@ public:
20 std::string GetTypeName() const override { return "Arbiter"; } 20 std::string GetTypeName() const override { return "Arbiter"; }
21 std::string GetName() const override { return name; } 21 std::string GetName() const override { return name; }
22 22
23 static Kernel::HandleType GetStaticHandleType() { return HandleType::AddressArbiter; } 23 static const HandleType HANDLE_TYPE = HandleType::AddressArbiter;
24 Kernel::HandleType GetHandleType() const override { return HandleType::AddressArbiter; } 24 HandleType GetHandleType() const override { return HANDLE_TYPE; }
25 25
26 std::string name; ///< Name of address arbiter object (optional) 26 std::string name; ///< Name of address arbiter object (optional)
27
28 ResultVal<bool> WaitSynchronization() override {
29 // TODO(bunnei): ImplementMe
30 ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
31 return UnimplementedFunction(ErrorModule::OS);
32 }
33}; 27};
34 28
35//////////////////////////////////////////////////////////////////////////////////////////////////// 29////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -53,13 +47,13 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3
53 // Wait current thread (acquire the arbiter)... 47 // Wait current thread (acquire the arbiter)...
54 case ArbitrationType::WaitIfLessThan: 48 case ArbitrationType::WaitIfLessThan:
55 if ((s32)Memory::Read32(address) <= value) { 49 if ((s32)Memory::Read32(address) <= value) {
56 Kernel::WaitCurrentThread(WAITTYPE_ARB, handle); 50 Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address);
57 HLE::Reschedule(__func__); 51 HLE::Reschedule(__func__);
58 } 52 }
59 break; 53 break;
60 54
61 default: 55 default:
62 ERROR_LOG(KERNEL, "unknown type=%d", type); 56 LOG_ERROR(Kernel, "unknown type=%d", type);
63 return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Usage); 57 return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Usage);
64 } 58 }
65 return RESULT_SUCCESS; 59 return RESULT_SUCCESS;
@@ -68,7 +62,8 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3
68/// Create an address arbiter 62/// Create an address arbiter
69AddressArbiter* CreateAddressArbiter(Handle& handle, const std::string& name) { 63AddressArbiter* CreateAddressArbiter(Handle& handle, const std::string& name) {
70 AddressArbiter* address_arbiter = new AddressArbiter; 64 AddressArbiter* address_arbiter = new AddressArbiter;
71 handle = Kernel::g_object_pool.Create(address_arbiter); 65 // TOOD(yuriks): Fix error reporting
66 handle = Kernel::g_handle_table.Create(address_arbiter).ValueOr(INVALID_HANDLE);
72 address_arbiter->name = name; 67 address_arbiter->name = name;
73 return address_arbiter; 68 return address_arbiter;
74} 69}
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index 8a5fb10b4..030e7ad7b 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
diff --git a/src/core/hle/kernel/archive.cpp b/src/core/hle/kernel/archive.cpp
deleted file mode 100644
index e273444c9..000000000
--- a/src/core/hle/kernel/archive.cpp
+++ /dev/null
@@ -1,429 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/common_types.h"
6#include "common/file_util.h"
7#include "common/math_util.h"
8
9#include "core/file_sys/archive.h"
10#include "core/file_sys/archive_sdmc.h"
11#include "core/file_sys/directory.h"
12#include "core/hle/kernel/archive.h"
13#include "core/hle/result.h"
14#include "core/hle/service/service.h"
15
16////////////////////////////////////////////////////////////////////////////////////////////////////
17// Kernel namespace
18
19namespace Kernel {
20
21// Command to access archive file
22enum class FileCommand : u32 {
23 Dummy1 = 0x000100C6,
24 Control = 0x040100C4,
25 OpenSubFile = 0x08010100,
26 Read = 0x080200C2,
27 Write = 0x08030102,
28 GetSize = 0x08040000,
29 SetSize = 0x08050080,
30 GetAttributes = 0x08060000,
31 SetAttributes = 0x08070040,
32 Close = 0x08080000,
33 Flush = 0x08090000,
34};
35
36// Command to access directory
37enum class DirectoryCommand : u32 {
38 Dummy1 = 0x000100C6,
39 Control = 0x040100C4,
40 Read = 0x08010042,
41 Close = 0x08020000,
42};
43
44class Archive : public Object {
45public:
46 std::string GetTypeName() const override { return "Archive"; }
47 std::string GetName() const override { return name; }
48
49 static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; }
50 Kernel::HandleType GetHandleType() const override { return HandleType::Archive; }
51
52 std::string name; ///< Name of archive (optional)
53 FileSys::Archive* backend; ///< Archive backend interface
54
55 ResultVal<bool> SyncRequest() override {
56 u32* cmd_buff = Service::GetCommandBuffer();
57 FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
58
59 switch (cmd) {
60 // Read from archive...
61 case FileCommand::Read:
62 {
63 u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
64 u32 length = cmd_buff[3];
65 u32 address = cmd_buff[5];
66
67 // Number of bytes read
68 cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
69 break;
70 }
71 // Write to archive...
72 case FileCommand::Write:
73 {
74 u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
75 u32 length = cmd_buff[3];
76 u32 flush = cmd_buff[4];
77 u32 address = cmd_buff[6];
78
79 // Number of bytes written
80 cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
81 break;
82 }
83 case FileCommand::GetSize:
84 {
85 u64 filesize = (u64) backend->GetSize();
86 cmd_buff[2] = (u32) filesize; // Lower word
87 cmd_buff[3] = (u32) (filesize >> 32); // Upper word
88 break;
89 }
90 case FileCommand::SetSize:
91 {
92 backend->SetSize(cmd_buff[1] | ((u64)cmd_buff[2] << 32));
93 break;
94 }
95 case FileCommand::Close:
96 {
97 DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
98 CloseArchive(backend->GetIdCode());
99 break;
100 }
101 // Unknown command...
102 default:
103 {
104 ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
105 return UnimplementedFunction(ErrorModule::FS);
106 }
107 }
108 cmd_buff[1] = 0; // No error
109 return MakeResult<bool>(false);
110 }
111
112 ResultVal<bool> WaitSynchronization() override {
113 // TODO(bunnei): ImplementMe
114 ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
115 return UnimplementedFunction(ErrorModule::FS);
116 }
117};
118
119class File : public Object {
120public:
121 std::string GetTypeName() const override { return "File"; }
122 std::string GetName() const override { return path.DebugStr(); }
123
124 static Kernel::HandleType GetStaticHandleType() { return HandleType::File; }
125 Kernel::HandleType GetHandleType() const override { return HandleType::File; }
126
127 FileSys::Path path; ///< Path of the file
128 std::unique_ptr<FileSys::File> backend; ///< File backend interface
129
130 ResultVal<bool> SyncRequest() override {
131 u32* cmd_buff = Service::GetCommandBuffer();
132 FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
133 switch (cmd) {
134
135 // Read from file...
136 case FileCommand::Read:
137 {
138 u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
139 u32 length = cmd_buff[3];
140 u32 address = cmd_buff[5];
141 DEBUG_LOG(KERNEL, "Read %s %s: offset=0x%llx length=%d address=0x%x",
142 GetTypeName().c_str(), GetName().c_str(), offset, length, address);
143 cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
144 break;
145 }
146
147 // Write to file...
148 case FileCommand::Write:
149 {
150 u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
151 u32 length = cmd_buff[3];
152 u32 flush = cmd_buff[4];
153 u32 address = cmd_buff[6];
154 DEBUG_LOG(KERNEL, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
155 GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
156 cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
157 break;
158 }
159
160 case FileCommand::GetSize:
161 {
162 DEBUG_LOG(KERNEL, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str());
163 u64 size = backend->GetSize();
164 cmd_buff[2] = (u32)size;
165 cmd_buff[3] = size >> 32;
166 break;
167 }
168
169 case FileCommand::SetSize:
170 {
171 u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
172 DEBUG_LOG(KERNEL, "SetSize %s %s size=%llu",
173 GetTypeName().c_str(), GetName().c_str(), size);
174 backend->SetSize(size);
175 break;
176 }
177
178 case FileCommand::Close:
179 {
180 DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
181 Kernel::g_object_pool.Destroy<File>(GetHandle());
182 break;
183 }
184
185 // Unknown command...
186 default:
187 ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
188 ResultCode error = UnimplementedFunction(ErrorModule::FS);
189 cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
190 return error;
191 }
192 cmd_buff[1] = 0; // No error
193 return MakeResult<bool>(false);
194 }
195
196 ResultVal<bool> WaitSynchronization() override {
197 // TODO(bunnei): ImplementMe
198 ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
199 return UnimplementedFunction(ErrorModule::FS);
200 }
201};
202
203class Directory : public Object {
204public:
205 std::string GetTypeName() const override { return "Directory"; }
206 std::string GetName() const override { return path.DebugStr(); }
207
208 static Kernel::HandleType GetStaticHandleType() { return HandleType::Directory; }
209 Kernel::HandleType GetHandleType() const override { return HandleType::Directory; }
210
211 FileSys::Path path; ///< Path of the directory
212 std::unique_ptr<FileSys::Directory> backend; ///< File backend interface
213
214 ResultVal<bool> SyncRequest() override {
215 u32* cmd_buff = Service::GetCommandBuffer();
216 DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]);
217 switch (cmd) {
218
219 // Read from directory...
220 case DirectoryCommand::Read:
221 {
222 u32 count = cmd_buff[1];
223 u32 address = cmd_buff[3];
224 auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
225 DEBUG_LOG(KERNEL, "Read %s %s: count=%d",
226 GetTypeName().c_str(), GetName().c_str(), count);
227
228 // Number of entries actually read
229 cmd_buff[2] = backend->Read(count, entries);
230 break;
231 }
232
233 case DirectoryCommand::Close:
234 {
235 DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
236 Kernel::g_object_pool.Destroy<Directory>(GetHandle());
237 break;
238 }
239
240 // Unknown command...
241 default:
242 ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
243 ResultCode error = UnimplementedFunction(ErrorModule::FS);
244 cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
245 return error;
246 }
247 cmd_buff[1] = 0; // No error
248 return MakeResult<bool>(false);
249 }
250
251 ResultVal<bool> WaitSynchronization() override {
252 // TODO(bunnei): ImplementMe
253 ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
254 return UnimplementedFunction(ErrorModule::FS);
255 }
256};
257
258////////////////////////////////////////////////////////////////////////////////////////////////////
259
260std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode
261
262ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code) {
263 auto itr = g_archive_map.find(id_code);
264 if (itr == g_archive_map.end()) {
265 return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
266 ErrorSummary::NotFound, ErrorLevel::Permanent);
267 }
268
269 return MakeResult<Handle>(itr->second);
270}
271
272ResultCode CloseArchive(FileSys::Archive::IdCode id_code) {
273 auto itr = g_archive_map.find(id_code);
274 if (itr == g_archive_map.end()) {
275 ERROR_LOG(KERNEL, "Cannot close archive %d, does not exist!", (int)id_code);
276 return InvalidHandle(ErrorModule::FS);
277 }
278
279 INFO_LOG(KERNEL, "Closed archive %d", (int) id_code);
280 return RESULT_SUCCESS;
281}
282
283/**
284 * Mounts an archive
285 * @param archive Pointer to the archive to mount
286 */
287ResultCode MountArchive(Archive* archive) {
288 FileSys::Archive::IdCode id_code = archive->backend->GetIdCode();
289 ResultVal<Handle> archive_handle = OpenArchive(id_code);
290 if (archive_handle.Succeeded()) {
291 ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code);
292 return archive_handle.Code();
293 }
294 g_archive_map[id_code] = archive->GetHandle();
295 INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName().c_str());
296 return RESULT_SUCCESS;
297}
298
299ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name) {
300 Archive* archive = new Archive;
301 Handle handle = Kernel::g_object_pool.Create(archive);
302 archive->name = name;
303 archive->backend = backend;
304
305 ResultCode result = MountArchive(archive);
306 if (result.IsError()) {
307 return result;
308 }
309
310 return RESULT_SUCCESS;
311}
312
313ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) {
314 // TODO(bunnei): Binary type files get a raw file pointer to the archive. Currently, we create
315 // the archive file handles at app loading, and then keep them persistent throughout execution.
316 // Archives file handles are just reused and not actually freed until emulation shut down.
317 // Verify if real hardware works this way, or if new handles are created each time
318 if (path.GetType() == FileSys::Binary)
319 // TODO(bunnei): FixMe - this is a hack to compensate for an incorrect FileSys backend
320 // design. While the functionally of this is OK, our implementation decision to separate
321 // normal files from archive file pointers is very likely wrong.
322 // See https://github.com/citra-emu/citra/issues/205
323 return MakeResult<Handle>(archive_handle);
324
325 File* file = new File;
326 Handle handle = Kernel::g_object_pool.Create(file);
327
328 Archive* archive = Kernel::g_object_pool.Get<Archive>(archive_handle);
329 if (archive == nullptr) {
330 return InvalidHandle(ErrorModule::FS);
331 }
332 file->path = path;
333 file->backend = archive->backend->OpenFile(path, mode);
334
335 if (!file->backend) {
336 return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
337 ErrorSummary::NotFound, ErrorLevel::Permanent);
338 }
339
340 return MakeResult<Handle>(handle);
341}
342
343/**
344 * Delete a File from an Archive
345 * @param archive_handle Handle to an open Archive object
346 * @param path Path to the File inside of the Archive
347 * @return Whether deletion succeeded
348 */
349Result DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path) {
350 Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
351 if (archive == nullptr)
352 return -1;
353 if (archive->backend->DeleteFile(path))
354 return 0;
355 return -1;
356}
357
358/**
359 * Delete a Directory from an Archive
360 * @param archive_handle Handle to an open Archive object
361 * @param path Path to the Directory inside of the Archive
362 * @return Whether deletion succeeded
363 */
364Result DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) {
365 Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
366 if (archive == nullptr)
367 return -1;
368 if (archive->backend->DeleteDirectory(path))
369 return 0;
370 return -1;
371}
372
373/**
374 * Create a Directory from an Archive
375 * @param archive_handle Handle to an open Archive object
376 * @param path Path to the Directory inside of the Archive
377 * @return Whether creation succeeded
378 */
379Result CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) {
380 Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
381 if (archive == nullptr)
382 return -1;
383 if (archive->backend->CreateDirectory(path))
384 return 0;
385 return -1;
386}
387
388/**
389 * Open a Directory from an Archive
390 * @param archive_handle Handle to an open Archive object
391 * @param path Path to the Directory inside of the Archive
392 * @return Opened Directory object
393 */
394ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) {
395 Directory* directory = new Directory;
396 Handle handle = Kernel::g_object_pool.Create(directory);
397
398 Archive* archive = Kernel::g_object_pool.Get<Archive>(archive_handle);
399 if (archive == nullptr) {
400 return InvalidHandle(ErrorModule::FS);
401 }
402 directory->path = path;
403 directory->backend = archive->backend->OpenDirectory(path);
404
405 return MakeResult<Handle>(handle);
406}
407
408/// Initialize archives
409void ArchiveInit() {
410 g_archive_map.clear();
411
412 // TODO(Link Mauve): Add the other archive types (see here for the known types:
413 // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished
414 // archive type is SDMC, so it is the only one getting exposed.
415
416 std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
417 auto archive = new FileSys::Archive_SDMC(sdmc_directory);
418 if (archive->Initialize())
419 CreateArchive(archive, "SDMC");
420 else
421 ERROR_LOG(KERNEL, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
422}
423
424/// Shutdown archives
425void ArchiveShutdown() {
426 g_archive_map.clear();
427}
428
429} // namespace Kernel
diff --git a/src/core/hle/kernel/archive.h b/src/core/hle/kernel/archive.h
deleted file mode 100644
index 6fc4f0f25..000000000
--- a/src/core/hle/kernel/archive.h
+++ /dev/null
@@ -1,85 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "core/file_sys/archive.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/result.h"
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// Kernel namespace
15
16namespace Kernel {
17
18/**
19 * Opens an archive
20 * @param id_code IdCode of the archive to open
21 * @return Handle to the opened archive
22 */
23ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code);
24
25/**
26 * Closes an archive
27 * @param id_code IdCode of the archive to open
28 */
29ResultCode CloseArchive(FileSys::Archive::IdCode id_code);
30
31/**
32 * Creates an Archive
33 * @param backend File system backend interface to the archive
34 * @param name Name of Archive
35 */
36ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name);
37
38/**
39 * Open a File from an Archive
40 * @param archive_handle Handle to an open Archive object
41 * @param path Path to the File inside of the Archive
42 * @param mode Mode under which to open the File
43 * @return Handle to the opened File object
44 */
45ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode);
46
47/**
48 * Delete a File from an Archive
49 * @param archive_handle Handle to an open Archive object
50 * @param path Path to the File inside of the Archive
51 * @return Whether deletion succeeded
52 */
53Result DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path);
54
55/**
56 * Delete a Directory from an Archive
57 * @param archive_handle Handle to an open Archive object
58 * @param path Path to the Directory inside of the Archive
59 * @return Whether deletion succeeded
60 */
61Result DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path);
62
63/**
64 * Create a Directory from an Archive
65 * @param archive_handle Handle to an open Archive object
66 * @param path Path to the Directory inside of the Archive
67 * @return Whether creation of directory succeeded
68 */
69Result CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path);
70
71/**
72 * Open a Directory from an Archive
73 * @param archive_handle Handle to an open Archive object
74 * @param path Path to the Directory inside of the Archive
75 * @return Handle to the opened File object
76 */
77ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path);
78
79/// Initialize archives
80void ArchiveInit();
81
82/// Shutdown archives
83void ArchiveShutdown();
84
85} // namespace FileSys
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
index 288080209..e43c3ee4e 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/event.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <map> 5#include <map>
@@ -19,8 +19,8 @@ public:
19 std::string GetTypeName() const override { return "Event"; } 19 std::string GetTypeName() const override { return "Event"; }
20 std::string GetName() const override { return name; } 20 std::string GetName() const override { return name; }
21 21
22 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Event; } 22 static const HandleType HANDLE_TYPE = HandleType::Event;
23 Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Event; } 23 HandleType GetHandleType() const override { return HANDLE_TYPE; }
24 24
25 ResetType intitial_reset_type; ///< ResetType specified at Event initialization 25 ResetType intitial_reset_type; ///< ResetType specified at Event initialization
26 ResetType reset_type; ///< Current ResetType 26 ResetType reset_type; ///< Current ResetType
@@ -53,7 +53,7 @@ public:
53 * @return Result of operation, 0 on success, otherwise error code 53 * @return Result of operation, 0 on success, otherwise error code
54 */ 54 */
55ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { 55ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) {
56 Event* evt = g_object_pool.Get<Event>(handle); 56 Event* evt = g_handle_table.Get<Event>(handle);
57 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); 57 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
58 58
59 evt->permanent_locked = permanent_locked; 59 evt->permanent_locked = permanent_locked;
@@ -67,7 +67,7 @@ ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) {
67 * @return Result of operation, 0 on success, otherwise error code 67 * @return Result of operation, 0 on success, otherwise error code
68 */ 68 */
69ResultCode SetEventLocked(const Handle handle, const bool locked) { 69ResultCode SetEventLocked(const Handle handle, const bool locked) {
70 Event* evt = g_object_pool.Get<Event>(handle); 70 Event* evt = g_handle_table.Get<Event>(handle);
71 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); 71 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
72 72
73 if (!evt->permanent_locked) { 73 if (!evt->permanent_locked) {
@@ -82,7 +82,7 @@ ResultCode SetEventLocked(const Handle handle, const bool locked) {
82 * @return Result of operation, 0 on success, otherwise error code 82 * @return Result of operation, 0 on success, otherwise error code
83 */ 83 */
84ResultCode SignalEvent(const Handle handle) { 84ResultCode SignalEvent(const Handle handle) {
85 Event* evt = g_object_pool.Get<Event>(handle); 85 Event* evt = g_handle_table.Get<Event>(handle);
86 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); 86 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
87 87
88 // Resume threads waiting for event to signal 88 // Resume threads waiting for event to signal
@@ -110,7 +110,7 @@ ResultCode SignalEvent(const Handle handle) {
110 * @return Result of operation, 0 on success, otherwise error code 110 * @return Result of operation, 0 on success, otherwise error code
111 */ 111 */
112ResultCode ClearEvent(Handle handle) { 112ResultCode ClearEvent(Handle handle) {
113 Event* evt = g_object_pool.Get<Event>(handle); 113 Event* evt = g_handle_table.Get<Event>(handle);
114 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); 114 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
115 115
116 if (!evt->permanent_locked) { 116 if (!evt->permanent_locked) {
@@ -129,7 +129,8 @@ ResultCode ClearEvent(Handle handle) {
129Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string& name) { 129Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string& name) {
130 Event* evt = new Event; 130 Event* evt = new Event;
131 131
132 handle = Kernel::g_object_pool.Create(evt); 132 // TOOD(yuriks): Fix error reporting
133 handle = Kernel::g_handle_table.Create(evt).ValueOr(INVALID_HANDLE);
133 134
134 evt->locked = true; 135 evt->locked = true;
135 evt->permanent_locked = false; 136 evt->permanent_locked = false;
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
index 73aec4e79..da793df1a 100644
--- a/src/core/hle/kernel/event.h
+++ b/src/core/hle/kernel/event.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 018000abd..e59ed1b57 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -1,106 +1,117 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6
5#include "common/common.h" 7#include "common/common.h"
6 8
7#include "core/core.h" 9#include "core/core.h"
8#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
9#include "core/hle/kernel/thread.h" 11#include "core/hle/kernel/thread.h"
10#include "core/hle/kernel/archive.h"
11 12
12namespace Kernel { 13namespace Kernel {
13 14
14Handle g_main_thread = 0; 15Handle g_main_thread = 0;
15ObjectPool g_object_pool; 16HandleTable g_handle_table;
17u64 g_program_id = 0;
16 18
17ObjectPool::ObjectPool() { 19HandleTable::HandleTable() {
18 next_id = INITIAL_NEXT_ID; 20 next_generation = 1;
21 Clear();
19} 22}
20 23
21Handle ObjectPool::Create(Object* obj, int range_bottom, int range_top) { 24ResultVal<Handle> HandleTable::Create(Object* obj) {
22 if (range_top > MAX_COUNT) { 25 _dbg_assert_(Kernel, obj != nullptr);
23 range_top = MAX_COUNT; 26
24 } 27 u16 slot = next_free_slot;
25 if (next_id >= range_bottom && next_id < range_top) { 28 if (slot >= generations.size()) {
26 range_bottom = next_id++; 29 LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use.");
27 } 30 return ERR_OUT_OF_HANDLES;
28 for (int i = range_bottom; i < range_top; i++) {
29 if (!occupied[i]) {
30 occupied[i] = true;
31 pool[i] = obj;
32 pool[i]->handle = i + HANDLE_OFFSET;
33 return i + HANDLE_OFFSET;
34 }
35 } 31 }
36 ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use."); 32 next_free_slot = generations[slot];
37 return 0; 33
38} 34 u16 generation = next_generation++;
39 35
40bool ObjectPool::IsValid(Handle handle) { 36 // Overflow count so it fits in the 15 bits dedicated to the generation in the handle.
41 int index = handle - HANDLE_OFFSET; 37 // CTR-OS doesn't use generation 0, so skip straight to 1.
42 if (index < 0) 38 if (next_generation >= (1 << 15)) next_generation = 1;
43 return false;
44 if (index >= MAX_COUNT)
45 return false;
46 39
47 return occupied[index]; 40 generations[slot] = generation;
41 intrusive_ptr_add_ref(obj);
42 objects[slot] = obj;
43
44 Handle handle = generation | (slot << 15);
45 obj->handle = handle;
46 return MakeResult<Handle>(handle);
48} 47}
49 48
50void ObjectPool::Clear() { 49ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
51 for (int i = 0; i < MAX_COUNT; i++) { 50 Object* object = GetGeneric(handle);
52 //brutally clear everything, no validation 51 if (object == nullptr) {
53 if (occupied[i]) 52 LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle);
54 delete pool[i]; 53 return ERR_INVALID_HANDLE;
55 occupied[i] = false;
56 } 54 }
57 pool.fill(nullptr); 55 return Create(object);
58 next_id = INITIAL_NEXT_ID; 56}
57
58ResultCode HandleTable::Close(Handle handle) {
59 if (!IsValid(handle))
60 return ERR_INVALID_HANDLE;
61
62 size_t slot = GetSlot(handle);
63 u16 generation = GetGeneration(handle);
64
65 intrusive_ptr_release(objects[slot]);
66 objects[slot] = nullptr;
67
68 generations[generation] = next_free_slot;
69 next_free_slot = slot;
70 return RESULT_SUCCESS;
59} 71}
60 72
61Object* &ObjectPool::operator [](Handle handle) 73bool HandleTable::IsValid(Handle handle) const {
62{ 74 size_t slot = GetSlot(handle);
63 _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ"); 75 u16 generation = GetGeneration(handle);
64 return pool[handle - HANDLE_OFFSET]; 76
77 return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation;
65} 78}
66 79
67void ObjectPool::List() { 80Object* HandleTable::GetGeneric(Handle handle) const {
68 for (int i = 0; i < MAX_COUNT; i++) { 81 if (handle == CurrentThread) {
69 if (occupied[i]) { 82 // TODO(yuriks) Directly return the pointer once this is possible.
70 if (pool[i]) { 83 handle = GetCurrentThreadHandle();
71 INFO_LOG(KERNEL, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(), 84 } else if (handle == CurrentProcess) {
72 pool[i]->GetName().c_str()); 85 LOG_ERROR(Kernel, "Current process (%08X) pseudo-handle not supported", CurrentProcess);
73 } 86 return nullptr;
74 }
75 } 87 }
76}
77 88
78int ObjectPool::GetCount() { 89 if (!IsValid(handle)) {
79 int count = 0; 90 return nullptr;
80 for (int i = 0; i < MAX_COUNT; i++) {
81 if (occupied[i])
82 count++;
83 } 91 }
84 return count; 92 return objects[GetSlot(handle)];
85} 93}
86 94
87Object* ObjectPool::CreateByIDType(int type) { 95void HandleTable::Clear() {
88 ERROR_LOG(COMMON, "Unimplemented: %d.", type); 96 for (size_t i = 0; i < MAX_COUNT; ++i) {
89 return nullptr; 97 generations[i] = i + 1;
98 if (objects[i] != nullptr)
99 intrusive_ptr_release(objects[i]);
100 objects[i] = nullptr;
101 }
102 next_free_slot = 0;
90} 103}
91 104
92/// Initialize the kernel 105/// Initialize the kernel
93void Init() { 106void Init() {
94 Kernel::ThreadingInit(); 107 Kernel::ThreadingInit();
95 Kernel::ArchiveInit();
96} 108}
97 109
98/// Shutdown the kernel 110/// Shutdown the kernel
99void Shutdown() { 111void Shutdown() {
100 Kernel::ThreadingShutdown(); 112 Kernel::ThreadingShutdown();
101 Kernel::ArchiveShutdown();
102 113
103 g_object_pool.Clear(); // Free all kernel objects 114 g_handle_table.Clear(); // Free all kernel objects
104} 115}
105 116
106/** 117/**
@@ -109,8 +120,6 @@ void Shutdown() {
109 * @return True on success, otherwise false 120 * @return True on success, otherwise false
110 */ 121 */
111bool LoadExec(u32 entry_point) { 122bool LoadExec(u32 entry_point) {
112 Init();
113
114 Core::g_app_core->SetPC(entry_point); 123 Core::g_app_core->SetPC(entry_point);
115 124
116 // 0x30 is the typical main thread priority I've seen used so far 125 // 0x30 is the typical main thread priority I've seen used so far
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 8d3937ce8..7f86fd07d 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project 1// Copyright 2014 Citra Emulator Project / PPSSPP Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -12,9 +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
17enum KernelHandle { 19// TODO: Verify code
20const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel,
21 ErrorSummary::OutOfResource, ErrorLevel::Temporary);
22// TOOD: Verify code
23const ResultCode ERR_INVALID_HANDLE = InvalidHandle(ErrorModule::Kernel);
24
25enum KernelHandle : Handle {
18 CurrentThread = 0xFFFF8000, 26 CurrentThread = 0xFFFF8000,
19 CurrentProcess = 0xFFFF8001, 27 CurrentProcess = 0xFFFF8001,
20}; 28};
@@ -22,7 +30,7 @@ enum KernelHandle {
22enum class HandleType : u32 { 30enum class HandleType : u32 {
23 Unknown = 0, 31 Unknown = 0,
24 Port = 1, 32 Port = 1,
25 Service = 2, 33 Session = 2,
26 Event = 3, 34 Event = 3,
27 Mutex = 4, 35 Mutex = 4,
28 SharedMemory = 5, 36 SharedMemory = 5,
@@ -30,20 +38,17 @@ enum class HandleType : u32 {
30 Thread = 7, 38 Thread = 7,
31 Process = 8, 39 Process = 8,
32 AddressArbiter = 9, 40 AddressArbiter = 9,
33 File = 10, 41 Semaphore = 10,
34 Semaphore = 11,
35 Archive = 12,
36 Directory = 13,
37}; 42};
38 43
39enum { 44enum {
40 DEFAULT_STACK_SIZE = 0x4000, 45 DEFAULT_STACK_SIZE = 0x4000,
41}; 46};
42 47
43class ObjectPool; 48class HandleTable;
44 49
45class Object : NonCopyable { 50class Object : NonCopyable {
46 friend class ObjectPool; 51 friend class HandleTable;
47 u32 handle; 52 u32 handle;
48public: 53public:
49 virtual ~Object() {} 54 virtual ~Object() {}
@@ -53,113 +58,145 @@ public:
53 virtual Kernel::HandleType GetHandleType() const = 0; 58 virtual Kernel::HandleType GetHandleType() const = 0;
54 59
55 /** 60 /**
56 * Synchronize kernel object. 61 * Wait for kernel object to synchronize.
57 * @return True if the current thread should wait as a result of the sync 62 * @return True if the current thread should wait as a result of the wait
58 */ 63 */
59 virtual ResultVal<bool> SyncRequest() { 64 virtual ResultVal<bool> WaitSynchronization() {
60 ERROR_LOG(KERNEL, "(UNIMPLEMENTED)"); 65 LOG_ERROR(Kernel, "(UNIMPLEMENTED)");
61 return UnimplementedFunction(ErrorModule::Kernel); 66 return UnimplementedFunction(ErrorModule::Kernel);
62 } 67 }
63 68
64 /** 69private:
65 * Wait for kernel object to synchronize. 70 friend void intrusive_ptr_add_ref(Object*);
66 * @return True if the current thread should wait as a result of the wait 71 friend void intrusive_ptr_release(Object*);
67 */ 72
68 virtual ResultVal<bool> WaitSynchronization() = 0; 73 unsigned int ref_count = 0;
69}; 74};
70 75
71class ObjectPool : NonCopyable { 76// Special functions that will later be used by boost::instrusive_ptr to do automatic ref-counting
72public: 77inline void intrusive_ptr_add_ref(Object* object) {
73 ObjectPool(); 78 ++object->ref_count;
74 ~ObjectPool() {} 79}
75 80
76 // Allocates a handle within the range and inserts the object into the map. 81inline void intrusive_ptr_release(Object* object) {
77 Handle Create(Object* obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF); 82 if (--object->ref_count == 0) {
83 delete object;
84 }
85}
78 86
79 static Object* CreateByIDType(int type); 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();
80 113
81 template <class T> 114 /**
82 void Destroy(Handle handle) { 115 * Allocates a handle for the given object.
83 if (Get<T>(handle)) { 116 * @return The created Handle or one of the following errors:
84 occupied[handle - HANDLE_OFFSET] = false; 117 * - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded.
85 delete pool[handle - HANDLE_OFFSET]; 118 */
86 } 119 ResultVal<Handle> Create(Object* obj);
87 }
88 120
89 bool IsValid(Handle handle); 121 /**
122 * Returns a new handle that points to the same object as the passed in handle.
123 * @return The duplicated Handle or one of the following errors:
124 * - `ERR_INVALID_HANDLE`: an invalid handle was passed in.
125 * - Any errors returned by `Create()`.
126 */
127 ResultVal<Handle> Duplicate(Handle handle);
90 128
91 template <class T> 129 /**
92 T* Get(Handle handle) { 130 * Closes a handle, removing it from the table and decreasing the object's ref-count.
93 if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) { 131 * @return `RESULT_SUCCESS` or one of the following errors:
94 if (handle != 0) { 132 * - `ERR_INVALID_HANDLE`: an invalid handle was passed in.
95 WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); 133 */
96 } 134 ResultCode Close(Handle handle);
97 return nullptr;
98 } else {
99 Object* t = pool[handle - HANDLE_OFFSET];
100 if (t->GetHandleType() != T::GetStaticHandleType()) {
101 WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle);
102 return nullptr;
103 }
104 return static_cast<T*>(t);
105 }
106 }
107 135
108 // ONLY use this when you know the handle is valid. 136 /// Checks if a handle is valid and points to an existing object.
109 template <class T> 137 bool IsValid(Handle handle) const;
110 T *GetFast(Handle handle) {
111 const Handle realHandle = handle - HANDLE_OFFSET;
112 _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]);
113 return static_cast<T*>(pool[realHandle]);
114 }
115 138
116 template <class T, typename ArgT> 139 /**
117 void Iterate(bool func(T*, ArgT), ArgT arg) { 140 * Looks up a handle.
118 int type = T::GetStaticIDType(); 141 * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid.
119 for (int i = 0; i < MAX_COUNT; i++) 142 */
120 { 143 Object* GetGeneric(Handle handle) const;
121 if (!occupied[i])
122 continue;
123 T* t = static_cast<T*>(pool[i]);
124 if (t->GetIDType() == type) {
125 if (!func(t, arg))
126 break;
127 }
128 }
129 }
130 144
131 bool GetIDType(Handle handle, HandleType* type) const { 145 /**
132 if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) || 146 * Looks up a handle while verifying its type.
133 !occupied[handle - HANDLE_OFFSET]) { 147 * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid or its
134 ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); 148 * type differs from the handle type `T::HANDLE_TYPE`.
135 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);
136 } 155 }
137 Object* t = pool[handle - HANDLE_OFFSET]; 156 return nullptr;
138 *type = t->GetHandleType();
139 return true;
140 } 157 }
141 158
142 Object* &operator [](Handle handle); 159 /// Closes all handles held in this table.
143 void List();
144 void Clear(); 160 void Clear();
145 int GetCount();
146 161
147private: 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;
148 168
149 enum { 169 static size_t GetSlot(Handle handle) { return handle >> 15; }
150 MAX_COUNT = 0x1000, 170 static u16 GetGeneration(Handle handle) { return handle & 0x7FFF; }
151 HANDLE_OFFSET = 0x100,
152 INITIAL_NEXT_ID = 0x10,
153 };
154 171
155 std::array<Object*, MAX_COUNT> pool; 172 /// Stores the Object referenced by the handle or null if the slot is empty.
156 std::array<bool, MAX_COUNT> occupied; 173 std::array<Object*, MAX_COUNT> objects;
157 int next_id; 174
175 /**
176 * The value of `next_generation` when the handle was created, used to check for validity. For
177 * empty slots, contains the index of the next free slot in the list.
178 */
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;
186
187 /// Head of the free slots linked list.
188 u16 next_free_slot;
158}; 189};
159 190
160extern ObjectPool g_object_pool; 191extern HandleTable g_handle_table;
161extern Handle g_main_thread; 192extern Handle g_main_thread;
162 193
194/// The ID code of the currently running game
195/// TODO(Subv): This variable should not be here,
196/// we need a way to store information about the currently loaded application
197/// for later query during runtime, maybe using the LDR service?
198extern u64 g_program_id;
199
163/// Initialize the kernel 200/// Initialize the kernel
164void Init(); 201void Init();
165 202
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index b303ba128..558068c79 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <map> 5#include <map>
@@ -18,8 +18,8 @@ public:
18 std::string GetTypeName() const override { return "Mutex"; } 18 std::string GetTypeName() const override { return "Mutex"; }
19 std::string GetName() const override { return name; } 19 std::string GetName() const override { return name; }
20 20
21 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; } 21 static const HandleType HANDLE_TYPE = HandleType::Mutex;
22 Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Mutex; } 22 HandleType GetHandleType() const override { return HANDLE_TYPE; }
23 23
24 bool initial_locked; ///< Initial lock state when mutex was created 24 bool initial_locked; ///< Initial lock state when mutex was created
25 bool locked; ///< Current locked state 25 bool locked; ///< Current locked state
@@ -27,21 +27,7 @@ public:
27 std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex 27 std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex
28 std::string name; ///< Name of mutex (optional) 28 std::string name; ///< Name of mutex (optional)
29 29
30 ResultVal<bool> SyncRequest() override { 30 ResultVal<bool> WaitSynchronization() override;
31 // TODO(bunnei): ImplementMe
32 locked = true;
33 return MakeResult<bool>(false);
34 }
35
36 ResultVal<bool> WaitSynchronization() override {
37 // TODO(bunnei): ImplementMe
38 bool wait = locked;
39 if (locked) {
40 Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
41 }
42
43 return MakeResult<bool>(wait);
44 }
45}; 31};
46 32
47//////////////////////////////////////////////////////////////////////////////////////////////////// 33////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -49,21 +35,46 @@ public:
49typedef std::multimap<Handle, Handle> MutexMap; 35typedef std::multimap<Handle, Handle> MutexMap;
50static MutexMap g_mutex_held_locks; 36static MutexMap g_mutex_held_locks;
51 37
52void MutexAcquireLock(Mutex* mutex, Handle thread) { 38/**
39 * Acquires the specified mutex for the specified thread
40 * @param mutex Mutex that is to be acquired
41 * @param thread Thread that will acquired
42 */
43void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) {
53 g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); 44 g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle()));
54 mutex->lock_thread = thread; 45 mutex->lock_thread = thread;
55} 46}
56 47
57void MutexAcquireLock(Mutex* mutex) { 48bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
58 Handle thread = GetCurrentThreadHandle();
59 MutexAcquireLock(mutex, thread); 49 MutexAcquireLock(mutex, thread);
50 Kernel::ResumeThreadFromWait(thread);
51 return true;
52}
53
54/**
55 * Resumes a thread waiting for the specified mutex
56 * @param mutex The mutex that some thread is waiting on
57 */
58void ResumeWaitingThread(Mutex* mutex) {
59 // Find the next waiting thread for the mutex...
60 if (mutex->waiting_threads.empty()) {
61 // Reset mutex lock thread handle, nothing is waiting
62 mutex->locked = false;
63 mutex->lock_thread = -1;
64 }
65 else {
66 // Resume the next waiting thread and re-lock the mutex
67 std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
68 ReleaseMutexForThread(mutex, *iter);
69 mutex->waiting_threads.erase(iter);
70 }
60} 71}
61 72
62void MutexEraseLock(Mutex* mutex) { 73void MutexEraseLock(Mutex* mutex) {
63 Handle handle = mutex->GetHandle(); 74 Handle handle = mutex->GetHandle();
64 auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); 75 auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread);
65 for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { 76 for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
66 if ((*iter).second == handle) { 77 if (iter->second == handle) {
67 g_mutex_held_locks.erase(iter); 78 g_mutex_held_locks.erase(iter);
68 break; 79 break;
69 } 80 }
@@ -71,6 +82,19 @@ void MutexEraseLock(Mutex* mutex) {
71 mutex->lock_thread = -1; 82 mutex->lock_thread = -1;
72} 83}
73 84
85void ReleaseThreadMutexes(Handle thread) {
86 auto locked = g_mutex_held_locks.equal_range(thread);
87
88 // Release every mutex that the thread holds, and resume execution on the waiting threads
89 for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
90 Mutex* mutex = g_handle_table.Get<Mutex>(iter->second);
91 ResumeWaitingThread(mutex);
92 }
93
94 // Erase all the locks that this thread holds
95 g_mutex_held_locks.erase(thread);
96}
97
74bool LockMutex(Mutex* mutex) { 98bool LockMutex(Mutex* mutex) {
75 // Mutex alread locked? 99 // Mutex alread locked?
76 if (mutex->locked) { 100 if (mutex->locked) {
@@ -80,28 +104,10 @@ bool LockMutex(Mutex* mutex) {
80 return true; 104 return true;
81} 105}
82 106
83bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
84 MutexAcquireLock(mutex, thread);
85 Kernel::ResumeThreadFromWait(thread);
86 return true;
87}
88
89bool ReleaseMutex(Mutex* mutex) { 107bool ReleaseMutex(Mutex* mutex) {
90 MutexEraseLock(mutex); 108 MutexEraseLock(mutex);
91 bool woke_threads = false; 109 ResumeWaitingThread(mutex);
92 110 return true;
93 // Find the next waiting thread for the mutex...
94 while (!woke_threads && !mutex->waiting_threads.empty()) {
95 std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
96 woke_threads |= ReleaseMutexForThread(mutex, *iter);
97 mutex->waiting_threads.erase(iter);
98 }
99 // Reset mutex lock thread handle, nothing is waiting
100 if (!woke_threads) {
101 mutex->locked = false;
102 mutex->lock_thread = -1;
103 }
104 return woke_threads;
105} 111}
106 112
107/** 113/**
@@ -109,7 +115,7 @@ bool ReleaseMutex(Mutex* mutex) {
109 * @param handle Handle to mutex to release 115 * @param handle Handle to mutex to release
110 */ 116 */
111ResultCode ReleaseMutex(Handle handle) { 117ResultCode ReleaseMutex(Handle handle) {
112 Mutex* mutex = Kernel::g_object_pool.Get<Mutex>(handle); 118 Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle);
113 if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel); 119 if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel);
114 120
115 if (!ReleaseMutex(mutex)) { 121 if (!ReleaseMutex(mutex)) {
@@ -130,7 +136,8 @@ ResultCode ReleaseMutex(Handle handle) {
130 */ 136 */
131Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) { 137Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) {
132 Mutex* mutex = new Mutex; 138 Mutex* mutex = new Mutex;
133 handle = Kernel::g_object_pool.Create(mutex); 139 // TODO(yuriks): Fix error reporting
140 handle = Kernel::g_handle_table.Create(mutex).ValueOr(INVALID_HANDLE);
134 141
135 mutex->locked = mutex->initial_locked = initial_locked; 142 mutex->locked = mutex->initial_locked = initial_locked;
136 mutex->name = name; 143 mutex->name = name;
@@ -158,4 +165,17 @@ Handle CreateMutex(bool initial_locked, const std::string& name) {
158 return handle; 165 return handle;
159} 166}
160 167
168ResultVal<bool> Mutex::WaitSynchronization() {
169 bool wait = locked;
170 if (locked) {
171 Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
172 }
173 else {
174 // Lock the mutex when the first thread accesses it
175 locked = true;
176 MutexAcquireLock(this);
177 }
178
179 return MakeResult<bool>(wait);
180}
161} // namespace 181} // namespace
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index 155449f95..a8ca97014 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -24,4 +24,10 @@ ResultCode ReleaseMutex(Handle handle);
24 */ 24 */
25Handle CreateMutex(bool initial_locked, const std::string& name="Unknown"); 25Handle CreateMutex(bool initial_locked, const std::string& name="Unknown");
26 26
27/**
28 * Releases all the mutexes held by the specified thread
29 * @param thread Thread that is holding the mutexes
30 */
31void ReleaseThreadMutexes(Handle thread);
32
27} // namespace 33} // namespace
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp
new file mode 100644
index 000000000..6bc8066a6
--- /dev/null
+++ b/src/core/hle/kernel/semaphore.cpp
@@ -0,0 +1,95 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <queue>
6
7#include "common/common.h"
8
9#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/semaphore.h"
11#include "core/hle/kernel/thread.h"
12
13namespace Kernel {
14
15class Semaphore : public Object {
16public:
17 std::string GetTypeName() const override { return "Semaphore"; }
18 std::string GetName() const override { return name; }
19
20 static const HandleType HANDLE_TYPE = HandleType::Semaphore;
21 HandleType GetHandleType() const override { return HANDLE_TYPE; }
22
23 s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have
24 s32 available_count; ///< Number of free slots left in the semaphore
25 std::queue<Handle> waiting_threads; ///< Threads that are waiting for the semaphore
26 std::string name; ///< Name of semaphore (optional)
27
28 /**
29 * Tests whether a semaphore still has free slots
30 * @return Whether the semaphore is available
31 */
32 bool IsAvailable() const {
33 return available_count > 0;
34 }
35
36 ResultVal<bool> WaitSynchronization() override {
37 bool wait = !IsAvailable();
38
39 if (wait) {
40 Kernel::WaitCurrentThread(WAITTYPE_SEMA, GetHandle());
41 waiting_threads.push(GetCurrentThreadHandle());
42 } else {
43 --available_count;
44 }
45
46 return MakeResult<bool>(wait);
47 }
48};
49
50////////////////////////////////////////////////////////////////////////////////////////////////////
51
52ResultCode CreateSemaphore(Handle* handle, s32 initial_count,
53 s32 max_count, const std::string& name) {
54
55 if (initial_count > max_count)
56 return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel,
57 ErrorSummary::WrongArgument, ErrorLevel::Permanent);
58
59 Semaphore* semaphore = new Semaphore;
60 // TOOD(yuriks): Fix error reporting
61 *handle = g_handle_table.Create(semaphore).ValueOr(INVALID_HANDLE);
62
63 // When the semaphore is created, some slots are reserved for other threads,
64 // and the rest is reserved for the caller thread
65 semaphore->max_count = max_count;
66 semaphore->available_count = initial_count;
67 semaphore->name = name;
68
69 return RESULT_SUCCESS;
70}
71
72ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) {
73 Semaphore* semaphore = g_handle_table.Get<Semaphore>(handle);
74 if (semaphore == nullptr)
75 return InvalidHandle(ErrorModule::Kernel);
76
77 if (semaphore->max_count - semaphore->available_count < release_count)
78 return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel,
79 ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
80
81 *count = semaphore->available_count;
82 semaphore->available_count += release_count;
83
84 // Notify some of the threads that the semaphore has been released
85 // stop once the semaphore is full again or there are no more waiting threads
86 while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) {
87 Kernel::ResumeThreadFromWait(semaphore->waiting_threads.front());
88 semaphore->waiting_threads.pop();
89 --semaphore->available_count;
90 }
91
92 return RESULT_SUCCESS;
93}
94
95} // namespace
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h
new file mode 100644
index 000000000..8644ecf0c
--- /dev/null
+++ b/src/core/hle/kernel/semaphore.h
@@ -0,0 +1,32 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "core/hle/kernel/kernel.h"
10
11namespace Kernel {
12
13/**
14 * Creates a semaphore.
15 * @param handle Pointer to the handle of the newly created object
16 * @param initial_count Number of slots reserved for other threads
17 * @param max_count Maximum number of slots the semaphore can have
18 * @param name Optional name of semaphore
19 * @return ResultCode of the error
20 */
21ResultCode CreateSemaphore(Handle* handle, s32 initial_count, s32 max_count, const std::string& name = "Unknown");
22
23/**
24 * Releases a certain number of slots from a semaphore.
25 * @param count The number of free slots the semaphore had before this call
26 * @param handle The handle of the semaphore to release
27 * @param release_count The number of slots to release
28 * @return ResultCode of the error
29 */
30ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count);
31
32} // namespace
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h
new file mode 100644
index 000000000..91f3ffc2c
--- /dev/null
+++ b/src/core/hle/kernel/session.h
@@ -0,0 +1,58 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/kernel/kernel.h"
8
9namespace Kernel {
10
11static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header
12
13/**
14 * Returns a pointer to the command buffer in kernel memory
15 * @param offset Optional offset into command buffer
16 * @return Pointer to command buffer
17 */
18inline static u32* GetCommandBuffer(const int offset=0) {
19 return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset);
20}
21
22/**
23 * Kernel object representing the client endpoint of an IPC session. Sessions are the basic CTR-OS
24 * primitive for communication between different processes, and are used to implement service calls
25 * to the various system services.
26 *
27 * To make a service call, the client must write the command header and parameters to the buffer
28 * located at offset 0x80 of the TLS (Thread-Local Storage) area, then execute a SendSyncRequest
29 * SVC call with its Session handle. The kernel will read the command header, using it to marshall
30 * the parameters to the process at the server endpoint of the session. After the server replies to
31 * the request, the response is marshalled back to the caller's TLS buffer and control is
32 * transferred back to it.
33 *
34 * In Citra, only the client endpoint is currently implemented and only HLE calls, where the IPC
35 * request is answered by C++ code in the emulator, are supported. When SendSyncRequest is called
36 * with the session handle, this class's SyncRequest method is called, which should read the TLS
37 * buffer and emulate the call accordingly. Since the code can directly read the emulated memory,
38 * no parameter marshalling is done.
39 *
40 * In the long term, this should be turned into the full-fledged IPC mechanism implemented by
41 * CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as
42 * opposed to HLE simulations.
43 */
44class Session : public Object {
45public:
46 std::string GetTypeName() const override { return "Session"; }
47
48 static const HandleType HANDLE_TYPE = HandleType::Session;
49 HandleType GetHandleType() const override { return HANDLE_TYPE; }
50
51 /**
52 * Handles a synchronous call to this session using HLE emulation. Emulated <-> emulated calls
53 * aren't supported yet.
54 */
55 virtual ResultVal<bool> SyncRequest() = 0;
56};
57
58}
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index cfcc0e0b7..cea1f6fa1 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/common.h" 5#include "common/common.h"
@@ -13,14 +13,8 @@ class SharedMemory : public Object {
13public: 13public:
14 std::string GetTypeName() const override { return "SharedMemory"; } 14 std::string GetTypeName() const override { return "SharedMemory"; }
15 15
16 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; } 16 static const HandleType HANDLE_TYPE = HandleType::SharedMemory;
17 Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::SharedMemory; } 17 HandleType GetHandleType() const override { return HANDLE_TYPE; }
18
19 ResultVal<bool> WaitSynchronization() override {
20 // TODO(bunnei): ImplementMe
21 ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
22 return UnimplementedFunction(ErrorModule::OS);
23 }
24 18
25 u32 base_address; ///< Address of shared memory block in RAM 19 u32 base_address; ///< Address of shared memory block in RAM
26 MemoryPermission permissions; ///< Permissions of shared memory block (SVC field) 20 MemoryPermission permissions; ///< Permissions of shared memory block (SVC field)
@@ -38,7 +32,8 @@ public:
38 */ 32 */
39SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) { 33SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) {
40 SharedMemory* shared_memory = new SharedMemory; 34 SharedMemory* shared_memory = new SharedMemory;
41 handle = Kernel::g_object_pool.Create(shared_memory); 35 // TOOD(yuriks): Fix error reporting
36 handle = Kernel::g_handle_table.Create(shared_memory).ValueOr(INVALID_HANDLE);
42 shared_memory->name = name; 37 shared_memory->name = name;
43 return shared_memory; 38 return shared_memory;
44} 39}
@@ -61,12 +56,12 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions
61 MemoryPermission other_permissions) { 56 MemoryPermission other_permissions) {
62 57
63 if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) { 58 if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) {
64 ERROR_LOG(KERNEL, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!", 59 LOG_ERROR(Kernel_SVC, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!",
65 handle, address); 60 handle, address);
66 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, 61 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
67 ErrorSummary::InvalidArgument, ErrorLevel::Permanent); 62 ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
68 } 63 }
69 SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle); 64 SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
70 if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); 65 if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
71 66
72 shared_memory->base_address = address; 67 shared_memory->base_address = address;
@@ -77,13 +72,13 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions
77} 72}
78 73
79ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) { 74ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) {
80 SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle); 75 SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
81 if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); 76 if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
82 77
83 if (0 != shared_memory->base_address) 78 if (0 != shared_memory->base_address)
84 return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset)); 79 return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset));
85 80
86 ERROR_LOG(KERNEL, "memory block handle=0x%08X not mapped!", handle); 81 LOG_ERROR(Kernel_SVC, "memory block handle=0x%08X not mapped!", handle);
87 // TODO(yuriks): Verify error code. 82 // TODO(yuriks): Verify error code.
88 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, 83 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
89 ErrorSummary::InvalidState, ErrorLevel::Permanent); 84 ErrorSummary::InvalidState, ErrorLevel::Permanent);
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 304cf5b67..bb65c7ccd 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -12,11 +12,15 @@ namespace Kernel {
12 12
13/// Permissions for mapped shared memory blocks 13/// Permissions for mapped shared memory blocks
14enum class MemoryPermission : u32 { 14enum class MemoryPermission : u32 {
15 None = 0, 15 None = 0,
16 Read = (1u << 0), 16 Read = (1u << 0),
17 Write = (1u << 1), 17 Write = (1u << 1),
18 ReadWrite = (Read | Write), 18 ReadWrite = (Read | Write),
19 DontCare = (1u << 28) 19 Execute = (1u << 2),
20 ReadExecute = (Read | Execute),
21 WriteExecute = (Write | Execute),
22 ReadWriteExecute = (Read | Write | Execute),
23 DontCare = (1u << 28)
20}; 24};
21 25
22/** 26/**
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index f3f54a4e9..872df2d14 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project 1// Copyright 2014 Citra Emulator Project / PPSSPP Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
@@ -14,6 +14,7 @@
14#include "core/hle/hle.h" 14#include "core/hle/hle.h"
15#include "core/hle/kernel/kernel.h" 15#include "core/hle/kernel/kernel.h"
16#include "core/hle/kernel/thread.h" 16#include "core/hle/kernel/thread.h"
17#include "core/hle/kernel/mutex.h"
17#include "core/hle/result.h" 18#include "core/hle/result.h"
18#include "core/mem_map.h" 19#include "core/mem_map.h"
19 20
@@ -25,8 +26,8 @@ public:
25 std::string GetName() const override { return name; } 26 std::string GetName() const override { return name; }
26 std::string GetTypeName() const override { return "Thread"; } 27 std::string GetTypeName() const override { return "Thread"; }
27 28
28 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; } 29 static const HandleType HANDLE_TYPE = HandleType::Thread;
29 Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Thread; } 30 HandleType GetHandleType() const override { return HANDLE_TYPE; }
30 31
31 inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } 32 inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; }
32 inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } 33 inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; }
@@ -49,6 +50,8 @@ public:
49 50
50 ThreadContext context; 51 ThreadContext context;
51 52
53 u32 thread_id;
54
52 u32 status; 55 u32 status;
53 u32 entry_point; 56 u32 entry_point;
54 u32 stack_top; 57 u32 stack_top;
@@ -61,6 +64,7 @@ public:
61 64
62 WaitType wait_type; 65 WaitType wait_type;
63 Handle wait_handle; 66 Handle wait_handle;
67 VAddr wait_address;
64 68
65 std::vector<Handle> waiting_threads; 69 std::vector<Handle> waiting_threads;
66 70
@@ -76,8 +80,10 @@ static Common::ThreadQueueList<Handle> thread_ready_queue;
76static Handle current_thread_handle; 80static Handle current_thread_handle;
77static Thread* current_thread; 81static Thread* current_thread;
78 82
79/// Gets the current thread 83static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup
80inline Thread* GetCurrentThread() { 84static u32 next_thread_id; ///< The next available thread id
85
86Thread* GetCurrentThread() {
81 return current_thread; 87 return current_thread;
82} 88}
83 89
@@ -121,6 +127,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
121 } 127 }
122 t->wait_type = WAITTYPE_NONE; 128 t->wait_type = WAITTYPE_NONE;
123 t->wait_handle = 0; 129 t->wait_handle = 0;
130 t->wait_address = 0;
124} 131}
125 132
126/// Change a thread to "ready" state 133/// Change a thread to "ready" state
@@ -140,30 +147,43 @@ void ChangeReadyState(Thread* t, bool ready) {
140 } 147 }
141} 148}
142 149
143/// Verify that a thread has not been released from waiting 150/// Check if a thread is blocking on a specified wait type
144inline bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) { 151static bool CheckWaitType(const Thread* thread, WaitType type) {
145 _dbg_assert_(KERNEL, thread != nullptr); 152 return (type == thread->wait_type) && (thread->IsWaiting());
146 return type == thread->wait_type && wait_handle == thread->wait_handle; 153}
154
155/// Check if a thread is blocking on a specified wait type with a specified handle
156static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle) {
157 return CheckWaitType(thread, type) && (wait_handle == thread->wait_handle);
158}
159
160/// Check if a thread is blocking on a specified wait type with a specified handle and address
161static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) {
162 return CheckWaitType(thread, type, wait_handle) && (wait_address == thread->wait_address);
147} 163}
148 164
149/// Stops the current thread 165/// Stops the current thread
150ResultCode StopThread(Handle handle, const char* reason) { 166ResultCode StopThread(Handle handle, const char* reason) {
151 Thread* thread = g_object_pool.Get<Thread>(handle); 167 Thread* thread = g_handle_table.Get<Thread>(handle);
152 if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); 168 if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
153 169
170 // Release all the mutexes that this thread holds
171 ReleaseThreadMutexes(handle);
172
154 ChangeReadyState(thread, false); 173 ChangeReadyState(thread, false);
155 thread->status = THREADSTATUS_DORMANT; 174 thread->status = THREADSTATUS_DORMANT;
156 for (Handle waiting_handle : thread->waiting_threads) { 175 for (Handle waiting_handle : thread->waiting_threads) {
157 Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle); 176 Thread* waiting_thread = g_handle_table.Get<Thread>(waiting_handle);
158 if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) { 177
178 if (CheckWaitType(waiting_thread, WAITTYPE_THREADEND, handle))
159 ResumeThreadFromWait(waiting_handle); 179 ResumeThreadFromWait(waiting_handle);
160 }
161 } 180 }
162 thread->waiting_threads.clear(); 181 thread->waiting_threads.clear();
163 182
164 // Stopped threads are never waiting. 183 // Stopped threads are never waiting.
165 thread->wait_type = WAITTYPE_NONE; 184 thread->wait_type = WAITTYPE_NONE;
166 thread->wait_handle = 0; 185 thread->wait_handle = 0;
186 thread->wait_address = 0;
167 187
168 return RESULT_SUCCESS; 188 return RESULT_SUCCESS;
169} 189}
@@ -178,7 +198,7 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
178 198
179 if (new_status == THREADSTATUS_WAIT) { 199 if (new_status == THREADSTATUS_WAIT) {
180 if (t->wait_type == WAITTYPE_NONE) { 200 if (t->wait_type == WAITTYPE_NONE) {
181 ERROR_LOG(KERNEL, "Waittype none not allowed"); 201 LOG_ERROR(Kernel, "Waittype none not allowed");
182 } 202 }
183 } 203 }
184} 204}
@@ -190,14 +210,14 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
190 210
191 // Iterate through threads, find highest priority thread that is waiting to be arbitrated... 211 // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
192 for (Handle handle : thread_queue) { 212 for (Handle handle : thread_queue) {
193 Thread* thread = g_object_pool.Get<Thread>(handle); 213 Thread* thread = g_handle_table.Get<Thread>(handle);
194 214
195 // TODO(bunnei): Verify arbiter address... 215 if (!CheckWaitType(thread, WAITTYPE_ARB, arbiter, address))
196 if (!VerifyWait(thread, WAITTYPE_ARB, arbiter))
197 continue; 216 continue;
198 217
199 if (thread == nullptr) 218 if (thread == nullptr)
200 continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. 219 continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up.
220
201 if(thread->current_priority <= priority) { 221 if(thread->current_priority <= priority) {
202 highest_priority_thread = handle; 222 highest_priority_thread = handle;
203 priority = thread->current_priority; 223 priority = thread->current_priority;
@@ -215,10 +235,9 @@ void ArbitrateAllThreads(u32 arbiter, u32 address) {
215 235
216 // Iterate through threads, find highest priority thread that is waiting to be arbitrated... 236 // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
217 for (Handle handle : thread_queue) { 237 for (Handle handle : thread_queue) {
218 Thread* thread = g_object_pool.Get<Thread>(handle); 238 Thread* thread = g_handle_table.Get<Thread>(handle);
219 239
220 // TODO(bunnei): Verify arbiter address... 240 if (CheckWaitType(thread, WAITTYPE_ARB, arbiter, address))
221 if (VerifyWait(thread, WAITTYPE_ARB, arbiter))
222 ResumeThreadFromWait(handle); 241 ResumeThreadFromWait(handle);
223 } 242 }
224} 243}
@@ -269,14 +288,9 @@ Thread* NextThread() {
269 if (next == 0) { 288 if (next == 0) {
270 return nullptr; 289 return nullptr;
271 } 290 }
272 return Kernel::g_object_pool.Get<Thread>(next); 291 return Kernel::g_handle_table.Get<Thread>(next);
273} 292}
274 293
275/**
276 * Puts the current thread in the wait state for the given type
277 * @param wait_type Type of wait
278 * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
279 */
280void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { 294void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
281 Thread* thread = GetCurrentThread(); 295 Thread* thread = GetCurrentThread();
282 thread->wait_type = wait_type; 296 thread->wait_type = wait_type;
@@ -284,11 +298,18 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
284 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); 298 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
285} 299}
286 300
301void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) {
302 WaitCurrentThread(wait_type, wait_handle);
303 GetCurrentThread()->wait_address = wait_address;
304}
305
287/// Resumes a thread from waiting by marking it as "ready" 306/// Resumes a thread from waiting by marking it as "ready"
288void ResumeThreadFromWait(Handle handle) { 307void ResumeThreadFromWait(Handle handle) {
289 Thread* thread = Kernel::g_object_pool.Get<Thread>(handle); 308 Thread* thread = Kernel::g_handle_table.Get<Thread>(handle);
290 if (thread) { 309 if (thread) {
291 thread->status &= ~THREADSTATUS_WAIT; 310 thread->status &= ~THREADSTATUS_WAIT;
311 thread->wait_handle = 0;
312 thread->wait_type = WAITTYPE_NONE;
292 if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { 313 if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
293 ChangeReadyState(thread, true); 314 ChangeReadyState(thread, true);
294 } 315 }
@@ -301,12 +322,12 @@ void DebugThreadQueue() {
301 if (!thread) { 322 if (!thread) {
302 return; 323 return;
303 } 324 }
304 INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); 325 LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
305 for (u32 i = 0; i < thread_queue.size(); i++) { 326 for (u32 i = 0; i < thread_queue.size(); i++) {
306 Handle handle = thread_queue[i]; 327 Handle handle = thread_queue[i];
307 s32 priority = thread_ready_queue.contains(handle); 328 s32 priority = thread_ready_queue.contains(handle);
308 if (priority != -1) { 329 if (priority != -1) {
309 INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle); 330 LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle);
310 } 331 }
311 } 332 }
312} 333}
@@ -316,15 +337,17 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
316 s32 processor_id, u32 stack_top, int stack_size) { 337 s32 processor_id, u32 stack_top, int stack_size) {
317 338
318 _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), 339 _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
319 "CreateThread priority=%d, outside of allowable range!", priority) 340 "priority=%d, outside of allowable range!", priority)
320 341
321 Thread* thread = new Thread; 342 Thread* thread = new Thread;
322 343
323 handle = Kernel::g_object_pool.Create(thread); 344 // TOOD(yuriks): Fix error reporting
345 handle = Kernel::g_handle_table.Create(thread).ValueOr(INVALID_HANDLE);
324 346
325 thread_queue.push_back(handle); 347 thread_queue.push_back(handle);
326 thread_ready_queue.prepare(priority); 348 thread_ready_queue.prepare(priority);
327 349
350 thread->thread_id = next_thread_id++;
328 thread->status = THREADSTATUS_DORMANT; 351 thread->status = THREADSTATUS_DORMANT;
329 thread->entry_point = entry_point; 352 thread->entry_point = entry_point;
330 thread->stack_top = stack_top; 353 thread->stack_top = stack_top;
@@ -333,6 +356,7 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
333 thread->processor_id = processor_id; 356 thread->processor_id = processor_id;
334 thread->wait_type = WAITTYPE_NONE; 357 thread->wait_type = WAITTYPE_NONE;
335 thread->wait_handle = 0; 358 thread->wait_handle = 0;
359 thread->wait_address = 0;
336 thread->name = name; 360 thread->name = name;
337 361
338 return thread; 362 return thread;
@@ -343,24 +367,24 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
343 u32 stack_top, int stack_size) { 367 u32 stack_top, int stack_size) {
344 368
345 if (name == nullptr) { 369 if (name == nullptr) {
346 ERROR_LOG(KERNEL, "CreateThread(): nullptr name"); 370 LOG_ERROR(Kernel_SVC, "nullptr name");
347 return -1; 371 return -1;
348 } 372 }
349 if ((u32)stack_size < 0x200) { 373 if ((u32)stack_size < 0x200) {
350 ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid stack_size=0x%08X", name, 374 LOG_ERROR(Kernel_SVC, "(name=%s): invalid stack_size=0x%08X", name,
351 stack_size); 375 stack_size);
352 return -1; 376 return -1;
353 } 377 }
354 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { 378 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
355 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); 379 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
356 WARN_LOG(KERNEL, "CreateThread(name=%s): invalid priority=0x%08X, clamping to %08X", 380 LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d",
357 name, priority, new_priority); 381 name, priority, new_priority);
358 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm 382 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
359 // validity of this 383 // validity of this
360 priority = new_priority; 384 priority = new_priority;
361 } 385 }
362 if (!Memory::GetPointer(entry_point)) { 386 if (!Memory::GetPointer(entry_point)) {
363 ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid entry %08x", name, entry_point); 387 LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point);
364 return -1; 388 return -1;
365 } 389 }
366 Handle handle; 390 Handle handle;
@@ -375,7 +399,7 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
375 399
376/// Get the priority of the thread specified by handle 400/// Get the priority of the thread specified by handle
377ResultVal<u32> GetThreadPriority(const Handle handle) { 401ResultVal<u32> GetThreadPriority(const Handle handle) {
378 Thread* thread = g_object_pool.Get<Thread>(handle); 402 Thread* thread = g_handle_table.Get<Thread>(handle);
379 if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); 403 if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
380 404
381 return MakeResult<u32>(thread->current_priority); 405 return MakeResult<u32>(thread->current_priority);
@@ -387,7 +411,7 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) {
387 if (!handle) { 411 if (!handle) {
388 thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior? 412 thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior?
389 } else { 413 } else {
390 thread = g_object_pool.Get<Thread>(handle); 414 thread = g_handle_table.Get<Thread>(handle);
391 if (thread == nullptr) { 415 if (thread == nullptr) {
392 return InvalidHandle(ErrorModule::Kernel); 416 return InvalidHandle(ErrorModule::Kernel);
393 } 417 }
@@ -397,7 +421,7 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) {
397 // If priority is invalid, clamp to valid range 421 // If priority is invalid, clamp to valid range
398 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { 422 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
399 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); 423 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
400 WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority); 424 LOG_WARNING(Kernel_SVC, "invalid priority=%d, clamping to %d", priority, new_priority);
401 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm 425 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
402 // validity of this 426 // validity of this
403 priority = new_priority; 427 priority = new_priority;
@@ -450,24 +474,44 @@ void Reschedule() {
450 Thread* prev = GetCurrentThread(); 474 Thread* prev = GetCurrentThread();
451 Thread* next = NextThread(); 475 Thread* next = NextThread();
452 HLE::g_reschedule = false; 476 HLE::g_reschedule = false;
453 if (next > 0) {
454 INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
455 477
478 if (next != nullptr) {
479 LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
456 SwitchContext(next); 480 SwitchContext(next);
481 } else {
482 LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle());
457 483
458 // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep 484 for (Handle handle : thread_queue) {
459 // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again. 485 Thread* thread = g_handle_table.Get<Thread>(handle);
460 // This results in the current thread yielding on a VBLANK once, and then it will be 486 LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X",
461 // immediately placed back in the queue for execution. 487 thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle);
462 if (prev->wait_type == WAITTYPE_VBLANK) {
463 ResumeThreadFromWait(prev->GetHandle());
464 } 488 }
465 } 489 }
490
491 // TODO(bunnei): Hack - There is no timing mechanism yet to wake up a thread if it has been put
492 // to sleep. So, we'll just immediately set it to "ready" again after an attempted context
493 // switch has occurred. This results in the current thread yielding on a sleep once, and then it
494 // will immediately be placed back in the queue for execution.
495
496 if (CheckWaitType(prev, WAITTYPE_SLEEP))
497 ResumeThreadFromWait(prev->GetHandle());
498}
499
500ResultCode GetThreadId(u32* thread_id, Handle handle) {
501 Thread* thread = g_handle_table.Get<Thread>(handle);
502 if (thread == nullptr)
503 return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS,
504 ErrorSummary::WrongArgument, ErrorLevel::Permanent);
505
506 *thread_id = thread->thread_id;
507
508 return RESULT_SUCCESS;
466} 509}
467 510
468//////////////////////////////////////////////////////////////////////////////////////////////////// 511////////////////////////////////////////////////////////////////////////////////////////////////////
469 512
470void ThreadingInit() { 513void ThreadingInit() {
514 next_thread_id = INITIAL_THREAD_ID;
471} 515}
472 516
473void ThreadingShutdown() { 517void ThreadingShutdown() {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index ce63a70d3..0e1397cd9 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -1,10 +1,13 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project 1// Copyright 2014 Citra Emulator Project / PPSSPP Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8
9#include "core/mem_map.h"
10
8#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
9#include "core/hle/result.h" 12#include "core/hle/result.h"
10 13
@@ -37,7 +40,6 @@ enum WaitType {
37 WAITTYPE_SEMA, 40 WAITTYPE_SEMA,
38 WAITTYPE_EVENT, 41 WAITTYPE_EVENT,
39 WAITTYPE_THREADEND, 42 WAITTYPE_THREADEND,
40 WAITTYPE_VBLANK,
41 WAITTYPE_MUTEX, 43 WAITTYPE_MUTEX,
42 WAITTYPE_SYNCH, 44 WAITTYPE_SYNCH,
43 WAITTYPE_ARB, 45 WAITTYPE_ARB,
@@ -58,6 +60,14 @@ void Reschedule();
58/// Stops the current thread 60/// Stops the current thread
59ResultCode StopThread(Handle thread, const char* reason); 61ResultCode StopThread(Handle thread, const char* reason);
60 62
63/**
64 * Retrieves the ID of the specified thread handle
65 * @param thread_id Will contain the output thread id
66 * @param handle Handle to the thread we want
67 * @return Whether the function was successful or not
68 */
69ResultCode GetThreadId(u32* thread_id, Handle handle);
70
61/// Resumes a thread from waiting by marking it as "ready" 71/// Resumes a thread from waiting by marking it as "ready"
62void ResumeThreadFromWait(Handle handle); 72void ResumeThreadFromWait(Handle handle);
63 73
@@ -77,6 +87,14 @@ Handle GetCurrentThreadHandle();
77 */ 87 */
78void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); 88void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
79 89
90/**
91 * Puts the current thread in the wait state for the given type
92 * @param wait_type Type of wait
93 * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
94 * @param wait_address Arbitration address used to resume from wait
95 */
96void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address);
97
80/// Put current thread in a wait state - on WaitSynchronization 98/// Put current thread in a wait state - on WaitSynchronization
81void WaitThread_Synchronization(); 99void WaitThread_Synchronization();
82 100