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
+