Merge branch 'linux' of git://github.com/dougvj/xenia into linux

# Conflicts:
#	.travis.yml
This commit is contained in:
DrChat
2017-12-14 19:20:02 -06:00
45 changed files with 2214 additions and 369 deletions

View File

@@ -29,7 +29,8 @@ struct bf {
// For enum values, we strip them down to an underlying type.
typedef
typename std::conditional<std::is_enum<T>::value, std::underlying_type<T>,
std::identity<T>>::type::type value_type;
std::remove_reference<T>>::type::type
value_type;
inline value_type mask() const {
return (((value_type)~0) >> (8 * sizeof(value_type) - n_bits)) << position;
}
@@ -39,4 +40,4 @@ struct bf {
} // namespace xe
#endif // XENIA_BASE_BIT_FIELD_H_
#endif // XENIA_BASE_BIT_FIELD_H_

View File

@@ -0,0 +1,35 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/exception_handler.h"
#include "xenia/base/assert.h"
#include "xenia/base/math.h"
#include "xenia/base/platform_linux.h"
namespace xe {
// This can be as large as needed, but isn't often needed.
// As we will be sometimes firing many exceptions we want to avoid having to
// scan the table too much or invoke many custom handlers.
constexpr size_t kMaxHandlerCount = 8;
// All custom handlers, left-aligned and null terminated.
// Executed in order.
std::pair<ExceptionHandler::Handler, void*> handlers_[kMaxHandlerCount];
void ExceptionHandler::Install(Handler fn, void* data) {
// TODO(dougvj) stub
}
void ExceptionHandler::Uninstall(Handler fn, void* data) {
// TODO(dougvj) stub
}
} // namespace xe

View File

@@ -12,6 +12,8 @@
#include "xenia/base/string.h"
#include <dirent.h>
#include <fcntl.h>
#include <ftw.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -33,6 +35,124 @@ bool CreateFolder(const std::wstring& path) {
return mkdir(xe::to_string(path).c_str(), 0774);
}
static int removeCallback(const char* fpath, const struct stat* sb,
int typeflag, struct FTW* ftwbuf) {
int rv = remove(fpath);
return rv;
}
bool DeleteFolder(const std::wstring& path) {
return nftw(xe::to_string(path).c_str(), removeCallback, 64,
FTW_DEPTH | FTW_PHYS) == 0
? true
: false;
}
static uint64_t convertUnixtimeToWinFiletime(time_t unixtime) {
// Linux uses number of seconds since 1/1/1970, and Windows uses
// number of nanoseconds since 1/1/1601
// so we convert linux time to nanoseconds and then add the number of
// nanoseconds from 1601 to 1970
// see https://msdn.microsoft.com/en-us/library/ms724228
uint64_t filetime = filetime = (unixtime * 10000000) + 116444736000000000;
return filetime;
}
bool IsFolder(const std::wstring& path) {
struct stat st;
if (stat(xe::to_string(path).c_str(), &st) == 0) {
if (S_ISDIR(st.st_mode)) return true;
}
return false;
}
bool CreateFile(const std::wstring& path) {
int file = creat(xe::to_string(path).c_str(), 0774);
if (file >= 0) {
close(file);
return true;
}
return false;
}
bool DeleteFile(const std::wstring& path) {
return (xe::to_string(path).c_str()) == 0 ? true : false;
}
class PosixFileHandle : public FileHandle {
public:
PosixFileHandle(std::wstring path, int handle)
: FileHandle(std::move(path)), handle_(handle) {}
~PosixFileHandle() override {
close(handle_);
handle_ = -1;
}
bool Read(size_t file_offset, void* buffer, size_t buffer_length,
size_t* out_bytes_read) override {
ssize_t out = pread(handle_, buffer, buffer_length, file_offset);
*out_bytes_read = out;
return out >= 0 ? true : false;
}
bool Write(size_t file_offset, const void* buffer, size_t buffer_length,
size_t* out_bytes_written) override {
ssize_t out = pwrite(handle_, buffer, buffer_length, file_offset);
*out_bytes_written = out;
return out >= 0 ? true : false;
}
void Flush() override { fsync(handle_); }
private:
int handle_ = -1;
};
std::unique_ptr<FileHandle> FileHandle::OpenExisting(std::wstring path,
uint32_t desired_access) {
int open_access;
if (desired_access & FileAccess::kGenericRead) {
open_access |= O_RDONLY;
}
if (desired_access & FileAccess::kGenericWrite) {
open_access |= O_WRONLY;
}
if (desired_access & FileAccess::kGenericExecute) {
open_access |= O_RDONLY;
}
if (desired_access & FileAccess::kGenericAll) {
open_access |= O_RDWR;
}
if (desired_access & FileAccess::kFileReadData) {
open_access |= O_RDONLY;
}
if (desired_access & FileAccess::kFileWriteData) {
open_access |= O_WRONLY;
}
if (desired_access & FileAccess::kFileAppendData) {
open_access |= O_APPEND;
}
int handle = open(xe::to_string(path).c_str(), open_access);
if (handle == -1) {
// TODO(benvanik): pick correct response.
return nullptr;
}
return std::make_unique<PosixFileHandle>(path, handle);
}
bool GetInfo(const std::wstring& path, FileInfo* out_info) {
struct stat st;
if (stat(xe::to_string(path).c_str(), &st) == 0) {
if (S_ISDIR(st.st_mode)) {
out_info->type = FileInfo::Type::kDirectory;
} else {
out_info->type = FileInfo::Type::kFile;
}
out_info->create_timestamp = convertUnixtimeToWinFiletime(st.st_ctime);
out_info->access_timestamp = convertUnixtimeToWinFiletime(st.st_atime);
out_info->write_timestamp = convertUnixtimeToWinFiletime(st.st_mtime);
return true;
}
return false;
}
std::vector<FileInfo> ListFiles(const std::wstring& path) {
std::vector<FileInfo> result;
@@ -43,18 +163,20 @@ std::vector<FileInfo> ListFiles(const std::wstring& path) {
while (auto ent = readdir(dir)) {
FileInfo info;
info.name = xe::to_wstring(ent->d_name);
struct stat st;
stat((xe::to_string(path) + xe::to_string(info.name)).c_str(), &st);
info.create_timestamp = convertUnixtimeToWinFiletime(st.st_ctime);
info.access_timestamp = convertUnixtimeToWinFiletime(st.st_atime);
info.write_timestamp = convertUnixtimeToWinFiletime(st.st_mtime);
if (ent->d_type == DT_DIR) {
info.type = FileInfo::Type::kDirectory;
info.total_size = 0;
} else {
info.type = FileInfo::Type::kFile;
info.total_size = 0; // TODO(DrChat): Find a way to get this
info.total_size = st.st_size;
}
info.create_timestamp = 0;
info.access_timestamp = 0;
info.write_timestamp = 0;
info.name = xe::to_wstring(ent->d_name);
result.push_back(info);
}
@@ -62,4 +184,4 @@ std::vector<FileInfo> ListFiles(const std::wstring& path) {
}
} // namespace filesystem
} // namespace xe
} // namespace xe

View File

@@ -11,10 +11,10 @@
#define XENIA_BASE_MATH_H_
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <type_traits>
#include "xenia/base/platform.h"
#if XE_ARCH_AMD64

View File

@@ -0,0 +1,21 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/platform_linux.h"
#include <stdlib.h>
#include <string>
namespace xe {
void LaunchBrowser(const char* url) {
auto cmd = std::string("xdg-open " + std::string(url));
system(cmd.c_str());
}
} // namespace xe

View File

@@ -0,0 +1,32 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_PLATFORM_X11_H_
#define XENIA_BASE_PLATFORM_X11_H_
// NOTE: if you're including this file it means you are explicitly depending
// on Linux headers. Including this file outside of linux platform specific
// source code will break portability
#include "xenia/base/platform.h"
// Xlib/Xcb is used only for GLX/Vulkan interaction, the window management
// and input events are done with gtk/gdk
#include <X11/Xlib-xcb.h>
#include <X11/Xlib.h>
#include <X11/Xos.h>
#include <X11/Xutil.h>
#include <xcb/xcb.h>
// Used for window management. Gtk is for GUI and wigets, gdk is for lower
// level events like key presses, mouse events, window handles, etc
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
#endif // XENIA_BASE_PLATFORM_X11_H_

View File

@@ -268,6 +268,7 @@ void Profiler::ToggleDisplay() {}
void Profiler::TogglePause() {}
void Profiler::set_window(ui::Window* window) {}
void Profiler::Present() {}
void Profiler::Flip() {}
#endif // XE_OPTION_PROFILING

View File

@@ -16,6 +16,7 @@
#include <climits>
#include <condition_variable>
#include <cstdint>
#include <functional>
#include <memory>
#include <mutex>
#include <string>

View File

@@ -13,9 +13,5 @@
#include <time.h>
namespace xe {
namespace threading {
void MaybeYield() { pthread_yield(); }
} // namespace threading
namespace threading {} // namespace threading
} // namespace xe

View File

@@ -14,6 +14,7 @@
#include <pthread.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
@@ -21,6 +22,9 @@
namespace xe {
namespace threading {
// TODO(dougvj)
void EnableAffinityConfiguration() {}
// uint64_t ticks() { return mach_absolute_time(); }
uint32_t current_thread_system_id() {
@@ -32,9 +36,16 @@ void set_name(const std::string& name) {
}
void set_name(std::thread::native_handle_type handle, const std::string& name) {
pthread_setname_np(pthread_self(), name.c_str());
pthread_setname_np(handle, name.c_str());
}
void MaybeYield() {
pthread_yield();
__sync_synchronize();
}
void SyncMemory() { __sync_synchronize(); }
void Sleep(std::chrono::microseconds duration) {
timespec rqtp = {time_t(duration.count() / 1000000),
time_t(duration.count() % 1000)};
@@ -42,11 +53,124 @@ void Sleep(std::chrono::microseconds duration) {
// TODO(benvanik): spin while rmtp >0?
}
template <typename T>
class PosixHandle : public T {
// TODO(dougvj) Not sure how to implement the equivalent of this on POSIX.
SleepResult AlertableSleep(std::chrono::microseconds duration) {
sleep(duration.count() / 1000);
return SleepResult::kSuccess;
}
// TODO(dougvj) We can probably wrap this with pthread_key_t but the type of
// TlsHandle probably needs to be refactored
TlsHandle AllocateTlsHandle() { assert_always(); }
bool FreeTlsHandle(TlsHandle handle) { return true; }
uintptr_t GetTlsValue(TlsHandle handle) { assert_always(); }
bool SetTlsValue(TlsHandle handle, uintptr_t value) { assert_always(); }
// TODO(dougvj)
class PosixHighResolutionTimer : public HighResolutionTimer {
public:
explicit PosixHandle(pthread_t handle) : handle_(handle) {}
~PosixHandle() override {}
PosixHighResolutionTimer(std::function<void()> callback)
: callback_(callback) {}
~PosixHighResolutionTimer() override {}
bool Initialize(std::chrono::milliseconds period) {
assert_always();
return false;
}
private:
std::function<void()> callback_;
};
std::unique_ptr<HighResolutionTimer> HighResolutionTimer::CreateRepeating(
std::chrono::milliseconds period, std::function<void()> callback) {
auto timer = std::make_unique<PosixHighResolutionTimer>(std::move(callback));
if (!timer->Initialize(period)) {
return nullptr;
}
return std::unique_ptr<HighResolutionTimer>(timer.release());
}
// TODO(dougvj) There really is no native POSIX handle for a single wait/signal
// construct pthreads is at a lower level with more handles for such a mechanism
// This simple wrapper class could function as our handle, but probably needs
// some more functionality
class PosixCondition {
public:
PosixCondition() : signal_(false) {
pthread_mutex_init(&mutex_, NULL);
pthread_cond_init(&cond_, NULL);
}
~PosixCondition() {
pthread_mutex_destroy(&mutex_);
pthread_cond_destroy(&cond_);
}
void Signal() {
pthread_mutex_lock(&mutex_);
signal_ = true;
pthread_cond_broadcast(&cond_);
pthread_mutex_unlock(&mutex_);
}
void Reset() {
pthread_mutex_lock(&mutex_);
signal_ = false;
pthread_mutex_unlock(&mutex_);
}
bool Wait(unsigned int timeout_ms) {
// Assume 0 means no timeout, not instant timeout
if (timeout_ms == 0) {
Wait();
}
struct timespec time_to_wait;
struct timeval now;
gettimeofday(&now, NULL);
// Add the number of seconds we want to wait to the current time
time_to_wait.tv_sec = now.tv_sec + (timeout_ms / 1000);
// Add the number of nanoseconds we want to wait to the current nanosecond
// stride
long nsec = (now.tv_usec + (timeout_ms % 1000)) * 1000;
// If we overflowed the nanosecond count then we add a second
time_to_wait.tv_sec += nsec / 1000000000UL;
// We only add nanoseconds within the 1 second stride
time_to_wait.tv_nsec = nsec % 1000000000UL;
pthread_mutex_lock(&mutex_);
while (!signal_) {
int status = pthread_cond_timedwait(&cond_, &mutex_, &time_to_wait);
if (status == ETIMEDOUT) return false; // We timed out
}
pthread_mutex_unlock(&mutex_);
return true; // We didn't time out
}
bool Wait() {
pthread_mutex_lock(&mutex_);
while (!signal_) {
pthread_cond_wait(&cond_, &mutex_);
}
pthread_mutex_unlock(&mutex_);
return true; // Did not time out;
}
private:
bool signal_;
pthread_cond_t cond_;
pthread_mutex_t mutex_;
};
// Native posix thread handle
template <typename T>
class PosixThreadHandle : public T {
public:
explicit PosixThreadHandle(pthread_t handle) : handle_(handle) {}
~PosixThreadHandle() override {}
protected:
void* native_handle() const override {
@@ -56,13 +180,134 @@ class PosixHandle : public T {
pthread_t handle_;
};
class PosixThread : public PosixHandle<Thread> {
// This is wraps a condition object as our handle because posix has no single
// native handle for higher level concurrency constructs such as semaphores
template <typename T>
class PosixConditionHandle : public T {
public:
explicit PosixThread(pthread_t handle) : PosixHandle(handle) {}
~PosixConditionHandle() override {}
protected:
void* native_handle() const override {
return reinterpret_cast<void*>(const_cast<PosixCondition*>(&handle_));
}
PosixCondition handle_;
};
// TODO(dougvj)
WaitResult Wait(WaitHandle* wait_handle, bool is_alertable,
std::chrono::milliseconds timeout) {
assert_always();
return WaitResult::kFailed;
}
// TODO(dougvj)
WaitResult SignalAndWait(WaitHandle* wait_handle_to_signal,
WaitHandle* wait_handle_to_wait_on, bool is_alertable,
std::chrono::milliseconds timeout) {
assert_always();
return WaitResult::kFailed;
}
// TODO(dougvj)
std::pair<WaitResult, size_t> WaitMultiple(WaitHandle* wait_handles[],
size_t wait_handle_count,
bool wait_all, bool is_alertable,
std::chrono::milliseconds timeout) {
assert_always();
return std::pair<WaitResult, size_t>(WaitResult::kFailed, 0);
}
// TODO(dougvj)
class PosixEvent : public PosixConditionHandle<Event> {
public:
PosixEvent(bool initial_state, int auto_reset) { assert_always(); }
~PosixEvent() override = default;
void Set() override { assert_always(); }
void Reset() override { assert_always(); }
void Pulse() override { assert_always(); }
private:
PosixCondition condition_;
};
std::unique_ptr<Event> Event::CreateManualResetEvent(bool initial_state) {
return std::make_unique<PosixEvent>(PosixEvent(initial_state, false));
}
std::unique_ptr<Event> Event::CreateAutoResetEvent(bool initial_state) {
return std::make_unique<PosixEvent>(PosixEvent(initial_state, true));
}
// TODO(dougvj)
class PosixSemaphore : public PosixConditionHandle<Semaphore> {
public:
PosixSemaphore(int initial_count, int maximum_count) { assert_always(); }
~PosixSemaphore() override = default;
bool Release(int release_count, int* out_previous_count) override {
assert_always();
return false;
}
};
std::unique_ptr<Semaphore> Semaphore::Create(int initial_count,
int maximum_count) {
return std::make_unique<PosixSemaphore>(initial_count, maximum_count);
}
// TODO(dougvj)
class PosixMutant : public PosixConditionHandle<Mutant> {
public:
PosixMutant(bool initial_owner) { assert_always(); }
~PosixMutant() = default;
bool Release() override {
assert_always();
return false;
}
};
std::unique_ptr<Mutant> Mutant::Create(bool initial_owner) {
return std::make_unique<PosixMutant>(initial_owner);
}
// TODO(dougvj)
class PosixTimer : public PosixConditionHandle<Timer> {
public:
PosixTimer(bool manual_reset) { assert_always(); }
~PosixTimer() = default;
bool SetOnce(std::chrono::nanoseconds due_time,
std::function<void()> opt_callback) override {
assert_always();
return false;
}
bool SetRepeating(std::chrono::nanoseconds due_time,
std::chrono::milliseconds period,
std::function<void()> opt_callback) override {
assert_always();
return false;
}
bool Cancel() override {
assert_always();
return false;
}
};
std::unique_ptr<Timer> Timer::CreateManualResetTimer() {
return std::make_unique<PosixTimer>(true);
}
std::unique_ptr<Timer> Timer::CreateSynchronizationTimer() {
return std::make_unique<PosixTimer>(false);
}
class PosixThread : public PosixThreadHandle<Thread> {
public:
explicit PosixThread(pthread_t handle) : PosixThreadHandle(handle) {}
~PosixThread() = default;
void set_name(std::string name) override {
// TODO(DrChat)
pthread_setname_np(handle_, name.c_str());
}
uint32_t system_id() const override { return 0; }
@@ -141,5 +386,20 @@ std::unique_ptr<Thread> Thread::Create(CreationParameters params,
return std::unique_ptr<PosixThread>(new PosixThread(handle));
}
Thread* Thread::GetCurrentThread() {
if (current_thread_) {
return current_thread_.get();
}
pthread_t handle = pthread_self();
current_thread_ = std::make_unique<PosixThread>(handle);
return current_thread_.get();
}
void Thread::Exit(int exit_code) {
pthread_exit(reinterpret_cast<void*>(exit_code));
}
} // namespace threading
} // namespace xe