diff --git a/common/src/Utilities/CMakeLists.txt b/common/src/Utilities/CMakeLists.txt index 88f2238be9..ccb22f20de 100644 --- a/common/src/Utilities/CMakeLists.txt +++ b/common/src/Utilities/CMakeLists.txt @@ -52,6 +52,7 @@ endif(CMAKE_BUILD_TYPE STREQUAL Release) # variable with all sources of this library set(UtilitiesSources + VirtualMemory.cpp AlignedMalloc.cpp ../../include/Utilities/FixedPointTypes.inl ../../include/Utilities/EventSource.inl @@ -65,8 +66,6 @@ set(UtilitiesSources HashTools.cpp IniInterface.cpp Linux/LnxHostSys.cpp - Linux/LnxMisc.cpp - Linux/LnxThreads.cpp Mutex.cpp PathUtils.cpp PrecompiledHeader.cpp @@ -76,12 +75,10 @@ set(UtilitiesSources pxStreams.cpp pxTranslate.cpp pxWindowTextWriter.cpp - Semaphore.cpp StringHelpers.cpp ThreadingDialogs.cpp ThreadTools.cpp vssprintf.cpp - VirtualMemory.cpp wxAppWithHelpers.cpp wxGuiTools.cpp wxHelpers.cpp @@ -120,7 +117,22 @@ set(UtilitiesHeaders ../../include/Utilities/wxAppWithHelpers.h ../../include/Utilities/wxBaseTools.h ../../include/Utilities/wxGuiTools.h - PrecompiledHeader.h) + PrecompiledHeader.h +) + +if(APPLE) + LIST(APPEND UtilitiesSources + Darwin/DarwinThreads.cpp + Darwin/DarwinMisc.cpp + Darwin/DarwinSemaphore.cpp + ) +else() + LIST(APPEND UtilitiesSources + Linux/LnxThreads.cpp + Linux/LnxMisc.cpp + Semaphore.cpp + ) +endif() set(UtilitiesFinalSources ${UtilitiesSources} diff --git a/common/src/Utilities/Darwin/DarwinMisc.cpp b/common/src/Utilities/Darwin/DarwinMisc.cpp new file mode 100644 index 0000000000..a8624ece0e --- /dev/null +++ b/common/src/Utilities/Darwin/DarwinMisc.cpp @@ -0,0 +1,150 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2014 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "../PrecompiledHeader.h" + +#include +#include + +#include +#include + +#include + +#define NELEM(x) \ + ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) + +// Darwin (OSX) is a bit different from Linux when requesting properties of +// the OS because of its BSD/Mach heritage. Helpfully, most of this code +// should translate pretty well to other *BSD systems. (e.g.: the sysctl(3) +// interface). +// +// For an overview of all of Darwin's sysctls, check: +// https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/sysctl.3.html + +// Return the total physical memory on the machine, in bytes. Returns 0 on +// failure (not supported by the operating system). +u64 GetPhysicalMemory() +{ + static u64 mem = 0; + + // fetch the total memory only once, as its an expensive system call and + // doesn't change during the course of the program. Thread-safety is + // ensured by atomic operations with full-barriers (usually compiled + // down to XCHG on x86). + if (__atomic_load_n(&mem, __ATOMIC_SEQ_CST) == 0) { + u64 getmem = 0; + size_t len = sizeof(getmem); + int mib[] = { CTL_HW, HW_MEMSIZE }; + if (sysctl(mib, NELEM(mib), &getmem, &len, NULL, 0) < 0) { + perror("sysctl:"); + } + __atomic_store_n(&mem, getmem, __ATOMIC_SEQ_CST); + } + + return mem; +} + +void InitCPUTicks() +{ +} + +// returns the performance-counter frequency: ticks per second (Hz) +// +// usage: +// u64 seconds_passed = GetCPUTicks() / GetTickFrequency(); +// u64 millis_passed = (GetCPUTicks() * 1000) / GetTickFrequency(); +// +// NOTE: multiply, subtract, ... your ticks before dividing by +// GetTickFrequency() to maintain good precision. +u64 GetTickFrequency() +{ + static u64 freq = 0; + + // by the time denom is not 0, the structure will have been fully + // updated and no more atomic accesses are necessary. + if (__atomic_load_n(&freq, __ATOMIC_SEQ_CST) == 0) { + mach_timebase_info_data_t info; + + // mach_timebase_info() is a syscall, very slow, that's why we take + // pains to only do it once. On x86(-64), the result is guaranteed + // to be info.denom == info.numer == 1 (i.e.: the frequency is 1e9, + // which means GetCPUTicks is just nanoseconds). + if (mach_timebase_info(&info) != KERN_SUCCESS) { + abort(); + } + + // store the calculated value atomically + __atomic_store_n(&freq, (u64) 1e9 * (u64) info.denom / (u64) info.numer, __ATOMIC_SEQ_CST); + } + + return freq; +} + +// return the number of "ticks" since some arbitrary, fixed time in the +// past. On OSX x86(-64), this is actually the number of nanoseconds passed, +// because mach_timebase_info.numer == denom == 1. So "ticks" == +// nanoseconds. +u64 GetCPUTicks() +{ + return mach_absolute_time(); +} + +wxString GetOSVersionString() +{ + wxString version; + static int initialized = 0; + + // fetch the OS description only once (thread-safely) + if (__atomic_load_n(&initialized, __ATOMIC_SEQ_CST) == 0) { + char type[32] = {0}; + char release[32] = {0}; + char arch[32] = {0}; + +#define SYSCTL_GET(var, base, name) \ + do { \ + int mib[] = { base, name }; \ + size_t len = sizeof(var); \ + sysctl(mib, NELEM(mib), NULL, &len, NULL, 0); \ + sysctl(mib, NELEM(mib), var, &len, NULL, 0); \ + } while (0) + + SYSCTL_GET(release, CTL_KERN, KERN_OSRELEASE); + SYSCTL_GET(type, CTL_KERN, KERN_OSTYPE); + SYSCTL_GET(arch, CTL_HW, HW_MACHINE); + +#undef SYSCTL_KERN + + // I know strcat is not good, but stpcpy is not universally + // available yet. + char buf[128] = {0}; + strcat(buf, type); + strcat(buf, " "); + strcat(buf, release); + strcat(buf, " "); + strcat(buf, arch); + + version = buf; + + __atomic_store_n(&initialized, 1, __ATOMIC_SEQ_CST); + } + + return version; +} + +void ScreensaverAllow(bool allow) +{ + // no-op +} diff --git a/common/src/Utilities/Darwin/DarwinSemaphore.cpp b/common/src/Utilities/Darwin/DarwinSemaphore.cpp new file mode 100644 index 0000000000..8171a54d5c --- /dev/null +++ b/common/src/Utilities/Darwin/DarwinSemaphore.cpp @@ -0,0 +1,243 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2014 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include +#include // assert + +#include // pthread_setcancelstate() + +#include // gettimeofday() + +#include +#include // semaphore_create() and semaphore_destroy() +#include // semaphore_*() +#include // mach_error_string() +#include // mach_absolute_time() + +#include "PrecompiledHeader.h" + +#include "Threading.h" +#include "ThreadingInternal.h" + +#include "wxBaseTools.h" +#include "wxGuiTools.h" + +// -------------------------------------------------------------------------------------- +// Semaphore Implementation for Darwin/OSX +// +// Sadly, Darwin/OSX needs its own implementation of Semaphores instead of +// relying on phtreads, because OSX unnamed semaphore (the best kind) +// support is very poor. +// +// This implementation makes use of Mach primitives instead. These are also +// what Grand Central Dispatch (GCD) is based on, as far as I understand: +// http://newosxbook.com/articles/GCD.html. +// +// -------------------------------------------------------------------------------------- + +#define MACH_CHECK(mach_retval) \ + do { \ + kern_return_t _kr = (mach_retval); \ + if (_kr != KERN_SUCCESS) { \ + fprintf(stderr, "mach error: %s", mach_error_string(_kr)); \ + assert(_kr == KERN_SUCCESS); \ + } \ + } while (0) + +Threading::Semaphore::Semaphore() +{ + // other platforms explicitly make a thread-private (unshared) semaphore + // here. But it seems Mach doesn't support that. + MACH_CHECK(semaphore_create(mach_task_self(), (semaphore_t *)&m_sema, SYNC_POLICY_FIFO, 0)); + __atomic_store_n(&m_counter, 0, __ATOMIC_SEQ_CST); +} + +Threading::Semaphore::~Semaphore() throw() +{ + MACH_CHECK(semaphore_destroy(mach_task_self(), (semaphore_t) m_sema)); + __atomic_store_n(&m_counter, 0, __ATOMIC_SEQ_CST); +} + +void Threading::Semaphore::Reset() +{ + MACH_CHECK(semaphore_destroy(mach_task_self(), (semaphore_t) m_sema)); + MACH_CHECK(semaphore_create(mach_task_self(), (semaphore_t *) &m_sema, SYNC_POLICY_FIFO, 0)); + __atomic_store_n(&m_counter, 0, __ATOMIC_SEQ_CST); +} + +void Threading::Semaphore::Post() +{ + MACH_CHECK(semaphore_signal(m_sema)); + __atomic_add_fetch(&m_counter, 1, __ATOMIC_SEQ_CST); +} + +void Threading::Semaphore::Post(int multiple) +{ + for (int i = 0; i < multiple; ++i) { + MACH_CHECK(semaphore_signal(m_sema)); + } + __atomic_add_fetch(&m_counter, multiple, __ATOMIC_SEQ_CST); +} + +void Threading::Semaphore::WaitWithoutYield() +{ + pxAssertMsg(!wxThread::IsMain(), "Unyielding semaphore wait issued from the main/gui thread. Please use Wait() instead."); + MACH_CHECK(semaphore_wait(m_sema)); + __atomic_sub_fetch(&m_counter, 1, __ATOMIC_SEQ_CST); +} + +bool Threading::Semaphore::WaitWithoutYield(const wxTimeSpan& timeout) +{ + // This method is the reason why there has to be a special Darwin + // implementation of Semaphore. Note that semaphore_timedwait() is prone + // to returning with KERN_ABORTED, which basically signifies that some + // signal has worken it up. The best official "documentation" for + // semaphore_timedwait() is the way it's used in Grand Central Dispatch, + // which is open-source. + + // on x86 platforms, mach_absolute_time() returns nanoseconds + // TODO(aktau): on iOS a scale value from mach_timebase_info will be necessary + u64 const kOneThousand = 1000; + u64 const kOneBillion = kOneThousand * kOneThousand * kOneThousand; + u64 const delta = timeout.GetMilliseconds().GetValue() * (kOneThousand * kOneThousand); + mach_timespec_t ts; + kern_return_t kr = KERN_ABORTED; + for (u64 now = mach_absolute_time(), deadline = now + delta; + kr == KERN_ABORTED; now = mach_absolute_time()) { + if (now > deadline) { + // timed out by definition + return false; + } + + u64 timeleft = deadline - now; + ts.tv_sec = timeleft / kOneBillion; + ts.tv_nsec = timeleft % kOneBillion; + + // possible return values of semaphore_timedwait() (from XNU sources): + // internal kernel val -> return value + // THREAD_INTERRUPTED -> KERN_ABORTED + // THREAD_TIMED_OUT -> KERN_OPERATION_TIMED_OUT + // THREAD_AWAKENED -> KERN_SUCCESS + // THREAD_RESTART -> KERN_TERMINATED + // default -> KERN_FAILURE + kr = semaphore_timedwait(m_sema, ts); + } + + if (kr == KERN_OPERATION_TIMED_OUT) { + return false; + } + + // while it's entirely possible to have KERN_FAILURE here, we should + // probably assert so we can study and correct the actual error here + // (the thread dying while someone is wainting for it). + MACH_CHECK(kr); + + __atomic_sub_fetch(&m_counter, 1, __ATOMIC_SEQ_CST); + return true; +} + +// This is a wxApp-safe implementation of Wait, which makes sure and executes the App's +// pending messages *if* the Wait is performed on the Main/GUI thread. This ensures that +// user input continues to be handled and that windows continue to repaint. If the Wait is +// called from another thread, no message pumping is performed. +void Threading::Semaphore::Wait() +{ +#if wxUSE_GUI + if(!wxThread::IsMain() || (wxTheApp == NULL)) { + WaitWithoutYield(); + } + else if(_WaitGui_RecursionGuard( L"Semaphore::Wait" )) { + ScopedBusyCursor hourglass( Cursor_ReallyBusy ); + WaitWithoutYield(); + } + else { + while (!WaitWithoutYield(def_yieldgui_interval)) { + YieldToMain(); + } + } +#else + WaitWithoutYield(); +#endif +} + +// This is a wxApp-safe implementation of WaitWithoutYield, which makes sure and executes the App's +// pending messages *if* the Wait is performed on the Main/GUI thread. This ensures that +// user input continues to be handled and that windows continue to repaint. If the Wait is +// called from another thread, no message pumping is performed. +// +// Returns: +// false if the wait timed out before the semaphore was signaled, or true if the signal was +// reached prior to timeout. +// +bool Threading::Semaphore::Wait(const wxTimeSpan& timeout) +{ +#if wxUSE_GUI + if(!wxThread::IsMain() || (wxTheApp == NULL)) { + return WaitWithoutYield(timeout); + } + else if (_WaitGui_RecursionGuard( L"Semaphore::TimedWait")) { + ScopedBusyCursor hourglass(Cursor_ReallyBusy); + return WaitWithoutYield(timeout); + } + else { + //ScopedBusyCursor hourglass( Cursor_KindaBusy ); + wxTimeSpan countdown((timeout)); + + do { + if (WaitWithoutYield(def_yieldgui_interval)) break; + YieldToMain(); + countdown -= def_yieldgui_interval; + } while (countdown.GetMilliseconds() > 0); + + return countdown.GetMilliseconds() > 0; + } +#else + return WaitWithoutYield(timeout); +#endif +} + +// Performs an uncancellable wait on a semaphore; restoring the thread's previous cancel state +// after the wait has completed. Useful for situations where the semaphore itself is stored on +// the stack and passed to another thread via GUI message or such, avoiding complications where +// the thread might be canceled and the stack value becomes invalid. +// +// Performance note: this function has quite a bit more overhead compared to Semaphore::WaitWithoutYield(), so +// consider manually specifying the thread as uncancellable and using WaitWithoutYield() instead if you need +// to do a lot of no-cancel waits in a tight loop worker thread, for example. +// +// I'm unsure how to do this with pure Mach primitives, the docs in +// osfmk/man seem a bit out of date so perhaps there's a possibility, but +// since as far as I know Mach threads are 1-to-1 on BSD uthreads (and thus +// POSIX threads), this should work. -- aktau +void Threading::Semaphore::WaitNoCancel() +{ + int oldstate; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); + Wait(); + pthread_setcancelstate(oldstate, NULL); +} + +void Threading::Semaphore::WaitNoCancel(const wxTimeSpan& timeout) +{ + int oldstate; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); + Wait(timeout); + pthread_setcancelstate(oldstate, NULL); +} + +int Threading::Semaphore::Count() +{ + return __atomic_load_n(&m_counter, __ATOMIC_SEQ_CST); +} diff --git a/common/src/Utilities/Darwin/DarwinThreads.cpp b/common/src/Utilities/Darwin/DarwinThreads.cpp new file mode 100644 index 0000000000..6fb6520a43 --- /dev/null +++ b/common/src/Utilities/Darwin/DarwinThreads.cpp @@ -0,0 +1,133 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2014 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "../PrecompiledHeader.h" +#include "PersistentThread.h" + +#include + +#if !defined(__APPLE__) +# error "DarwinThreads.cpp should only be compiled by projects or makefiles targeted at OSX." +#else + +#include +#include +#include + +// Note: assuming multicore is safer because it forces the interlocked routines to use +// the LOCK prefix. The prefix works on single core CPUs fine (but is slow), but not +// having the LOCK prefix is very bad indeed. + +__forceinline void Threading::Sleep( int ms ) +{ + usleep(1000 * ms); +} + +// For use in spin/wait loops, acts as a hint to Intel CPUs and should, in theory +// improve performance and reduce cpu power consumption. +__forceinline void Threading::SpinWait() +{ + // If this doesn't compile you can just comment it out (it only serves as a + // performance hint and isn't required). + __asm__ ( "pause" ); +} + +__forceinline void Threading::EnableHiresScheduler() +{ + // Darwin has customizable schedulers, see xnu/osfmk/man. Not + // implemented yet though (and not sure if useful for pcsx2). +} + +__forceinline void Threading::DisableHiresScheduler() +{ + // see EnableHiresScheduler() +} + +// Just like on Windows, this is not really the number of ticks per second, +// but just a factor by which one has to divide GetThreadCpuTime() or +// pxThread::GetCpuTime() if one wants to receive a value in seconds. NOTE: +// doing this will of course yield precision loss. +u64 Threading::GetThreadTicksPerSecond() +{ + return 1000000; // the *CpuTime() functions return values in microseconds +} + +// gets the CPU time used by the current thread (both system and user), in +// microseconds, returns 0 on failure +static u64 getthreadtime(thread_port_t thread) { + mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; + thread_basic_info_data_t info; + + kern_return_t kr = thread_info(thread, THREAD_BASIC_INFO, + (thread_info_t) &info, &count); + if (kr != KERN_SUCCESS) { + return 0; + } + + // add system and user time + return (u64) info.user_time.seconds * (u64) 1e6 + + (u64) info.user_time.microseconds + + (u64) info.system_time.seconds * (u64) 1e6 + + (u64) info.system_time.microseconds; +} + +// Returns the current timestamp (not relative to a real world clock) in +// units of 100 nanoseconds. The weird units are to mirror the Windows +// counterpart in WinThreads.cpp, which uses the GetThreadTimes() API. On +// OSX/Darwin, this is only accurate up until 1ms (and possibly less), so +// not very good. +u64 Threading::GetThreadCpuTime() +{ + // we could also use mach_thread_self() and mach_port_deallocate(), but + // that calls upon mach traps (kinda like system calls). Unless I missed + // something in the COMMPAGE (like Linux vDSO) which makes overrides it + // to be user-space instead. In contract, + // pthread_mach_thread_np(pthread_self()) is entirely in user-space. + u64 us = getthreadtime(pthread_mach_thread_np(pthread_self())); + return us * 10ULL; +} + +u64 Threading::pxThread::GetCpuTime() const +{ + // Get the cpu time for the thread belonging to this object. Use m_native_id and/or + // m_native_handle to implement it. Return value should be a measure of total time the + // thread has used on the CPU (scaled by the value returned by GetThreadTicksPerSecond(), + // which typically would be an OS-provided scalar or some sort). + if (!m_native_id) { + return 0; + } + + return getthreadtime((thread_port_t) m_native_id) * 10ULL; +} + +void Threading::pxThread::_platform_specific_OnStartInThread() +{ + m_native_id = (uptr) mach_thread_self(); +} + +void Threading::pxThread::_platform_specific_OnCleanupInThread() +{ + // cleanup of handles that were upened in + // _platform_specific_OnStartInThread + mach_port_deallocate(mach_task_self(), (thread_port_t) m_native_id); +} + +// name can be up to 16 bytes +void Threading::pxThread::_DoSetThreadName(const char *name) +{ + pthread_setname_np(name); +} + +#endif