Mercurial > foo_out_sdl
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 0:e9bb126753e7 | 1:20d02a178406 |
|---|---|
| 1 #include "pfc-lite.h" | |
| 2 | |
| 3 | |
| 4 #ifndef _WIN32 | |
| 5 #include <stdio.h> | |
| 6 #include <unistd.h> | |
| 7 #include <fcntl.h> | |
| 8 #include <errno.h> | |
| 9 #include <poll.h> | |
| 10 #include <math.h> | |
| 11 | |
| 12 #ifdef __APPLE__ | |
| 13 #include <mach-o/dyld.h> | |
| 14 #endif | |
| 15 | |
| 16 #include "nix-objects.h" | |
| 17 #include "string_base.h" | |
| 18 #include "array.h" | |
| 19 #include "debug.h" | |
| 20 #include "timers.h" | |
| 21 #include "filehandle.h" | |
| 22 | |
| 23 namespace pfc { | |
| 24 void nixFormatError( string_base & str, int code ) { | |
| 25 char buffer[512] = {}; | |
| 26 strerror_r(code, buffer, sizeof(buffer)); | |
| 27 str = buffer; | |
| 28 } | |
| 29 | |
| 30 void setNonBlocking( int fd, bool bNonBlocking ) { | |
| 31 int flags = fcntl(fd, F_GETFL, 0); | |
| 32 int flags2 = flags; | |
| 33 if (bNonBlocking) flags2 |= O_NONBLOCK; | |
| 34 else flags2 &= ~O_NONBLOCK; | |
| 35 if (flags2 != flags) fcntl(fd, F_SETFL, flags2); | |
| 36 } | |
| 37 | |
| 38 void setCloseOnExec( int fd, bool bCloseOnExec ) { | |
| 39 int flags = fcntl(fd, F_GETFD); | |
| 40 int flags2 = flags; | |
| 41 if (bCloseOnExec) flags2 |= FD_CLOEXEC; | |
| 42 else flags2 &= ~FD_CLOEXEC; | |
| 43 if (flags != flags2) fcntl(fd, F_SETFD, flags2); | |
| 44 } | |
| 45 | |
| 46 void setInheritable( int fd, bool bInheritable ) { | |
| 47 setCloseOnExec( fd, !bInheritable ); | |
| 48 } | |
| 49 | |
| 50 void createPipe( int fd[2], bool bInheritable ) { | |
| 51 #if defined(__linux__) && defined(O_CLOEXEC) | |
| 52 if (pipe2(fd, bInheritable ? 0 : O_CLOEXEC) < 0) throw exception_nix(); | |
| 53 #else | |
| 54 if (pipe(fd) < 0) throw exception_nix(); | |
| 55 if (!bInheritable) { | |
| 56 setInheritable( fd[0], false ); | |
| 57 setInheritable( fd[1], false ); | |
| 58 } | |
| 59 #endif | |
| 60 } | |
| 61 | |
| 62 exception_nix::exception_nix() { | |
| 63 _init(errno); | |
| 64 } | |
| 65 exception_nix::exception_nix(int code) { | |
| 66 _init(code); | |
| 67 } | |
| 68 void exception_nix::_init(int code) { | |
| 69 PFC_ASSERT( code != EINTR ); | |
| 70 m_code = code; | |
| 71 nixFormatError(m_msg, code); | |
| 72 } | |
| 73 | |
| 74 timeval makeTimeVal( double timeSeconds ) { | |
| 75 timeval tv = {}; | |
| 76 uint64_t temp = (uint64_t) floor( timeSeconds * 1000000.0 + 0.5); | |
| 77 tv.tv_usec = (uint32_t) (temp % 1000000); | |
| 78 tv.tv_sec = (uint32_t) (temp / 1000000); | |
| 79 return tv; | |
| 80 } | |
| 81 double importTimeval(const timeval & in) { | |
| 82 return (double)in.tv_sec + (double)in.tv_usec / 1000000.0; | |
| 83 } | |
| 84 | |
| 85 void fdSet::operator+=( int fd ) { | |
| 86 m_fds.insert( fd ); | |
| 87 } | |
| 88 void fdSet::operator-=( int fd ) { | |
| 89 m_fds.erase(fd); | |
| 90 } | |
| 91 bool fdSet::operator[] (int fd ) { | |
| 92 return m_fds.find( fd ) != m_fds.end(); | |
| 93 } | |
| 94 void fdSet::clear() { | |
| 95 m_fds.clear(); | |
| 96 } | |
| 97 | |
| 98 void fdSet::operator+=( fdSet const & other ) { | |
| 99 for(auto i = other.m_fds.begin(); i != other.m_fds.end(); ++ i ) { | |
| 100 (*this) += *i; | |
| 101 } | |
| 102 } | |
| 103 | |
| 104 int fdSelect::Select() { | |
| 105 return Select_( -1 ); | |
| 106 } | |
| 107 int fdSelect::Select( double timeOutSeconds ) { | |
| 108 int ms; | |
| 109 if (timeOutSeconds < 0) { | |
| 110 ms = -1; | |
| 111 } else if (timeOutSeconds == 0) { | |
| 112 ms = 0; | |
| 113 } else { | |
| 114 ms = pfc::rint32( timeOutSeconds * 1000 ); | |
| 115 if (ms < 1) ms = 1; | |
| 116 } | |
| 117 return Select_( ms ); | |
| 118 } | |
| 119 | |
| 120 int fdSelect::Select_( int timeOutMS ) { | |
| 121 fdSet total = Reads; | |
| 122 total += Writes; | |
| 123 total += Errors; | |
| 124 const size_t count = total.m_fds.size(); | |
| 125 pfc::array_t< pollfd > v; | |
| 126 v.set_size_discard( count ); | |
| 127 size_t walk = 0; | |
| 128 for( auto fd : total.m_fds) { | |
| 129 auto & f = v[walk++]; | |
| 130 f.fd = fd; | |
| 131 f.events = (Reads[fd] ? POLLIN : 0) | (Writes[fd] ? POLLOUT : 0); | |
| 132 // POLLERR ignored in events, only used in revents | |
| 133 f.revents = 0; | |
| 134 } | |
| 135 hires_timer timer; | |
| 136 int countdown = timeOutMS; | |
| 137 if (countdown > 0) timer.start(); | |
| 138 int status; | |
| 139 for ( ;; ) { | |
| 140 status = poll(v.get_ptr(), (int)count, countdown); | |
| 141 if (status >= 0) break; | |
| 142 | |
| 143 int e = errno; | |
| 144 if (e == EINTR) { | |
| 145 if (countdown < 0) continue; // infinite | |
| 146 if (countdown > 0) { | |
| 147 countdown = timeOutMS - rint32( timer.query() ); | |
| 148 if (countdown > 0) continue; | |
| 149 } | |
| 150 // should not really get here | |
| 151 status = 0; | |
| 152 break; | |
| 153 } else { | |
| 154 throw exception_nix(e); | |
| 155 } | |
| 156 | |
| 157 } | |
| 158 | |
| 159 Reads.clear(); Writes.clear(); Errors.clear(); | |
| 160 | |
| 161 if (status > 0) { | |
| 162 for(walk = 0; walk < count; ++walk) { | |
| 163 auto & f = v[walk]; | |
| 164 if (f.revents & POLLIN) Reads += f.fd; | |
| 165 if (f.revents & POLLOUT) Writes += f.fd; | |
| 166 if (f.revents & POLLERR) Errors += f.fd; | |
| 167 } | |
| 168 PFC_ASSERT( !Reads.m_fds.empty() || !Writes.m_fds.empty() || !Errors.m_fds.empty() ); | |
| 169 } | |
| 170 | |
| 171 return status; | |
| 172 } | |
| 173 | |
| 174 inline bool fdCanRead_select( int fdRead ) { | |
| 175 PFC_ASSERT( fdRead < FD_SETSIZE ); | |
| 176 timeval tv = {}; | |
| 177 fd_set set; | |
| 178 FD_ZERO(&set); | |
| 179 FD_SET(fdRead, &set); | |
| 180 | |
| 181 return select(fdRead + 1, &set, nullptr, nullptr, &tv) > 0; | |
| 182 } | |
| 183 inline bool fdCanRead_poll(int fdRead) { | |
| 184 pollfd arg = {fdRead, POLLIN }; | |
| 185 poll(&arg, 1, 0); | |
| 186 return (arg.revents & POLLIN) != 0; | |
| 187 } | |
| 188 | |
| 189 bool fdCanRead( int fdRead ) { | |
| 190 if ( fdRead < 0 ) { | |
| 191 PFC_ASSERT( !"???" ); | |
| 192 return false; | |
| 193 } | |
| 194 #ifdef __APPLE__ | |
| 195 // BROKEN extremely inefficient implementation of poll() on Apple systems, avoid if possible | |
| 196 if ( fdRead < FD_SETSIZE ) { | |
| 197 return fdCanRead_select( fdRead ); | |
| 198 } | |
| 199 #endif | |
| 200 return fdCanRead_poll(fdRead); | |
| 201 } | |
| 202 | |
| 203 bool fdCanWrite( int fd ) { | |
| 204 return fdWaitWrite( fd, 0 ); | |
| 205 } | |
| 206 | |
| 207 bool fdWaitRead( int fd, double timeOutSeconds ) { | |
| 208 if ( timeOutSeconds == 0 ) return fdCanRead( fd ); | |
| 209 fdSelect sel; sel.Reads += fd; | |
| 210 return sel.Select( timeOutSeconds ) > 0; | |
| 211 } | |
| 212 bool fdWaitWrite( int fd, double timeOutSeconds ) { | |
| 213 fdSelect sel; sel.Writes += fd; | |
| 214 return sel.Select( timeOutSeconds ) > 0; | |
| 215 } | |
| 216 | |
| 217 nix_event::nix_event(bool state) { | |
| 218 createPipe( m_fd ); | |
| 219 setNonBlocking( m_fd[0] ); | |
| 220 setNonBlocking( m_fd[1] ); | |
| 221 if ( state ) set_state(true); | |
| 222 } | |
| 223 nix_event::~nix_event() { | |
| 224 close( m_fd[0] ); | |
| 225 close( m_fd[1] ); | |
| 226 } | |
| 227 | |
| 228 void nix_event::set_state( bool state ) { | |
| 229 if (state) { | |
| 230 // Ensure that there is a byte in the pipe | |
| 231 if (!fdCanRead(m_fd[0] ) ) { | |
| 232 uint8_t dummy = 0; | |
| 233 write( m_fd[1], &dummy, 1); | |
| 234 } | |
| 235 } else { | |
| 236 // Keep reading until clear | |
| 237 for(;;) { | |
| 238 uint8_t dummy; | |
| 239 if (read(m_fd[0], &dummy, 1 ) != 1) break; | |
| 240 } | |
| 241 } | |
| 242 } | |
| 243 | |
| 244 bool nix_event::wait_for( double p_timeout_seconds ) { | |
| 245 return fdWaitRead( m_fd[0], p_timeout_seconds ); | |
| 246 } | |
| 247 bool nix_event::is_set() { | |
| 248 return fdCanRead(m_fd[0]); | |
| 249 } | |
| 250 bool nix_event::g_wait_for( int p_event, double p_timeout_seconds ) { | |
| 251 return fdWaitRead( p_event, p_timeout_seconds ); | |
| 252 } | |
| 253 size_t nix_event::g_multiWait( const pfc::eventHandle_t * events, size_t count, double timeout ) { | |
| 254 fdSelect sel; | |
| 255 for( size_t i = 0; i < count; ++ i ) { | |
| 256 sel.Reads += events[i]; | |
| 257 } | |
| 258 int state = sel.Select( timeout ); | |
| 259 if (state < 0) throw exception_nix(); | |
| 260 if (state == 0) return SIZE_MAX; | |
| 261 for( size_t i = 0; i < count; ++ i ) { | |
| 262 if ( sel.Reads[ events[i] ] ) return i; | |
| 263 } | |
| 264 crash(); // should not get here | |
| 265 return SIZE_MAX; | |
| 266 } | |
| 267 size_t nix_event::g_multiWait(std::initializer_list<eventHandle_t> const & arg, double timeout) { | |
| 268 return g_multiWait(arg.begin(), arg.size(), timeout); | |
| 269 } | |
| 270 int nix_event::g_twoEventWait( int h1, int h2, double timeout ) { | |
| 271 fdSelect sel; | |
| 272 sel.Reads += h1; | |
| 273 sel.Reads += h2; | |
| 274 int state = sel.Select( timeout ); | |
| 275 if (state < 0) throw exception_nix(); | |
| 276 if (state == 0) return 0; | |
| 277 if (sel.Reads[ h1 ] ) return 1; | |
| 278 if (sel.Reads[ h2 ] ) return 2; | |
| 279 crash(); // should not get here | |
| 280 return 0; | |
| 281 } | |
| 282 int nix_event::g_twoEventWait( nix_event & ev1, nix_event & ev2, double timeout ) { | |
| 283 return g_twoEventWait( ev1.get_handle(), ev2.get_handle(), timeout ); | |
| 284 } | |
| 285 | |
| 286 void nixSleep(double seconds) { | |
| 287 fdSelect sel; sel.Select( seconds ); | |
| 288 } | |
| 289 void sleepSeconds(double seconds) { | |
| 290 return nixSleep(seconds); | |
| 291 } | |
| 292 | |
| 293 void yield() { | |
| 294 return nixSleep(0.001); | |
| 295 } | |
| 296 | |
| 297 double nixGetTime() { | |
| 298 timeval tv = {}; | |
| 299 gettimeofday(&tv, NULL); | |
| 300 return importTimeval(tv); | |
| 301 } | |
| 302 tickcount_t getTickCount() { | |
| 303 return rint64(nixGetTime() * 1000.f); | |
| 304 } | |
| 305 | |
| 306 bool nixReadSymLink( string_base & strOut, const char * path ) { | |
| 307 size_t l = 1024; | |
| 308 for(;;) { | |
| 309 array_t<char> buffer; buffer.set_size( l + 1 ); | |
| 310 ssize_t rv = (size_t) readlink(path, buffer.get_ptr(), l); | |
| 311 if (rv < 0) return false; | |
| 312 if ((size_t)rv <= l) { | |
| 313 buffer.get_ptr()[rv] = 0; | |
| 314 strOut = buffer.get_ptr(); | |
| 315 return true; | |
| 316 } | |
| 317 l *= 2; | |
| 318 } | |
| 319 } | |
| 320 bool nixSelfProcessPath( string_base & strOut ) { | |
| 321 #ifdef __APPLE__ | |
| 322 uint32_t len = 0; | |
| 323 _NSGetExecutablePath(NULL, &len); | |
| 324 array_t<char> temp; temp.set_size( len + 1 ); temp.fill_null(); | |
| 325 _NSGetExecutablePath(temp.get_ptr(), &len); | |
| 326 strOut = temp.get_ptr(); | |
| 327 return true; | |
| 328 #else | |
| 329 return nixReadSymLink( strOut, PFC_string_formatter() << "/proc/" << (unsigned) getpid() << "/exe"); | |
| 330 #endif | |
| 331 } | |
| 332 | |
| 333 static int openDevRand() { | |
| 334 int ret = open("/dev/urandom", O_RDONLY); | |
| 335 if ( ret < 0 ) throw exception_nix(); | |
| 336 return ret; | |
| 337 } | |
| 338 void nixGetRandomData( void * outPtr, size_t outBytes ) { | |
| 339 try { | |
| 340 static fileHandle randomData = openDevRand(); | |
| 341 if (read(randomData.h, outPtr, outBytes) != outBytes) throw exception_nix(); | |
| 342 } catch (std::exception const & e) { | |
| 343 throw std::runtime_error("getRandomData failure"); | |
| 344 } | |
| 345 } | |
| 346 | |
| 347 #ifndef __APPLE__ // for Apple they are implemented in Obj-C | |
| 348 bool isShiftKeyPressed() {return false;} | |
| 349 bool isCtrlKeyPressed() {return false;} | |
| 350 bool isAltKeyPressed() {return false;} | |
| 351 #endif | |
| 352 } | |
| 353 | |
| 354 void uSleepSeconds( double seconds, bool ) { | |
| 355 pfc::nixSleep( seconds ); | |
| 356 } | |
| 357 #endif // _WIN32 | |
| 358 |
