Mercurial > foo_out_sdl
diff foosdk/sdk/pfc/nix-objects.cpp @ 1:20d02a178406 default tip
*: check in everything else
yay
| author | Paper <paper@tflc.us> |
|---|---|
| date | Mon, 05 Jan 2026 02:15:46 -0500 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/foosdk/sdk/pfc/nix-objects.cpp Mon Jan 05 02:15:46 2026 -0500 @@ -0,0 +1,358 @@ +#include "pfc-lite.h" + + +#ifndef _WIN32 +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <poll.h> +#include <math.h> + +#ifdef __APPLE__ +#include <mach-o/dyld.h> +#endif + +#include "nix-objects.h" +#include "string_base.h" +#include "array.h" +#include "debug.h" +#include "timers.h" +#include "filehandle.h" + +namespace pfc { + void nixFormatError( string_base & str, int code ) { + char buffer[512] = {}; + strerror_r(code, buffer, sizeof(buffer)); + str = buffer; + } + + void setNonBlocking( int fd, bool bNonBlocking ) { + int flags = fcntl(fd, F_GETFL, 0); + int flags2 = flags; + if (bNonBlocking) flags2 |= O_NONBLOCK; + else flags2 &= ~O_NONBLOCK; + if (flags2 != flags) fcntl(fd, F_SETFL, flags2); + } + + void setCloseOnExec( int fd, bool bCloseOnExec ) { + int flags = fcntl(fd, F_GETFD); + int flags2 = flags; + if (bCloseOnExec) flags2 |= FD_CLOEXEC; + else flags2 &= ~FD_CLOEXEC; + if (flags != flags2) fcntl(fd, F_SETFD, flags2); + } + + void setInheritable( int fd, bool bInheritable ) { + setCloseOnExec( fd, !bInheritable ); + } + + void createPipe( int fd[2], bool bInheritable ) { +#if defined(__linux__) && defined(O_CLOEXEC) + if (pipe2(fd, bInheritable ? 0 : O_CLOEXEC) < 0) throw exception_nix(); +#else + if (pipe(fd) < 0) throw exception_nix(); + if (!bInheritable) { + setInheritable( fd[0], false ); + setInheritable( fd[1], false ); + } +#endif + } + + exception_nix::exception_nix() { + _init(errno); + } + exception_nix::exception_nix(int code) { + _init(code); + } + void exception_nix::_init(int code) { + PFC_ASSERT( code != EINTR ); + m_code = code; + nixFormatError(m_msg, code); + } + + timeval makeTimeVal( double timeSeconds ) { + timeval tv = {}; + uint64_t temp = (uint64_t) floor( timeSeconds * 1000000.0 + 0.5); + tv.tv_usec = (uint32_t) (temp % 1000000); + tv.tv_sec = (uint32_t) (temp / 1000000); + return tv; + } + double importTimeval(const timeval & in) { + return (double)in.tv_sec + (double)in.tv_usec / 1000000.0; + } + + void fdSet::operator+=( int fd ) { + m_fds.insert( fd ); + } + void fdSet::operator-=( int fd ) { + m_fds.erase(fd); + } + bool fdSet::operator[] (int fd ) { + return m_fds.find( fd ) != m_fds.end(); + } + void fdSet::clear() { + m_fds.clear(); + } + + void fdSet::operator+=( fdSet const & other ) { + for(auto i = other.m_fds.begin(); i != other.m_fds.end(); ++ i ) { + (*this) += *i; + } + } + + int fdSelect::Select() { + return Select_( -1 ); + } + int fdSelect::Select( double timeOutSeconds ) { + int ms; + if (timeOutSeconds < 0) { + ms = -1; + } else if (timeOutSeconds == 0) { + ms = 0; + } else { + ms = pfc::rint32( timeOutSeconds * 1000 ); + if (ms < 1) ms = 1; + } + return Select_( ms ); + } + + int fdSelect::Select_( int timeOutMS ) { + fdSet total = Reads; + total += Writes; + total += Errors; + const size_t count = total.m_fds.size(); + pfc::array_t< pollfd > v; + v.set_size_discard( count ); + size_t walk = 0; + for( auto fd : total.m_fds) { + auto & f = v[walk++]; + f.fd = fd; + f.events = (Reads[fd] ? POLLIN : 0) | (Writes[fd] ? POLLOUT : 0); + // POLLERR ignored in events, only used in revents + f.revents = 0; + } + hires_timer timer; + int countdown = timeOutMS; + if (countdown > 0) timer.start(); + int status; + for ( ;; ) { + status = poll(v.get_ptr(), (int)count, countdown); + if (status >= 0) break; + + int e = errno; + if (e == EINTR) { + if (countdown < 0) continue; // infinite + if (countdown > 0) { + countdown = timeOutMS - rint32( timer.query() ); + if (countdown > 0) continue; + } + // should not really get here + status = 0; + break; + } else { + throw exception_nix(e); + } + + } + + Reads.clear(); Writes.clear(); Errors.clear(); + + if (status > 0) { + for(walk = 0; walk < count; ++walk) { + auto & f = v[walk]; + if (f.revents & POLLIN) Reads += f.fd; + if (f.revents & POLLOUT) Writes += f.fd; + if (f.revents & POLLERR) Errors += f.fd; + } + PFC_ASSERT( !Reads.m_fds.empty() || !Writes.m_fds.empty() || !Errors.m_fds.empty() ); + } + + return status; + } + + inline bool fdCanRead_select( int fdRead ) { + PFC_ASSERT( fdRead < FD_SETSIZE ); + timeval tv = {}; + fd_set set; + FD_ZERO(&set); + FD_SET(fdRead, &set); + + return select(fdRead + 1, &set, nullptr, nullptr, &tv) > 0; + } + inline bool fdCanRead_poll(int fdRead) { + pollfd arg = {fdRead, POLLIN }; + poll(&arg, 1, 0); + return (arg.revents & POLLIN) != 0; + } + + bool fdCanRead( int fdRead ) { + if ( fdRead < 0 ) { + PFC_ASSERT( !"???" ); + return false; + } + #ifdef __APPLE__ + // BROKEN extremely inefficient implementation of poll() on Apple systems, avoid if possible + if ( fdRead < FD_SETSIZE ) { + return fdCanRead_select( fdRead ); + } + #endif + return fdCanRead_poll(fdRead); + } + + bool fdCanWrite( int fd ) { + return fdWaitWrite( fd, 0 ); + } + + bool fdWaitRead( int fd, double timeOutSeconds ) { + if ( timeOutSeconds == 0 ) return fdCanRead( fd ); + fdSelect sel; sel.Reads += fd; + return sel.Select( timeOutSeconds ) > 0; + } + bool fdWaitWrite( int fd, double timeOutSeconds ) { + fdSelect sel; sel.Writes += fd; + return sel.Select( timeOutSeconds ) > 0; + } + + nix_event::nix_event(bool state) { + createPipe( m_fd ); + setNonBlocking( m_fd[0] ); + setNonBlocking( m_fd[1] ); + if ( state ) set_state(true); + } + nix_event::~nix_event() { + close( m_fd[0] ); + close( m_fd[1] ); + } + + void nix_event::set_state( bool state ) { + if (state) { + // Ensure that there is a byte in the pipe + if (!fdCanRead(m_fd[0] ) ) { + uint8_t dummy = 0; + write( m_fd[1], &dummy, 1); + } + } else { + // Keep reading until clear + for(;;) { + uint8_t dummy; + if (read(m_fd[0], &dummy, 1 ) != 1) break; + } + } + } + + bool nix_event::wait_for( double p_timeout_seconds ) { + return fdWaitRead( m_fd[0], p_timeout_seconds ); + } + bool nix_event::is_set() { + return fdCanRead(m_fd[0]); + } + bool nix_event::g_wait_for( int p_event, double p_timeout_seconds ) { + return fdWaitRead( p_event, p_timeout_seconds ); + } + size_t nix_event::g_multiWait( const pfc::eventHandle_t * events, size_t count, double timeout ) { + fdSelect sel; + for( size_t i = 0; i < count; ++ i ) { + sel.Reads += events[i]; + } + int state = sel.Select( timeout ); + if (state < 0) throw exception_nix(); + if (state == 0) return SIZE_MAX; + for( size_t i = 0; i < count; ++ i ) { + if ( sel.Reads[ events[i] ] ) return i; + } + crash(); // should not get here + return SIZE_MAX; + } + size_t nix_event::g_multiWait(std::initializer_list<eventHandle_t> const & arg, double timeout) { + return g_multiWait(arg.begin(), arg.size(), timeout); + } + int nix_event::g_twoEventWait( int h1, int h2, double timeout ) { + fdSelect sel; + sel.Reads += h1; + sel.Reads += h2; + int state = sel.Select( timeout ); + if (state < 0) throw exception_nix(); + if (state == 0) return 0; + if (sel.Reads[ h1 ] ) return 1; + if (sel.Reads[ h2 ] ) return 2; + crash(); // should not get here + return 0; + } + int nix_event::g_twoEventWait( nix_event & ev1, nix_event & ev2, double timeout ) { + return g_twoEventWait( ev1.get_handle(), ev2.get_handle(), timeout ); + } + + void nixSleep(double seconds) { + fdSelect sel; sel.Select( seconds ); + } + void sleepSeconds(double seconds) { + return nixSleep(seconds); + } + + void yield() { + return nixSleep(0.001); + } + + double nixGetTime() { + timeval tv = {}; + gettimeofday(&tv, NULL); + return importTimeval(tv); + } + tickcount_t getTickCount() { + return rint64(nixGetTime() * 1000.f); + } + + bool nixReadSymLink( string_base & strOut, const char * path ) { + size_t l = 1024; + for(;;) { + array_t<char> buffer; buffer.set_size( l + 1 ); + ssize_t rv = (size_t) readlink(path, buffer.get_ptr(), l); + if (rv < 0) return false; + if ((size_t)rv <= l) { + buffer.get_ptr()[rv] = 0; + strOut = buffer.get_ptr(); + return true; + } + l *= 2; + } + } + bool nixSelfProcessPath( string_base & strOut ) { +#ifdef __APPLE__ + uint32_t len = 0; + _NSGetExecutablePath(NULL, &len); + array_t<char> temp; temp.set_size( len + 1 ); temp.fill_null(); + _NSGetExecutablePath(temp.get_ptr(), &len); + strOut = temp.get_ptr(); + return true; +#else + return nixReadSymLink( strOut, PFC_string_formatter() << "/proc/" << (unsigned) getpid() << "/exe"); +#endif + } + + static int openDevRand() { + int ret = open("/dev/urandom", O_RDONLY); + if ( ret < 0 ) throw exception_nix(); + return ret; + } + void nixGetRandomData( void * outPtr, size_t outBytes ) { + try { + static fileHandle randomData = openDevRand(); + if (read(randomData.h, outPtr, outBytes) != outBytes) throw exception_nix(); + } catch (std::exception const & e) { + throw std::runtime_error("getRandomData failure"); + } + } + +#ifndef __APPLE__ // for Apple they are implemented in Obj-C + bool isShiftKeyPressed() {return false;} + bool isCtrlKeyPressed() {return false;} + bool isAltKeyPressed() {return false;} +#endif +} + +void uSleepSeconds( double seconds, bool ) { + pfc::nixSleep( seconds ); +} +#endif // _WIN32 +
