- Timestamp:
- Apr 28, 2021, 4:56:50 PM (5 years ago)
- Branches:
- ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, master, new-ast-unique-expr, pthread-emulation, qualifiedEnum
- Children:
- 8d66610
- Parents:
- feacef9 (diff), b7fd2db6 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)links above to see all the changes relative to each parent. - Location:
- libcfa
- Files:
-
- 6 added
- 3 deleted
- 54 edited
-
configure.ac (modified) (2 diffs)
-
prelude/builtins.c (modified) (5 diffs)
-
prelude/defines.hfa.in (modified) (1 diff)
-
src/Makefile.am (modified) (3 diffs)
-
src/bits/debug.hfa (modified) (1 diff)
-
src/bits/defs.hfa (modified) (1 diff)
-
src/bits/locks.hfa (modified) (3 diffs)
-
src/bits/queue.hfa (modified) (2 diffs)
-
src/bits/weakso_locks.cfa (modified) (1 diff)
-
src/bits/weakso_locks.hfa (modified) (2 diffs)
-
src/clock.hfa (modified) (2 diffs)
-
src/concurrency/alarm.cfa (modified) (4 diffs)
-
src/concurrency/clib/cfathread.cfa (modified) (3 diffs)
-
src/concurrency/clib/cfathread.h (modified) (2 diffs)
-
src/concurrency/coroutine.cfa (modified) (5 diffs)
-
src/concurrency/coroutine.hfa (modified) (4 diffs)
-
src/concurrency/future.hfa (modified) (2 diffs)
-
src/concurrency/invoke.c (modified) (2 diffs)
-
src/concurrency/invoke.h (modified) (3 diffs)
-
src/concurrency/io.cfa (modified) (6 diffs)
-
src/concurrency/io/call.cfa.in (modified) (26 diffs)
-
src/concurrency/io/setup.cfa (modified) (12 diffs)
-
src/concurrency/io/types.hfa (modified) (7 diffs)
-
src/concurrency/iofwd.hfa (modified) (4 diffs)
-
src/concurrency/kernel.cfa (modified) (33 diffs)
-
src/concurrency/kernel.hfa (modified) (12 diffs)
-
src/concurrency/kernel/fwd.hfa (modified) (6 diffs)
-
src/concurrency/kernel/startup.cfa (modified) (19 diffs)
-
src/concurrency/kernel_private.hfa (modified) (4 diffs)
-
src/concurrency/locks.cfa (modified) (9 diffs)
-
src/concurrency/locks.hfa (modified) (5 diffs)
-
src/concurrency/monitor.hfa (modified) (1 diff)
-
src/concurrency/preemption.cfa (modified) (12 diffs)
-
src/concurrency/ready_queue.cfa (modified) (19 diffs)
-
src/concurrency/ready_subqueue.hfa (modified) (5 diffs)
-
src/concurrency/stats.cfa (modified) (3 diffs)
-
src/concurrency/stats.hfa (modified) (4 diffs)
-
src/concurrency/thread.cfa (modified) (7 diffs)
-
src/concurrency/thread.hfa (modified) (4 diffs)
-
src/containers/array.hfa (added)
-
src/containers/list.hfa (modified) (9 diffs)
-
src/containers/queueLockFree.hfa (added)
-
src/exception.c (modified) (3 diffs)
-
src/exception.h (modified) (4 diffs)
-
src/exception.hfa (modified) (3 diffs)
-
src/fstream.cfa (modified) (19 diffs)
-
src/fstream.hfa (modified) (7 diffs)
-
src/gmp.hfa (modified) (2 diffs)
-
src/heap.cfa (modified) (8 diffs)
-
src/iostream.cfa (modified) (65 diffs)
-
src/iostream.hfa (modified) (20 diffs)
-
src/math.hfa (modified) (2 diffs)
-
src/startup.cfa (modified) (1 diff)
-
src/stdhdr/bfdlink.h (deleted)
-
src/stdhdr/hwloc.h (deleted)
-
src/stdhdr/krb5.h (deleted)
-
src/stdhdr/libltdl/lt_dlloader.h (added)
-
src/stdhdr/sys/socket.h (added)
-
src/stdlib.hfa (modified) (7 diffs)
-
src/strstream.cfa (added)
-
src/strstream.hfa (added)
-
src/time.hfa (modified) (5 diffs)
-
src/virtual.c (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
libcfa/configure.ac
rfeacef9 r5407cdc 169 169 AH_TEMPLATE([CFA_HAVE_IOSQE_FIXED_FILE],[Defined if io_uring support is present when compiling libcfathread and supports the flag FIXED_FILE.]) 170 170 AH_TEMPLATE([CFA_HAVE_IOSQE_IO_DRAIN],[Defined if io_uring support is present when compiling libcfathread and supports the flag IO_DRAIN.]) 171 AH_TEMPLATE([CFA_HAVE_IOSQE_ASYNC],[Defined if io_uring support is present when compiling libcfathread and supports the flag ASYNC.])172 171 AH_TEMPLATE([CFA_HAVE_IOSQE_IO_LINK],[Defined if io_uring support is present when compiling libcfathread and supports the flag IO_LINK.]) 173 172 AH_TEMPLATE([CFA_HAVE_IOSQE_IO_HARDLINK],[Defined if io_uring support is present when compiling libcfathread and supports the flag IO_HARDLINK.]) 173 AH_TEMPLATE([CFA_HAVE_IOSQE_ASYNC],[Defined if io_uring support is present when compiling libcfathread and supports the flag ASYNC.]) 174 AH_TEMPLATE([CFA_HAVE_IOSQE_BUFFER_SELECT],[Defined if io_uring support is present when compiling libcfathread and supports the flag BUFFER_SELEC.]) 174 175 AH_TEMPLATE([CFA_HAVE_SPLICE_F_FD_IN_FIXED],[Defined if io_uring support is present when compiling libcfathread and supports the flag SPLICE_F_FD_IN_FIXED.]) 175 176 AH_TEMPLATE([CFA_HAVE_IORING_SETUP_ATTACH_WQ],[Defined if io_uring support is present when compiling libcfathread and supports the flag IORING_SETUP_ATTACH_WQ.]) … … 182 183 183 184 define(ioring_ops, [IORING_OP_NOP,IORING_OP_READV,IORING_OP_WRITEV,IORING_OP_FSYNC,IORING_OP_READ_FIXED,IORING_OP_WRITE_FIXED,IORING_OP_POLL_ADD,IORING_OP_POLL_REMOVE,IORING_OP_SYNC_FILE_RANGE,IORING_OP_SENDMSG,IORING_OP_RECVMSG,IORING_OP_TIMEOUT,IORING_OP_TIMEOUT_REMOVE,IORING_OP_ACCEPT,IORING_OP_ASYNC_CANCEL,IORING_OP_LINK_TIMEOUT,IORING_OP_CONNECT,IORING_OP_FALLOCATE,IORING_OP_OPENAT,IORING_OP_CLOSE,IORING_OP_FILES_UPDATE,IORING_OP_STATX,IORING_OP_READ,IORING_OP_WRITE,IORING_OP_FADVISE,IORING_OP_MADVISE,IORING_OP_SEND,IORING_OP_RECV,IORING_OP_OPENAT2,IORING_OP_EPOLL_CTL,IORING_OP_SPLICE,IORING_OP_PROVIDE_BUFFERS,IORING_OP_REMOVE_BUFFER,IORING_OP_TEE]) 184 define(ioring_flags, [IOSQE_FIXED_FILE,IOSQE_IO_DRAIN,IOSQE_ ASYNC,IOSQE_IO_LINK,IOSQE_IO_HARDLINK,SPLICE_F_FD_IN_FIXED,IORING_SETUP_ATTACH_WQ])185 define(ioring_flags, [IOSQE_FIXED_FILE,IOSQE_IO_DRAIN,IOSQE_IO_LINK,IOSQE_IO_HARDLINK,IOSQE_ASYNC,IOSQE_BUFFER_SELECT,SPLICE_F_FD_IN_FIXED,IORING_SETUP_ATTACH_WQ]) 185 186 186 187 define(ioring_from_decls, [ -
libcfa/prelude/builtins.c
rfeacef9 r5407cdc 9 9 // Author : Peter A. Buhr 10 10 // Created On : Fri Jul 21 16:21:03 2017 11 // Last Modified By : Andrew Beach12 // Last Modified On : Tue Oct 27 14:42:00 202013 // Update Count : 11 111 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Apr 13 17:26:32 2021 13 // Update Count : 117 14 14 // 15 15 … … 125 125 } // distribution 126 126 127 #define __CFA_BASE_COMP_1__() if ( ep== 1 ) return 1128 #define __CFA_BASE_COMP_2__() if ( ep == 2 ) return ep<< (y - 1)127 #define __CFA_BASE_COMP_1__() if ( x == 1 ) return 1 128 #define __CFA_BASE_COMP_2__() if ( x == 2 ) return x << (y - 1) 129 129 #define __CFA_EXP_OVERFLOW__() if ( y >= sizeof(y) * CHAR_BIT ) return 0 130 130 … … 134 134 __CFA_BASE_COMP_2__(); /* special case, positive shifting for integral types */ \ 135 135 __CFA_EXP_OVERFLOW__(); /* immediate overflow, negative exponent > 2^size-1 */ \ 136 typeof( ep) op = 1; /* accumulate odd product */ \136 typeof(x) op = 1; /* accumulate odd product */ \ 137 137 for ( ; y > 1; y >>= 1 ) { /* squaring exponentiation, O(log2 y) */ \ 138 if ( (y & 1) == 1 ) op = op * ep; /* odd ? */ \139 ep = ep * ep; \138 if ( (y & 1) == 1 ) op = op * x; /* odd ? */ \ 139 x = x * x; \ 140 140 } \ 141 return ep* op141 return x * op 142 142 143 143 static inline { 144 long int ?\?( int ep, unsigned int y ) { __CFA_EXP__(); } 145 long int ?\?( long int ep, unsigned long int y ) { __CFA_EXP__(); } 144 long int ?\?( int x, unsigned int y ) { __CFA_EXP__(); } 145 long int ?\?( long int x, unsigned long int y ) { __CFA_EXP__(); } 146 long long int ?\?( long long int x, unsigned long long int y ) { __CFA_EXP__(); } 146 147 // unsigned computation may be faster and larger 147 unsigned long int ?\?( unsigned int ep, unsigned int y ) { __CFA_EXP__(); } 148 unsigned long int ?\?( unsigned long int ep, unsigned long int y ) { __CFA_EXP__(); } 148 unsigned long int ?\?( unsigned int x, unsigned int y ) { __CFA_EXP__(); } 149 unsigned long int ?\?( unsigned long int x, unsigned long int y ) { __CFA_EXP__(); } 150 unsigned long long int ?\?( unsigned long long int x, unsigned long long int y ) { __CFA_EXP__(); } 149 151 } // distribution 150 152 … … 157 159 158 160 static inline forall( OT | { void ?{}( OT & this, one_t ); OT ?*?( OT, OT ); } ) { 159 OT ?\?( OT ep, unsigned int y ) { __CFA_EXP__(); } 160 OT ?\?( OT ep, unsigned long int y ) { __CFA_EXP__(); } 161 OT ?\?( OT x, unsigned int y ) { __CFA_EXP__(); } 162 OT ?\?( OT x, unsigned long int y ) { __CFA_EXP__(); } 163 OT ?\?( OT x, unsigned long long int y ) { __CFA_EXP__(); } 161 164 } // distribution 162 165 … … 166 169 167 170 static inline { 171 long int ?\=?( int & x, unsigned int y ) { x = x \ y; return x; } 168 172 long int ?\=?( long int & x, unsigned long int y ) { x = x \ y; return x; } 173 long long int ?\=?( long long int & x, unsigned long long int y ) { x = x \ y; return x; } 174 unsigned long int ?\=?( unsigned int & x, unsigned int y ) { x = x \ y; return x; } 169 175 unsigned long int ?\=?( unsigned long int & x, unsigned long int y ) { x = x \ y; return x; } 170 int ?\=?( int & x, unsigned long int y ) { x = x \ y; return x; } 171 unsigned int ?\=?( unsigned int & x, unsigned long int y ) { x = x \ y; return x; } 176 unsigned long long int ?\=?( unsigned long long int & x, unsigned long long int y ) { x = x \ y; return x; } 172 177 } // distribution 173 178 -
libcfa/prelude/defines.hfa.in
rfeacef9 r5407cdc 149 149 150 150 /* Defined if io_uring support is present when compiling libcfathread and 151 supports the flag BUFFER_SELEC. */ 152 #undef CFA_HAVE_IOSQE_BUFFER_SELECT 153 154 /* Defined if io_uring support is present when compiling libcfathread and 151 155 supports the flag FIXED_FILE. */ 152 156 #undef CFA_HAVE_IOSQE_FIXED_FILE -
libcfa/src/Makefile.am
rfeacef9 r5407cdc 11 11 ## Created On : Sun May 31 08:54:01 2015 12 12 ## Last Modified By : Peter A. Buhr 13 ## Last Modified On : Wed Dec 9 22:46:14 202014 ## Update Count : 25 013 ## Last Modified On : Sat Apr 24 09:09:56 2021 14 ## Update Count : 254 15 15 ############################################################################### 16 16 … … 56 56 bits/queue.hfa \ 57 57 bits/sequence.hfa \ 58 containers/array.hfa \ 58 59 concurrency/iofwd.hfa \ 59 60 containers/list.hfa \ 61 containers/queueLockFree.hfa \ 60 62 containers/stackLockFree.hfa \ 61 63 vec/vec.hfa \ … … 67 69 common.hfa \ 68 70 fstream.hfa \ 71 strstream.hfa \ 69 72 heap.hfa \ 70 73 iostream.hfa \ -
libcfa/src/bits/debug.hfa
rfeacef9 r5407cdc 101 101 __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_print_buffer(STDERR_FILENO, __VA_ARGS__)) 102 102 #define __cfadbg_print_buffer_decl(group, ...) \ 103 __CFADBG_PRINT_GROUP_##group(char __dbg_text[256]; int __dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_bits_write( __dbg_text, __dbg_len ))103 __CFADBG_PRINT_GROUP_##group(char __dbg_text[256]; int __dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_bits_write( STDERR_FILENO, __dbg_text, __dbg_len )) 104 104 #define __cfadbg_print_buffer_local(group, ...) \ 105 105 __CFADBG_PRINT_GROUP_##group(__dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_bits_write(STDERR_FILENO, __dbg_text, __dbg_len)) -
libcfa/src/bits/defs.hfa
rfeacef9 r5407cdc 74 74 #error unsupported architecture 75 75 #endif 76 77 #define CFA_IO_LAZY (1_l64u << 32_l64u) -
libcfa/src/bits/locks.hfa
rfeacef9 r5407cdc 37 37 extern "C" { 38 38 extern void disable_interrupts() OPTIONAL_THREAD; 39 extern void enable_interrupts _noPoll() OPTIONAL_THREAD;39 extern void enable_interrupts( bool poll = true ) OPTIONAL_THREAD; 40 40 41 41 #ifdef __CFA_DEBUG__ … … 57 57 __cfaabi_dbg_record_lock( this, caller ); 58 58 } else { 59 enable_interrupts _noPoll();59 enable_interrupts( false ); 60 60 } 61 61 return result; … … 90 90 static inline void unlock( __spinlock_t & this ) { 91 91 __atomic_clear( &this.lock, __ATOMIC_RELEASE ); 92 enable_interrupts _noPoll();92 enable_interrupts( false ); 93 93 } 94 94 #endif -
libcfa/src/bits/queue.hfa
rfeacef9 r5407cdc 15 15 }; 16 16 17 inline {17 static inline { 18 18 // wrappers to make Collection have T 19 19 T & head( Queue(T) & q ) with( q ) { … … 154 154 struct QueueIter { 155 155 inline ColIter; // Plan 9 inheritance 156 }; 156 }; 157 157 158 inline {158 static inline { 159 159 void ?{}( QueueIter(T) & qi ) with( qi ) { 160 160 ((ColIter &)qi){}; -
libcfa/src/bits/weakso_locks.cfa
rfeacef9 r5407cdc 18 18 #include "bits/weakso_locks.hfa" 19 19 20 void ?{}( blocking_lock & this, bool multi_acquisition, bool strict_owner) {}21 void ^?{}( blocking_lock & this) {}20 void ?{}( blocking_lock &, bool, bool ) {} 21 void ^?{}( blocking_lock & ) {} 22 22 23 void lock( blocking_lock & this ) {} 24 bool try_lock( blocking_lock & this ) { return false; } 25 void unlock( blocking_lock & this ) {} 26 void on_notify( blocking_lock & this, struct $thread * t ) {} 27 void on_wait( blocking_lock & this ) {} 28 size_t wait_count( blocking_lock & this ) { return 0; } 29 void set_recursion_count( blocking_lock & this, size_t recursion ) {} 30 size_t get_recursion_count( blocking_lock & this ) { return 0; } 23 void lock( blocking_lock & ) {} 24 bool try_lock( blocking_lock & ) { return false; } 25 void unlock( blocking_lock & ) {} 26 void on_notify( blocking_lock &, struct $thread * ) {} 27 size_t on_wait( blocking_lock & ) { return 0; } 28 void on_wakeup( blocking_lock &, size_t ) {} 29 size_t wait_count( blocking_lock & ) { return 0; } -
libcfa/src/bits/weakso_locks.hfa
rfeacef9 r5407cdc 56 56 void unlock( blocking_lock & this ) OPTIONAL_THREAD; 57 57 void on_notify( blocking_lock & this, struct $thread * t ) OPTIONAL_THREAD; 58 void on_wait( blocking_lock & this ) OPTIONAL_THREAD; 58 size_t on_wait( blocking_lock & this ) OPTIONAL_THREAD; 59 void on_wakeup( blocking_lock & this, size_t ) OPTIONAL_THREAD; 59 60 size_t wait_count( blocking_lock & this ) OPTIONAL_THREAD; 60 void set_recursion_count( blocking_lock & this, size_t recursion ) OPTIONAL_THREAD;61 size_t get_recursion_count( blocking_lock & this ) OPTIONAL_THREAD;62 61 63 62 //---------- … … 69 68 static inline void ?{}( multiple_acquisition_lock & this ) {((blocking_lock &)this){ true, false };} 70 69 static inline void ^?{}( multiple_acquisition_lock & this ) {} 71 static inline void lock ( multiple_acquisition_lock & this ) { lock ( (blocking_lock &)this ); } 72 static inline void unlock ( multiple_acquisition_lock & this ) { unlock ( (blocking_lock &)this ); } 73 static inline void on_wait ( multiple_acquisition_lock & this ) { on_wait( (blocking_lock &)this ); } 70 static inline void lock ( multiple_acquisition_lock & this ) { lock ( (blocking_lock &)this ); } 71 static inline bool try_lock ( multiple_acquisition_lock & this ) { return try_lock( (blocking_lock &)this ); } 72 static inline void unlock ( multiple_acquisition_lock & this ) { unlock ( (blocking_lock &)this ); } 73 static inline size_t on_wait ( multiple_acquisition_lock & this ) { return on_wait ( (blocking_lock &)this ); } 74 static inline void on_wakeup( multiple_acquisition_lock & this, size_t v ) { on_wakeup ( (blocking_lock &)this, v ); } 74 75 static inline void on_notify( multiple_acquisition_lock & this, struct $thread * t ){ on_notify( (blocking_lock &)this, t ); } 75 static inline void set_recursion_count( multiple_acquisition_lock & this, size_t recursion ){ set_recursion_count( (blocking_lock &)this, recursion ); }76 static inline size_t get_recursion_count( multiple_acquisition_lock & this ){ return get_recursion_count( (blocking_lock &)this ); } -
libcfa/src/clock.hfa
rfeacef9 r5407cdc 10 10 // Created On : Thu Apr 12 14:36:06 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Jan 6 12:49:58 202013 // Update Count : 912 // Last Modified On : Sun Apr 18 08:12:16 2021 13 // Update Count : 28 14 14 // 15 15 … … 27 27 //######################### Clock ######################### 28 28 29 struct Clock { // private 30 Duration offset; // for virtual clock: contains offset from real-time 29 struct Clock { // virtual clock 30 // private 31 Duration offset; // offset from computer real-time 31 32 }; 32 33 33 34 static inline { 34 void reset Clock( Clock & clk, Duration adj ) with( clk ) {35 void reset( Clock & clk, Duration adj ) with( clk ) { // change offset 35 36 offset = adj + __timezone`s; // timezone (global) is (UTC - local time) in seconds 36 } // reset Clock37 } // reset 37 38 38 void ?{}( Clock & clk, Duration adj ) { resetClock( clk, adj ); } 39 void ?{}( Clock & clk ) { reset( clk, (Duration){ 0 } ); } // create no offset 40 void ?{}( Clock & clk, Duration adj ) { reset( clk, adj ); } // create with offset 39 41 40 Duration getResNsec() { 42 // System-wide clock that measures real, i.e., wall-clock) time. This clock is affected by discontinuous jumps in 43 // the system time. For example, manual changes of the clock, and incremental adjustments performed by adjtime(3) 44 // and NTP (daylight saving (Fall back). 45 Duration resolutionHi() { // clock resolution in nanoseconds (fine) 41 46 struct timespec res; 42 47 clock_getres( CLOCK_REALTIME, &res ); 43 48 return ((int64_t)res.tv_sec * TIMEGRAN + res.tv_nsec)`ns; 44 } // getRes49 } // resolutionHi 45 50 46 Duration getRes() {51 Duration resolution() { // clock resolution without nanoseconds (coarse) 47 52 struct timespec res; 48 53 clock_getres( CLOCK_REALTIME_COARSE, &res ); 49 54 return ((int64_t)res.tv_sec * TIMEGRAN + res.tv_nsec)`ns; 50 } // getRes55 } // resolution 51 56 52 Time getTimeNsec() { //with nanoseconds57 Time timeHiRes() { // real time with nanoseconds 53 58 timespec curr; 54 59 clock_gettime( CLOCK_REALTIME, &curr ); 55 60 return (Time){ curr }; 56 } // getTimeNsec61 } // timeHiRes 57 62 58 Time getTime() { //without nanoseconds63 Time time() { // real time without nanoseconds 59 64 timespec curr; 60 65 clock_gettime( CLOCK_REALTIME_COARSE, &curr ); 61 66 curr.tv_nsec = 0; 62 67 return (Time){ curr }; 63 } // getTime68 } // time 64 69 65 Time getTime( Clock & clk ) with( clk ) {66 return getTime() + offset;67 } // getTime70 Time time( Clock & clk ) with( clk ) { // real time for given clock 71 return time() + offset; 72 } // time 68 73 69 74 Time ?()( Clock & clk ) with( clk ) { // alternative syntax 70 return getTime() + offset;71 } // getTime75 return time() + offset; 76 } // ?() 72 77 73 timeval getTime( Clock & clk ) {78 timeval time( Clock & clk ) { // convert to C time format 74 79 return (timeval){ clk() }; 75 } // getTime80 } // time 76 81 77 tm getTime( Clock & clk ) with( clk ) {82 tm time( Clock & clk ) with( clk ) { 78 83 tm ret; 79 localtime_r( getTime( clk ).tv_sec, &ret );84 localtime_r( time( clk ).tv_sec, &ret ); 80 85 return ret; 81 } // getTime86 } // time 82 87 83 Time getCPUTime() { 88 // CFA processor CPU-time watch that ticks when the processor (kernel thread) is running. This watch is affected by 89 // discontinuous jumps when the OS is not running the kernal thread. A duration is returned because the value is 90 // relative and cannot be converted to real-time (wall-clock) time. 91 Duration processor() { // non-monotonic duration of kernel thread 84 92 timespec ts; 85 93 clock_gettime( CLOCK_THREAD_CPUTIME_ID, &ts ); 86 return (Time){ ts }; 87 } // getCPUTime 94 return (Duration){ ts }; 95 } // processor 96 97 // Program CPU-time watch measures CPU time consumed by all processors (kernel threads) in the UNIX process. This 98 // watch is affected by discontinuous jumps when the OS is not running the kernel threads. A duration is returned 99 // because the value is relative and cannot be converted to real-time (wall-clock) time. 100 Duration program() { // non-monotonic duration of program CPU 101 timespec ts; 102 clock_gettime( CLOCK_PROCESS_CPUTIME_ID, &ts ); 103 return (Duration){ ts }; 104 } // program 105 106 // Monotonic duration from machine boot and including system suspension. This watch is unaffected by discontinuous 107 // jumps resulting from manual changes of the clock, and incremental adjustments performed by adjtime(3) and NTP 108 // (Fall back). A duration is returned because the value is relative and cannot be converted to real-time 109 // (wall-clock) time. 110 Duration boot() { // monotonic duration since computer boot 111 timespec ts; 112 clock_gettime( CLOCK_BOOTTIME, &ts ); 113 return (Duration){ ts }; 114 } // boot 88 115 } // distribution 89 116 -
libcfa/src/concurrency/alarm.cfa
rfeacef9 r5407cdc 15 15 16 16 #define __cforall_thread__ 17 // #define __CFA_DEBUG_PRINT_PREEMPTION__ 17 18 18 19 #include <errno.h> … … 107 108 bool first = ! & alarms`first; 108 109 110 __cfadbg_print_safe( preemption, " KERNEL: alarm inserting %p (%lu).\n", this, this->alarm.tn ); 109 111 insert( &alarms, this ); 110 112 if( first ) { … … 114 116 unlock( event_kernel->lock ); 115 117 this->set = true; 116 enable_interrupts( __cfaabi_dbg_ctx);118 enable_interrupts(); 117 119 } 118 120 … … 125 127 } 126 128 unlock( event_kernel->lock ); 127 enable_interrupts( __cfaabi_dbg_ctx);129 enable_interrupts(); 128 130 this->set = false; 129 131 } -
libcfa/src/concurrency/clib/cfathread.cfa
rfeacef9 r5407cdc 14 14 // 15 15 16 #include "fstream.hfa" 17 #include "locks.hfa" 16 18 #include "kernel.hfa" 19 #include "stats.hfa" 17 20 #include "thread.hfa" 18 19 thread CRunner { 20 void (*themain)( CRunner * ); 21 #include "time.hfa" 22 23 #include "cfathread.h" 24 25 extern void ?{}(processor &, const char[], cluster &, $thread *); 26 extern "C" { 27 extern void __cfactx_invoke_thread(void (*main)(void *), void * this); 28 } 29 30 //================================================================================ 31 // Thread run y the C Interface 32 33 struct cfathread_object { 34 $thread self; 35 void * (*themain)( void * ); 36 void * arg; 37 void * ret; 21 38 }; 22 23 static void ?{}( CRunner & this, void (*themain)( CRunner * ) ) { 39 void main(cfathread_object & this); 40 void ^?{}(cfathread_object & mutex this); 41 42 static inline $thread * get_thread( cfathread_object & this ) { return &this.self; } 43 44 typedef ThreadCancelled(cfathread_object) cfathread_exception; 45 typedef ThreadCancelled_vtable(cfathread_object) cfathread_vtable; 46 47 void defaultResumptionHandler(ThreadCancelled(cfathread_object) & except) { 48 abort | "A thread was cancelled"; 49 } 50 51 cfathread_vtable _cfathread_vtable_instance; 52 53 cfathread_vtable & const _default_vtable = _cfathread_vtable_instance; 54 55 cfathread_vtable const & get_exception_vtable(cfathread_exception *) { 56 return _cfathread_vtable_instance; 57 } 58 59 static void ?{}( cfathread_object & this, cluster & cl, void *(*themain)( void * ), void * arg ) { 24 60 this.themain = themain; 25 } 26 27 void main( CRunner & this ) { 28 this.themain( &this ); 29 } 30 31 processor * procs = 0p; 32 int proc_cnt = 1; 33 61 this.arg = arg; 62 (this.self){"C-thread", cl}; 63 __thrd_start(this, main); 64 } 65 66 void ^?{}(cfathread_object & mutex this) { 67 ^(this.self){}; 68 } 69 70 void main( cfathread_object & this ) { 71 __attribute__((unused)) void * const thrd_obj = (void*)&this; 72 __attribute__((unused)) void * const thrd_hdl = (void*)active_thread(); 73 /* paranoid */ verify( thrd_obj == thrd_hdl ); 74 75 this.ret = this.themain( this.arg ); 76 } 77 78 //================================================================================ 79 // Special Init Thread responsible for the initialization or processors 80 struct __cfainit { 81 $thread self; 82 void (*init)( void * ); 83 void * arg; 84 }; 85 void main(__cfainit & this); 86 void ^?{}(__cfainit & mutex this); 87 88 static inline $thread * get_thread( __cfainit & this ) { return &this.self; } 89 90 typedef ThreadCancelled(__cfainit) __cfainit_exception; 91 typedef ThreadCancelled_vtable(__cfainit) __cfainit_vtable; 92 93 void defaultResumptionHandler(ThreadCancelled(__cfainit) & except) { 94 abort | "The init thread was cancelled"; 95 } 96 97 __cfainit_vtable ___cfainit_vtable_instance; 98 99 __cfainit_vtable const & get_exception_vtable(__cfainit_exception *) { 100 return ___cfainit_vtable_instance; 101 } 102 103 static void ?{}( __cfainit & this, void (*init)( void * ), void * arg ) { 104 this.init = init; 105 this.arg = arg; 106 (this.self){"Processir Init"}; 107 108 // Don't use __thrd_start! just prep the context manually 109 $thread * this_thrd = get_thread(this); 110 void (*main_p)(__cfainit &) = main; 111 112 disable_interrupts(); 113 __cfactx_start(main_p, get_coroutine(this), this, __cfactx_invoke_thread); 114 115 this_thrd->context.[SP, FP] = this_thrd->self_cor.context.[SP, FP]; 116 /* paranoid */ verify( this_thrd->context.SP ); 117 118 this_thrd->state = Ready; 119 enable_interrupts(); 120 } 121 122 void ^?{}(__cfainit & mutex this) { 123 ^(this.self){}; 124 } 125 126 void main( __cfainit & this ) { 127 __attribute__((unused)) void * const thrd_obj = (void*)&this; 128 __attribute__((unused)) void * const thrd_hdl = (void*)active_thread(); 129 /* paranoid */ verify( thrd_obj == thrd_hdl ); 130 131 this.init( this.arg ); 132 } 133 134 //================================================================================ 135 // Main Api 34 136 extern "C" { 35 //-------------------- 36 // Basic thread management 37 CRunner * cfathread_create( void (*main)( CRunner * ) ) { 38 return new( main ); 39 } 40 41 void cfathread_join( CRunner * thrd ) { 42 delete( thrd ); 137 int cfathread_cluster_create(cfathread_cluster_t * cl) __attribute__((nonnull(1))) { 138 *cl = new(); 139 return 0; 140 } 141 142 cfathread_cluster_t cfathread_cluster_self(void) { 143 return active_cluster(); 144 } 145 146 int cfathread_cluster_print_stats( cfathread_cluster_t cl ) { 147 #if !defined(__CFA_NO_STATISTICS__) 148 print_stats_at_exit( *cl, CFA_STATS_READY_Q | CFA_STATS_IO ); 149 print_stats_now( *cl, CFA_STATS_READY_Q | CFA_STATS_IO ); 150 #endif 151 return 0; 152 } 153 154 int cfathread_cluster_add_worker(cfathread_cluster_t cl, pthread_t* tid, void (*init_routine) (void *), void * arg) { 155 __cfainit * it = 0p; 156 if(init_routine) { 157 it = alloc(); 158 (*it){init_routine, arg}; 159 } 160 processor * proc = alloc(); 161 (*proc){ "C-processor", *cl, get_thread(*it) }; 162 163 // Wait for the init thread to return before continuing 164 if(it) { 165 ^(*it){}; 166 free(it); 167 } 168 169 if(tid) *tid = proc->kernel_thread; 170 return 0; 171 } 172 173 int cfathread_cluster_pause (cfathread_cluster_t) { 174 abort | "Pausing clusters is not supported"; 175 exit(1); 176 } 177 178 int cfathread_cluster_resume(cfathread_cluster_t) { 179 abort | "Resuming clusters is not supported"; 180 exit(1); 181 } 182 183 //-------------------- 184 // Thread attributes 185 int cfathread_attr_init(cfathread_attr_t *attr) __attribute__((nonnull (1))) { 186 attr->cl = active_cluster(); 187 return 0; 188 } 189 190 //-------------------- 191 // Thread 192 int cfathread_create( cfathread_t * handle, const cfathread_attr_t * attr, void *(*main)( void * ), void * arg ) __attribute__((nonnull (1))) { 193 cluster * cl = attr ? attr->cl : active_cluster(); 194 cfathread_t thrd = alloc(); 195 (*thrd){ *cl, main, arg }; 196 *handle = thrd; 197 return 0; 198 } 199 200 int cfathread_join( cfathread_t thrd, void ** retval ) { 201 void * ret = join( *thrd ).ret; 202 ^( *thrd ){}; 203 free(thrd); 204 if(retval) { 205 *retval = ret; 206 } 207 return 0; 208 } 209 210 int cfathread_get_errno(void) { 211 return errno; 212 } 213 214 cfathread_t cfathread_self(void) { 215 return (cfathread_t)active_thread(); 216 } 217 218 int cfathread_usleep(useconds_t usecs) { 219 sleep(usecs`us); 220 return 0; 221 } 222 223 int cfathread_sleep(unsigned int secs) { 224 sleep(secs`s); 225 return 0; 43 226 } 44 227 … … 47 230 } 48 231 49 void cfathread_unpark( CRunner *thrd ) {232 void cfathread_unpark( cfathread_t thrd ) { 50 233 unpark( *thrd ); 51 234 } … … 55 238 } 56 239 57 //-------------------- 58 // Basic kernel features 59 void cfathread_setproccnt( int ncnt ) { 60 assert( ncnt >= 1 ); 61 adelete( procs ); 62 63 proc_cnt = ncnt - 1; 64 procs = anew(proc_cnt); 65 } 66 } 240 typedef struct cfathread_mutex * cfathread_mutex_t; 241 242 //-------------------- 243 // Mutex 244 struct cfathread_mutex { 245 fast_lock impl; 246 }; 247 int cfathread_mutex_init(cfathread_mutex_t *restrict mut, const cfathread_mutexattr_t *restrict) __attribute__((nonnull (1))) { *mut = new(); return 0; } 248 int cfathread_mutex_destroy(cfathread_mutex_t *mut) __attribute__((nonnull (1))) { delete( *mut ); return 0; } 249 int cfathread_mutex_lock (cfathread_mutex_t *mut) __attribute__((nonnull (1))) { lock( (*mut)->impl ); return 0; } 250 int cfathread_mutex_unlock (cfathread_mutex_t *mut) __attribute__((nonnull (1))) { unlock( (*mut)->impl ); return 0; } 251 int cfathread_mutex_trylock(cfathread_mutex_t *mut) __attribute__((nonnull (1))) { 252 bool ret = try_lock( (*mut)->impl ); 253 if( ret ) return 0; 254 else return EBUSY; 255 } 256 257 //-------------------- 258 // Condition 259 struct cfathread_condition { 260 condition_variable(fast_lock) impl; 261 }; 262 int cfathread_cond_init(cfathread_cond_t *restrict cond, const cfathread_condattr_t *restrict) __attribute__((nonnull (1))) { *cond = new(); return 0; } 263 int cfathread_cond_signal(cfathread_cond_t *cond) __attribute__((nonnull (1))) { notify_one( (*cond)->impl ); return 0; } 264 int cfathread_cond_wait(cfathread_cond_t *restrict cond, cfathread_mutex_t *restrict mut) __attribute__((nonnull (1,2))) { wait( (*cond)->impl, (*mut)->impl ); return 0; } 265 int cfathread_cond_timedwait(cfathread_cond_t *restrict cond, cfathread_mutex_t *restrict mut, const struct timespec *restrict abstime) __attribute__((nonnull (1,2,3))) { 266 Time t = { *abstime }; 267 if( wait( (*cond)->impl, (*mut)->impl, t ) ) { 268 return 0; 269 } 270 errno = ETIMEDOUT; 271 return ETIMEDOUT; 272 } 273 } 274 275 #include <iofwd.hfa> 276 277 extern "C" { 278 #include <unistd.h> 279 #include <sys/types.h> 280 #include <sys/socket.h> 281 282 //-------------------- 283 // IO operations 284 int cfathread_socket(int domain, int type, int protocol) { 285 return socket(domain, type, protocol); 286 } 287 int cfathread_bind(int socket, const struct sockaddr *address, socklen_t address_len) { 288 return bind(socket, address, address_len); 289 } 290 291 int cfathread_listen(int socket, int backlog) { 292 return listen(socket, backlog); 293 } 294 295 int cfathread_accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len) { 296 return cfa_accept4(socket, address, address_len, 0, CFA_IO_LAZY); 297 } 298 299 int cfathread_connect(int socket, const struct sockaddr *address, socklen_t address_len) { 300 return cfa_connect(socket, address, address_len, CFA_IO_LAZY); 301 } 302 303 int cfathread_dup(int fildes) { 304 return dup(fildes); 305 } 306 307 int cfathread_close(int fildes) { 308 return cfa_close(fildes, CFA_IO_LAZY); 309 } 310 311 ssize_t cfathread_sendmsg(int socket, const struct msghdr *message, int flags) { 312 return cfa_sendmsg(socket, message, flags, CFA_IO_LAZY); 313 } 314 315 ssize_t cfathread_write(int fildes, const void *buf, size_t nbyte) { 316 // Use send rather then write for socket since it's faster 317 return cfa_send(fildes, buf, nbyte, 0, CFA_IO_LAZY); 318 } 319 320 ssize_t cfathread_recvfrom(int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len) { 321 struct iovec iov; 322 iov.iov_base = buffer; 323 iov.iov_len = length; 324 325 struct msghdr msg; 326 msg.msg_name = address; 327 msg.msg_namelen = address_len ? (socklen_t)*address_len : (socklen_t)0; 328 msg.msg_iov = &iov; 329 msg.msg_iovlen = 1; 330 msg.msg_control = 0p; 331 msg.msg_controllen = 0; 332 333 ssize_t ret = cfa_recvmsg(socket, &msg, flags, CFA_IO_LAZY); 334 335 if(address_len) *address_len = msg.msg_namelen; 336 return ret; 337 } 338 339 ssize_t cfathread_read(int fildes, void *buf, size_t nbyte) { 340 // Use recv rather then read for socket since it's faster 341 return cfa_recv(fildes, buf, nbyte, 0, CFA_IO_LAZY); 342 } 343 344 } -
libcfa/src/concurrency/clib/cfathread.h
rfeacef9 r5407cdc 14 14 // 15 15 16 #include "stddef.h"17 #include "invoke.h"18 19 16 #if defined(__cforall) || defined(__cplusplus) 20 17 extern "C" { 21 18 #endif 19 #include <asm/types.h> 20 #include <errno.h> 21 #include <unistd.h> 22 23 22 24 //-------------------- 23 25 // Basic types 24 struct cfathread_CRunner_t; 25 typedef struct cfathread_CRunner_t * cfathread_t; 26 27 typedef struct cluster * cfathread_cluster_t; 28 29 int cfathread_cluster_create(cfathread_cluster_t * cluster); 30 cfathread_cluster_t cfathread_cluster_self(void); 31 int cfathread_cluster_print_stats(cfathread_cluster_t cluster); 32 int cfathread_cluster_add_worker(cfathread_cluster_t cluster, pthread_t* tid, void (*init_routine) (void *), void * arg); 33 int cfathread_cluster_pause (cfathread_cluster_t cluster); 34 int cfathread_cluster_resume(cfathread_cluster_t cluster); 26 35 27 36 //-------------------- 28 // Basic thread support 29 cfathread_t cfathread_create( void (*main)( cfathread_t ) ); 30 void cfathread_join( cfathread_t ); 37 // thread attribute 38 typedef struct cfathread_attr { 39 cfathread_cluster_t cl; 40 } cfathread_attr_t; 41 42 int cfathread_attr_init(cfathread_attr_t * attr) __attribute__((nonnull (1))); 43 static inline int cfathread_attr_destroy(cfathread_attr_t * attr) __attribute__((nonnull (1))); 44 static inline int cfathread_attr_destroy(cfathread_attr_t * attr) { return 0; } 45 static inline int cfathread_attr_setbackground(cfathread_attr_t * attr, int background) __attribute__((nonnull (1))); 46 static inline int cfathread_attr_setbackground(cfathread_attr_t * attr, int background) { return 0; } 47 static inline int cfathread_attr_setcluster(cfathread_attr_t * attr, cfathread_cluster_t cl) __attribute__((nonnull (1))); 48 static inline int cfathread_attr_setcluster(cfathread_attr_t * attr, cfathread_cluster_t cl) { attr->cl = cl; return 0; } 49 50 //-------------------- 51 // thread type 52 struct cfathread_object; 53 typedef struct cfathread_object * cfathread_t; 54 55 int cfathread_create( cfathread_t * h, const cfathread_attr_t * a, void *(*main)( void * ), void * arg ) __attribute__((nonnull (1))); 56 int cfathread_join( cfathread_t, void ** retval ); 57 58 int cfathread_get_errno(void); 59 cfathread_t cfathread_self(void); 60 61 int cfathread_usleep(useconds_t usecs); 62 int cfathread_sleep(unsigned int secs); 31 63 32 64 void cfathread_park( void ); … … 35 67 36 68 //-------------------- 37 // Basic kernel features38 void cfathread_setproccnt( int );69 // mutex and condition 70 struct timespec; 39 71 72 typedef struct cfathread_mutex_attr { 73 } cfathread_mutexattr_t; 74 typedef struct cfathread_mutex * cfathread_mutex_t; 75 int cfathread_mutex_init(cfathread_mutex_t *restrict mut, const cfathread_mutexattr_t *restrict attr) __attribute__((nonnull (1))); 76 int cfathread_mutex_destroy(cfathread_mutex_t *mut) __attribute__((nonnull (1))); 77 int cfathread_mutex_lock(cfathread_mutex_t *mut) __attribute__((nonnull (1))); 78 int cfathread_mutex_trylock(cfathread_mutex_t *mut) __attribute__((nonnull (1))); 79 int cfathread_mutex_unlock(cfathread_mutex_t *mut) __attribute__((nonnull (1))); 80 81 typedef struct cfathread_cond_attr { 82 } cfathread_condattr_t; 83 typedef struct cfathread_condition * cfathread_cond_t; 84 int cfathread_cond_init(cfathread_cond_t *restrict cond, const cfathread_condattr_t *restrict attr) __attribute__((nonnull (1))); 85 int cfathread_cond_wait(cfathread_cond_t *restrict cond, cfathread_mutex_t *restrict mut) __attribute__((nonnull (1,2))); 86 int cfathread_cond_timedwait(cfathread_cond_t *restrict cond, cfathread_mutex_t *restrict mut, const struct timespec *restrict abstime) __attribute__((nonnull (1,2,3))); 87 int cfathread_cond_signal(cfathread_cond_t *cond) __attribute__((nonnull (1))); 88 89 //-------------------- 90 // IO operations 91 struct sockaddr; 92 struct msghdr; 93 int cfathread_socket(int domain, int type, int protocol); 94 int cfathread_bind(int socket, const struct sockaddr *address, socklen_t address_len); 95 int cfathread_listen(int socket, int backlog); 96 int cfathread_accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len); 97 int cfathread_connect(int socket, const struct sockaddr *address, socklen_t address_len); 98 int cfathread_dup(int fildes); 99 int cfathread_close(int fildes); 100 ssize_t cfathread_sendmsg(int socket, const struct msghdr *message, int flags); 101 ssize_t cfathread_write(int fildes, const void *buf, size_t nbyte); 102 ssize_t cfathread_recvfrom(int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len); 103 ssize_t cfathread_read(int fildes, void *buf, size_t nbyte); 40 104 41 105 #if defined(__cforall) || defined(__cplusplus) -
libcfa/src/concurrency/coroutine.cfa
rfeacef9 r5407cdc 46 46 47 47 //----------------------------------------------------------------------------- 48 FORALL_DATA_INSTANCE(CoroutineCancelled, (coroutine_t &), (coroutine_t))49 50 forall(T &)51 void mark_exception(CoroutineCancelled(T) *) {}52 53 48 forall(T &) 54 49 void copy(CoroutineCancelled(T) * dst, CoroutineCancelled(T) * src) { … … 65 60 // This code should not be inlined. It is the error path on resume. 66 61 forall(T & | is_coroutine(T)) 67 void __cfaehm_cancelled_coroutine( T & cor, $coroutine * desc ) { 62 void __cfaehm_cancelled_coroutine( 63 T & cor, $coroutine * desc, EHM_DEFAULT_VTABLE(CoroutineCancelled, (T)) ) { 68 64 verify( desc->cancellation ); 69 65 desc->state = Cancelled; … … 72 68 // TODO: Remove explitate vtable set once trac#186 is fixed. 73 69 CoroutineCancelled(T) except; 74 except.virtual_table = & get_exception_vtable(&except);70 except.virtual_table = &_default_vtable; 75 71 except.the_coroutine = &cor; 76 72 except.the_exception = except; 77 throwResume except; 73 // Why does this need a cast? 74 throwResume (CoroutineCancelled(T) &)except; 78 75 79 76 except->virtual_table->free( except ); … … 148 145 // Part of the Public API 149 146 // Not inline since only ever called once per coroutine 150 forall(T & | is_coroutine(T) )147 forall(T & | is_coroutine(T) | { EHM_DEFAULT_VTABLE(CoroutineCancelled, (T)); }) 151 148 void prime(T& cor) { 152 149 $coroutine* this = get_coroutine(cor); … … 196 193 197 194 void __stack_clean ( __stack_info_t * this ) { 198 size_t size = ((intptr_t)this->storage->base) - ((intptr_t)this->storage->limit) + sizeof(__stack_t);199 195 void * storage = this->storage->limit; 200 196 201 197 #if CFA_COROUTINE_USE_MMAP 198 size_t size = ((intptr_t)this->storage->base) - ((intptr_t)this->storage->limit) + sizeof(__stack_t); 202 199 storage = (void *)(((intptr_t)storage) - __page_size); 203 200 if(munmap(storage, size + __page_size) == -1) { -
libcfa/src/concurrency/coroutine.hfa
rfeacef9 r5407cdc 22 22 //----------------------------------------------------------------------------- 23 23 // Exception thrown from resume when a coroutine stack is cancelled. 24 FORALL_DATA_EXCEPTION(CoroutineCancelled, (coroutine_t &), (coroutine_t)) (24 EHM_FORALL_EXCEPTION(CoroutineCancelled, (coroutine_t &), (coroutine_t)) ( 25 25 coroutine_t * the_coroutine; 26 26 exception_t * the_exception; … … 60 60 //----------------------------------------------------------------------------- 61 61 // Public coroutine API 62 forall(T & | is_coroutine(T) )62 forall(T & | is_coroutine(T) | { EHM_DEFAULT_VTABLE(CoroutineCancelled, (T)); }) 63 63 void prime(T & cor); 64 64 … … 130 130 131 131 forall(T & | is_coroutine(T)) 132 void __cfaehm_cancelled_coroutine( T & cor, $coroutine * desc ); 132 void __cfaehm_cancelled_coroutine( 133 T & cor, $coroutine * desc, EHM_DEFAULT_VTABLE(CoroutineCancelled, (T)) ); 133 134 134 135 // Resume implementation inlined for performance 135 forall(T & | is_coroutine(T) )136 forall(T & | is_coroutine(T) | { EHM_DEFAULT_VTABLE(CoroutineCancelled, (T)); }) 136 137 static inline T & resume(T & cor) { 137 138 // optimization : read TLS once and reuse it … … 163 164 $ctx_switch( src, dst ); 164 165 if ( unlikely(dst->cancellation) ) { 165 __cfaehm_cancelled_coroutine( cor, dst );166 __cfaehm_cancelled_coroutine( cor, dst, _default_vtable ); 166 167 } 167 168 -
libcfa/src/concurrency/future.hfa
rfeacef9 r5407cdc 37 37 38 38 // Fulfil the future, returns whether or not someone was unblocked 39 boolfulfil( future(T) & this, T result ) {39 $thread * fulfil( future(T) & this, T result ) { 40 40 this.result = result; 41 41 return fulfil( (future_t&)this ); … … 96 96 bool fulfil( multi_future(T) & this, T result ) { 97 97 this.result = result; 98 return fulfil( (future_t&)this ) ;98 return fulfil( (future_t&)this ) != 0p; 99 99 } 100 100 -
libcfa/src/concurrency/invoke.c
rfeacef9 r5407cdc 34 34 35 35 extern void disable_interrupts() OPTIONAL_THREAD; 36 extern void enable_interrupts( _ _cfaabi_dbg_ctx_param);36 extern void enable_interrupts( _Bool poll ); 37 37 38 38 void __cfactx_invoke_coroutine( … … 82 82 ) { 83 83 // Officially start the thread by enabling preemption 84 enable_interrupts( __cfaabi_dbg_ctx);84 enable_interrupts( true ); 85 85 86 86 // Call the main of the thread -
libcfa/src/concurrency/invoke.h
rfeacef9 r5407cdc 148 148 struct $thread * prev; 149 149 volatile unsigned long long ts; 150 intpreferred;150 unsigned preferred; 151 151 }; 152 152 … … 200 200 } node; 201 201 202 struct processor * last_proc; 203 202 204 #if defined( __CFA_WITH_VERIFY__ ) 203 205 void * canary; … … 224 226 } 225 227 228 static inline $thread * volatile & ?`next ( $thread * this ) __attribute__((const)) { 229 return this->seqable.next; 230 } 231 226 232 static inline $thread *& Back( $thread * this ) __attribute__((const)) { 227 233 return this->seqable.back; -
libcfa/src/concurrency/io.cfa
rfeacef9 r5407cdc 32 32 extern "C" { 33 33 #include <sys/syscall.h> 34 #include <sys/eventfd.h> 34 35 35 36 #include <linux/io_uring.h> … … 39 40 #include "kernel.hfa" 40 41 #include "kernel/fwd.hfa" 42 #include "kernel_private.hfa" 41 43 #include "io/types.hfa" 42 44 … … 79 81 }; 80 82 81 // returns true of acquired as leader or second leader 82 static inline bool try_lock( __leaderlock_t & this ) { 83 const uintptr_t thrd = 1z | (uintptr_t)active_thread(); 84 bool block; 85 disable_interrupts(); 86 for() { 87 struct $thread * expected = this.value; 88 if( 1p != expected && 0p != expected ) { 89 /* paranoid */ verify( thrd != (uintptr_t)expected ); // We better not already be the next leader 90 enable_interrupts( __cfaabi_dbg_ctx ); 91 return false; 92 } 93 struct $thread * desired; 94 if( 0p == expected ) { 95 // If the lock isn't locked acquire it, no need to block 96 desired = 1p; 97 block = false; 98 } 99 else { 100 // If the lock is already locked try becomming the next leader 101 desired = (struct $thread *)thrd; 102 block = true; 103 } 104 if( __atomic_compare_exchange_n(&this.value, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ) break; 105 } 106 if( block ) { 107 enable_interrupts( __cfaabi_dbg_ctx ); 108 park(); 109 disable_interrupts(); 110 } 111 return true; 112 } 113 114 static inline bool next( __leaderlock_t & this ) { 83 static $io_context * __ioarbiter_allocate( $io_arbiter & this, __u32 idxs[], __u32 want ); 84 static void __ioarbiter_submit( $io_context * , __u32 idxs[], __u32 have, bool lazy ); 85 static void __ioarbiter_flush ( $io_context & ); 86 static inline void __ioarbiter_notify( $io_context & ctx ); 87 //============================================================================================= 88 // I/O Polling 89 //============================================================================================= 90 static inline unsigned __flush( struct $io_context & ); 91 static inline __u32 __release_sqes( struct $io_context & ); 92 extern void __kernel_unpark( $thread * thrd ); 93 94 bool __cfa_io_drain( processor * proc ) { 115 95 /* paranoid */ verify( ! __preemption_enabled() ); 116 struct $thread * nextt; 117 for() { 118 struct $thread * expected = this.value; 119 /* paranoid */ verify( (1 & (uintptr_t)expected) == 1 ); // The lock better be locked 120 121 struct $thread * desired; 122 if( 1p == expected ) { 123 // No next leader, just unlock 124 desired = 0p; 125 nextt = 0p; 126 } 127 else { 128 // There is a next leader, remove but keep locked 129 desired = 1p; 130 nextt = (struct $thread *)(~1z & (uintptr_t)expected); 131 } 132 if( __atomic_compare_exchange_n(&this.value, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ) break; 133 } 134 135 if(nextt) { 136 unpark( nextt ); 137 enable_interrupts( __cfaabi_dbg_ctx ); 138 return true; 139 } 140 enable_interrupts( __cfaabi_dbg_ctx ); 141 return false; 142 } 143 144 //============================================================================================= 145 // I/O Syscall 146 //============================================================================================= 147 static int __io_uring_enter( struct __io_data & ring, unsigned to_submit, bool get ) { 148 bool need_sys_to_submit = false; 149 bool need_sys_to_complete = false; 150 unsigned flags = 0; 151 152 TO_SUBMIT: 153 if( to_submit > 0 ) { 154 if( !(ring.ring_flags & IORING_SETUP_SQPOLL) ) { 155 need_sys_to_submit = true; 156 break TO_SUBMIT; 157 } 158 if( (*ring.submit_q.flags) & IORING_SQ_NEED_WAKEUP ) { 159 need_sys_to_submit = true; 160 flags |= IORING_ENTER_SQ_WAKEUP; 161 } 162 } 163 164 if( get && !(ring.ring_flags & IORING_SETUP_SQPOLL) ) { 165 flags |= IORING_ENTER_GETEVENTS; 166 if( (ring.ring_flags & IORING_SETUP_IOPOLL) ) { 167 need_sys_to_complete = true; 168 } 169 } 170 171 int ret = 0; 172 if( need_sys_to_submit || need_sys_to_complete ) { 173 __cfadbg_print_safe(io_core, "Kernel I/O : IO_URING enter %d %u %u\n", ring.fd, to_submit, flags); 174 ret = syscall( __NR_io_uring_enter, ring.fd, to_submit, 0, flags, (sigset_t *)0p, _NSIG / 8); 175 __cfadbg_print_safe(io_core, "Kernel I/O : IO_URING %d returned %d\n", ring.fd, ret); 176 177 if( ret < 0 ) { 178 switch((int)errno) { 179 case EAGAIN: 180 case EINTR: 181 case EBUSY: 182 ret = -1; 183 break; 184 default: 185 abort( "KERNEL ERROR: IO_URING SYSCALL - (%d) %s\n", (int)errno, strerror(errno) ); 186 } 187 } 188 } 189 190 // Memory barrier 191 __atomic_thread_fence( __ATOMIC_SEQ_CST ); 192 return ret; 193 } 194 195 //============================================================================================= 196 // I/O Polling 197 //============================================================================================= 198 static unsigned __collect_submitions( struct __io_data & ring ); 199 static __u32 __release_consumed_submission( struct __io_data & ring ); 200 static inline void __clean( volatile struct io_uring_sqe * sqe ); 201 202 // Process a single completion message from the io_uring 203 // This is NOT thread-safe 204 static inline void process( volatile struct io_uring_cqe & cqe ) { 205 struct io_future_t * future = (struct io_future_t *)(uintptr_t)cqe.user_data; 206 __cfadbg_print_safe( io, "Kernel I/O : Syscall completed : cqe %p, result %d for %p\n", &cqe, cqe.res, future ); 207 208 fulfil( *future, cqe.res ); 209 } 210 211 static [int, bool] __drain_io( & struct __io_data ring ) { 212 /* paranoid */ verify( ! __preemption_enabled() ); 213 214 unsigned to_submit = 0; 215 if( ring.poller_submits ) { 216 // If the poller thread also submits, then we need to aggregate the submissions which are ready 217 to_submit = __collect_submitions( ring ); 218 } 219 220 int ret = __io_uring_enter(ring, to_submit, true); 221 if( ret < 0 ) { 222 return [0, true]; 223 } 224 225 // update statistics 226 if (to_submit > 0) { 227 __STATS__( true, 228 if( to_submit > 0 ) { 229 io.submit_q.submit_avg.rdy += to_submit; 230 io.submit_q.submit_avg.csm += ret; 231 io.submit_q.submit_avg.cnt += 1; 232 } 233 ) 234 } 235 236 __atomic_thread_fence( __ATOMIC_SEQ_CST ); 237 238 // Release the consumed SQEs 239 __release_consumed_submission( ring ); 96 /* paranoid */ verify( ready_schedule_islocked() ); 97 /* paranoid */ verify( proc ); 98 /* paranoid */ verify( proc->io.ctx ); 240 99 241 100 // Drain the queue 242 unsigned head = *ring.completion_q.head; 243 unsigned tail = *ring.completion_q.tail; 244 const __u32 mask = *ring.completion_q.mask; 245 246 // Nothing was new return 0 247 if (head == tail) { 248 return [0, to_submit > 0]; 249 } 101 $io_context * ctx = proc->io.ctx; 102 unsigned head = *ctx->cq.head; 103 unsigned tail = *ctx->cq.tail; 104 const __u32 mask = *ctx->cq.mask; 250 105 251 106 __u32 count = tail - head; 252 /* paranoid */ verify( count != 0 ); 107 __STATS__( false, io.calls.drain++; io.calls.completed += count; ) 108 109 if(count == 0) return false; 110 253 111 for(i; count) { 254 112 unsigned idx = (head + i) & mask; 255 volatile struct io_uring_cqe & cqe = ring.completion_q.cqes[idx];113 volatile struct io_uring_cqe & cqe = ctx->cq.cqes[idx]; 256 114 257 115 /* paranoid */ verify(&cqe); 258 116 259 process( cqe ); 260 } 117 struct io_future_t * future = (struct io_future_t *)(uintptr_t)cqe.user_data; 118 __cfadbg_print_safe( io, "Kernel I/O : Syscall completed : cqe %p, result %d for %p\n", &cqe, cqe.res, future ); 119 120 __kernel_unpark( fulfil( *future, cqe.res, false ) ); 121 } 122 123 __cfadbg_print_safe(io, "Kernel I/O : %u completed\n", count); 261 124 262 125 // Mark to the kernel that the cqe has been seen 263 126 // Ensure that the kernel only sees the new value of the head index after the CQEs have been read. 264 __atomic_fetch_add( ring.completion_q.head, count, __ATOMIC_SEQ_CST ); 265 266 return [count, count > 0 || to_submit > 0]; 267 } 268 269 void main( $io_ctx_thread & this ) { 270 __ioctx_register( this ); 271 272 __cfadbg_print_safe(io_core, "Kernel I/O : IO poller %d (%p) ready\n", this.ring->fd, &this); 273 274 const int reset_cnt = 5; 275 int reset = reset_cnt; 276 // Then loop until we need to start 277 LOOP: 278 while(!__atomic_load_n(&this.done, __ATOMIC_SEQ_CST)) { 279 // Drain the io 280 int count; 281 bool again; 282 disable_interrupts(); 283 [count, again] = __drain_io( *this.ring ); 284 285 if(!again) reset--; 286 127 __atomic_store_n( ctx->cq.head, head + count, __ATOMIC_SEQ_CST ); 128 129 /* paranoid */ verify( ready_schedule_islocked() ); 130 /* paranoid */ verify( ! __preemption_enabled() ); 131 132 return true; 133 } 134 135 void __cfa_io_flush( processor * proc ) { 136 /* paranoid */ verify( ! __preemption_enabled() ); 137 /* paranoid */ verify( proc ); 138 /* paranoid */ verify( proc->io.ctx ); 139 140 $io_context & ctx = *proc->io.ctx; 141 142 __ioarbiter_flush( ctx ); 143 144 __STATS__( true, io.calls.flush++; ) 145 int ret = syscall( __NR_io_uring_enter, ctx.fd, ctx.sq.to_submit, 0, 0, (sigset_t *)0p, _NSIG / 8); 146 if( ret < 0 ) { 147 switch((int)errno) { 148 case EAGAIN: 149 case EINTR: 150 case EBUSY: 287 151 // Update statistics 288 __STATS__( true, 289 io.complete_q.completed_avg.val += count; 290 io.complete_q.completed_avg.cnt += 1; 291 ) 292 enable_interrupts( __cfaabi_dbg_ctx ); 293 294 // If we got something, just yield and check again 295 if(reset > 1) { 296 yield(); 297 continue LOOP; 152 __STATS__( false, io.calls.errors.busy ++; ) 153 return; 154 default: 155 abort( "KERNEL ERROR: IO_URING SYSCALL - (%d) %s\n", (int)errno, strerror(errno) ); 298 156 } 299 300 // We alread failed to find completed entries a few time. 301 if(reset == 1) { 302 // Rearm the context so it can block 303 // but don't block right away 304 // we need to retry one last time in case 305 // something completed *just now* 306 __ioctx_prepare_block( this ); 307 continue LOOP; 308 } 309 310 __STATS__( false, 311 io.complete_q.blocks += 1; 312 ) 313 __cfadbg_print_safe(io_core, "Kernel I/O : Parking io poller %d (%p)\n", this.ring->fd, &this); 314 315 // block this thread 316 wait( this.sem ); 317 318 // restore counter 319 reset = reset_cnt; 320 } 321 322 __cfadbg_print_safe(io_core, "Kernel I/O : Fast poller %d (%p) stopping\n", this.ring->fd, &this); 323 324 __ioctx_unregister( this ); 157 } 158 159 __cfadbg_print_safe(io, "Kernel I/O : %u submitted to io_uring %d\n", ret, ctx.fd); 160 __STATS__( true, io.calls.submitted += ret; ) 161 /* paranoid */ verify( ctx.sq.to_submit <= *ctx.sq.num ); 162 /* paranoid */ verify( ctx.sq.to_submit >= ret ); 163 164 ctx.sq.to_submit -= ret; 165 166 /* paranoid */ verify( ctx.sq.to_submit <= *ctx.sq.num ); 167 168 // Release the consumed SQEs 169 __release_sqes( ctx ); 170 171 /* paranoid */ verify( ! __preemption_enabled() ); 172 173 ctx.proc->io.pending = false; 325 174 } 326 175 … … 344 193 // head and tail must be fully filled and shouldn't ever be touched again. 345 194 // 195 //============================================================================================= 196 // Allocation 197 // for user's convenience fill the sqes from the indexes 198 static inline void __fill(struct io_uring_sqe * out_sqes[], __u32 want, __u32 idxs[], struct $io_context * ctx) { 199 struct io_uring_sqe * sqes = ctx->sq.sqes; 200 for(i; want) { 201 __cfadbg_print_safe(io, "Kernel I/O : filling loop\n"); 202 out_sqes[i] = &sqes[idxs[i]]; 203 } 204 } 205 206 // Try to directly allocate from the a given context 207 // Not thread-safe 208 static inline bool __alloc(struct $io_context * ctx, __u32 idxs[], __u32 want) { 209 __sub_ring_t & sq = ctx->sq; 210 const __u32 mask = *sq.mask; 211 __u32 fhead = sq.free_ring.head; // get the current head of the queue 212 __u32 ftail = sq.free_ring.tail; // get the current tail of the queue 213 214 // If we don't have enough sqes, fail 215 if((ftail - fhead) < want) { return false; } 216 217 // copy all the indexes we want from the available list 218 for(i; want) { 219 __cfadbg_print_safe(io, "Kernel I/O : allocating loop\n"); 220 idxs[i] = sq.free_ring.array[(fhead + i) & mask]; 221 } 222 223 // Advance the head to mark the indexes as consumed 224 __atomic_store_n(&sq.free_ring.head, fhead + want, __ATOMIC_RELEASE); 225 226 // return success 227 return true; 228 } 346 229 347 230 // Allocate an submit queue entry. … … 350 233 // for convenience, return both the index and the pointer to the sqe 351 234 // sqe == &sqes[idx] 352 [* volatile struct io_uring_sqe, __u32] __submit_alloc( struct __io_data & ring, __u64 data ) { 353 /* paranoid */ verify( data != 0 ); 354 355 // Prepare the data we need 356 __attribute((unused)) int len = 0; 357 __attribute((unused)) int block = 0; 358 __u32 cnt = *ring.submit_q.num; 359 __u32 mask = *ring.submit_q.mask; 360 361 __u32 off = thread_rand(); 362 363 // Loop around looking for an available spot 364 for() { 365 // Look through the list starting at some offset 366 for(i; cnt) { 367 __u64 expected = 3; 368 __u32 idx = (i + off) & mask; // Get an index from a random 369 volatile struct io_uring_sqe * sqe = &ring.submit_q.sqes[idx]; 370 volatile __u64 * udata = &sqe->user_data; 371 372 // Allocate the entry by CASing the user_data field from 0 to the future address 373 if( *udata == expected && 374 __atomic_compare_exchange_n( udata, &expected, data, true, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED ) ) 375 { 376 // update statistics 377 __STATS__( false, 378 io.submit_q.alloc_avg.val += len; 379 io.submit_q.alloc_avg.block += block; 380 io.submit_q.alloc_avg.cnt += 1; 381 ) 382 383 // debug log 384 __cfadbg_print_safe( io, "Kernel I/O : allocated [%p, %u] for %p (%p)\n", sqe, idx, active_thread(), (void*)data ); 385 386 // Success return the data 387 return [sqe, idx]; 388 } 389 verify(expected != data); 390 391 // This one was used 392 len ++; 393 } 394 395 block++; 396 397 yield(); 398 } 399 } 400 401 static inline __u32 __submit_to_ready_array( struct __io_data & ring, __u32 idx, const __u32 mask ) { 402 /* paranoid */ verify( idx <= mask ); 403 /* paranoid */ verify( idx != -1ul32 ); 404 405 // We need to find a spot in the ready array 406 __attribute((unused)) int len = 0; 407 __attribute((unused)) int block = 0; 408 __u32 ready_mask = ring.submit_q.ready_cnt - 1; 409 410 __u32 off = thread_rand(); 411 412 __u32 picked; 413 LOOKING: for() { 414 for(i; ring.submit_q.ready_cnt) { 415 picked = (i + off) & ready_mask; 416 __u32 expected = -1ul32; 417 if( __atomic_compare_exchange_n( &ring.submit_q.ready[picked], &expected, idx, true, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED ) ) { 418 break LOOKING; 419 } 420 verify(expected != idx); 421 422 len ++; 423 } 424 425 block++; 426 427 __u32 released = __release_consumed_submission( ring ); 428 if( released == 0 ) { 429 yield(); 430 } 431 } 432 433 // update statistics 434 __STATS__( false, 435 io.submit_q.look_avg.val += len; 436 io.submit_q.look_avg.block += block; 437 io.submit_q.look_avg.cnt += 1; 438 ) 439 440 return picked; 441 } 442 443 void __submit( struct io_context * ctx, __u32 idx ) __attribute__((nonnull (1))) { 444 __io_data & ring = *ctx->thrd.ring; 445 235 struct $io_context * cfa_io_allocate(struct io_uring_sqe * sqes[], __u32 idxs[], __u32 want) { 236 __cfadbg_print_safe(io, "Kernel I/O : attempting to allocate %u\n", want); 237 238 disable_interrupts(); 239 processor * proc = __cfaabi_tls.this_processor; 240 $io_context * ctx = proc->io.ctx; 241 /* paranoid */ verify( __cfaabi_tls.this_processor ); 242 /* paranoid */ verify( ctx ); 243 244 __cfadbg_print_safe(io, "Kernel I/O : attempting to fast allocation\n"); 245 246 // We can proceed to the fast path 247 if( __alloc(ctx, idxs, want) ) { 248 // Allocation was successful 249 __STATS__( true, io.alloc.fast += 1; ) 250 enable_interrupts(); 251 252 __cfadbg_print_safe(io, "Kernel I/O : fast allocation successful from ring %d\n", ctx->fd); 253 254 __fill( sqes, want, idxs, ctx ); 255 return ctx; 256 } 257 // The fast path failed, fallback 258 __STATS__( true, io.alloc.fail += 1; ) 259 260 // Fast path failed, fallback on arbitration 261 __STATS__( true, io.alloc.slow += 1; ) 262 enable_interrupts(); 263 264 $io_arbiter * ioarb = proc->cltr->io.arbiter; 265 /* paranoid */ verify( ioarb ); 266 267 __cfadbg_print_safe(io, "Kernel I/O : falling back on arbiter for allocation\n"); 268 269 struct $io_context * ret = __ioarbiter_allocate(*ioarb, idxs, want); 270 271 __cfadbg_print_safe(io, "Kernel I/O : slow allocation completed from ring %d\n", ret->fd); 272 273 __fill( sqes, want, idxs,ret ); 274 return ret; 275 } 276 277 278 //============================================================================================= 279 // submission 280 static inline void __submit( struct $io_context * ctx, __u32 idxs[], __u32 have, bool lazy) { 281 // We can proceed to the fast path 282 // Get the right objects 283 __sub_ring_t & sq = ctx->sq; 284 const __u32 mask = *sq.mask; 285 __u32 tail = *sq.kring.tail; 286 287 // Add the sqes to the array 288 for( i; have ) { 289 __cfadbg_print_safe(io, "Kernel I/O : __submit loop\n"); 290 sq.kring.array[ (tail + i) & mask ] = idxs[i]; 291 } 292 293 // Make the sqes visible to the submitter 294 __atomic_store_n(sq.kring.tail, tail + have, __ATOMIC_RELEASE); 295 sq.to_submit++; 296 297 ctx->proc->io.pending = true; 298 ctx->proc->io.dirty = true; 299 if(sq.to_submit > 30 || !lazy) { 300 __cfa_io_flush( ctx->proc ); 301 } 302 } 303 304 void cfa_io_submit( struct $io_context * inctx, __u32 idxs[], __u32 have, bool lazy ) __attribute__((nonnull (1))) { 305 __cfadbg_print_safe(io, "Kernel I/O : attempting to submit %u (%s)\n", have, lazy ? "lazy" : "eager"); 306 307 disable_interrupts(); 308 processor * proc = __cfaabi_tls.this_processor; 309 $io_context * ctx = proc->io.ctx; 310 /* paranoid */ verify( __cfaabi_tls.this_processor ); 311 /* paranoid */ verify( ctx ); 312 313 // Can we proceed to the fast path 314 if( ctx == inctx ) // We have the right instance? 446 315 { 447 __attribute__((unused)) volatile struct io_uring_sqe * sqe = &ring.submit_q.sqes[idx]; 448 __cfadbg_print_safe( io, 449 "Kernel I/O : submitting %u (%p) for %p\n" 450 " data: %p\n" 451 " opcode: %s\n" 452 " fd: %d\n" 453 " flags: %d\n" 454 " prio: %d\n" 455 " off: %p\n" 456 " addr: %p\n" 457 " len: %d\n" 458 " other flags: %d\n" 459 " splice fd: %d\n" 460 " pad[0]: %llu\n" 461 " pad[1]: %llu\n" 462 " pad[2]: %llu\n", 463 idx, sqe, 464 active_thread(), 465 (void*)sqe->user_data, 466 opcodes[sqe->opcode], 467 sqe->fd, 468 sqe->flags, 469 sqe->ioprio, 470 (void*)sqe->off, 471 (void*)sqe->addr, 472 sqe->len, 473 sqe->accept_flags, 474 sqe->splice_fd_in, 475 sqe->__pad2[0], 476 sqe->__pad2[1], 477 sqe->__pad2[2] 478 ); 479 } 480 481 482 // Get now the data we definetely need 483 volatile __u32 * const tail = ring.submit_q.tail; 484 const __u32 mask = *ring.submit_q.mask; 485 486 // There are 2 submission schemes, check which one we are using 487 if( ring.poller_submits ) { 488 // If the poller thread submits, then we just need to add this to the ready array 489 __submit_to_ready_array( ring, idx, mask ); 490 491 post( ctx->thrd.sem ); 492 493 __cfadbg_print_safe( io, "Kernel I/O : Added %u to ready for %p\n", idx, active_thread() ); 494 } 495 else if( ring.eager_submits ) { 496 __attribute__((unused)) __u32 picked = __submit_to_ready_array( ring, idx, mask ); 497 498 #if defined(LEADER_LOCK) 499 if( !try_lock(ring.submit_q.submit_lock) ) { 500 __STATS__( false, 501 io.submit_q.helped += 1; 502 ) 503 return; 504 } 505 /* paranoid */ verify( ! __preemption_enabled() ); 506 __STATS__( true, 507 io.submit_q.leader += 1; 508 ) 509 #else 510 for() { 511 yield(); 512 513 if( try_lock(ring.submit_q.submit_lock __cfaabi_dbg_ctx2) ) { 514 __STATS__( false, 515 io.submit_q.leader += 1; 516 ) 517 break; 518 } 519 520 // If some one else collected our index, we are done 521 #warning ABA problem 522 if( ring.submit_q.ready[picked] != idx ) { 523 __STATS__( false, 524 io.submit_q.helped += 1; 525 ) 526 return; 527 } 528 529 __STATS__( false, 530 io.submit_q.busy += 1; 531 ) 532 } 533 #endif 534 535 // We got the lock 536 // Collect the submissions 537 unsigned to_submit = __collect_submitions( ring ); 538 539 // Actually submit 540 int ret = __io_uring_enter( ring, to_submit, false ); 541 542 #if defined(LEADER_LOCK) 543 /* paranoid */ verify( ! __preemption_enabled() ); 544 next(ring.submit_q.submit_lock); 545 #else 546 unlock(ring.submit_q.submit_lock); 547 #endif 548 if( ret < 0 ) { 549 return; 550 } 551 552 // Release the consumed SQEs 553 __release_consumed_submission( ring ); 554 555 // update statistics 556 __STATS__( false, 557 io.submit_q.submit_avg.rdy += to_submit; 558 io.submit_q.submit_avg.csm += ret; 559 io.submit_q.submit_avg.cnt += 1; 560 ) 561 562 __cfadbg_print_safe( io, "Kernel I/O : submitted %u (among %u) for %p\n", idx, ret, active_thread() ); 563 } 564 else 565 { 566 // get mutual exclusion 567 #if defined(LEADER_LOCK) 568 while(!try_lock(ring.submit_q.submit_lock)); 569 #else 570 lock(ring.submit_q.submit_lock __cfaabi_dbg_ctx2); 571 #endif 572 573 /* paranoid */ verifyf( ring.submit_q.sqes[ idx ].user_data != 3ul64, 574 /* paranoid */ "index %u already reclaimed\n" 575 /* paranoid */ "head %u, prev %u, tail %u\n" 576 /* paranoid */ "[-0: %u,-1: %u,-2: %u,-3: %u]\n", 577 /* paranoid */ idx, 578 /* paranoid */ *ring.submit_q.head, ring.submit_q.prev_head, *tail 579 /* paranoid */ ,ring.submit_q.array[ ((*ring.submit_q.head) - 0) & (*ring.submit_q.mask) ] 580 /* paranoid */ ,ring.submit_q.array[ ((*ring.submit_q.head) - 1) & (*ring.submit_q.mask) ] 581 /* paranoid */ ,ring.submit_q.array[ ((*ring.submit_q.head) - 2) & (*ring.submit_q.mask) ] 582 /* paranoid */ ,ring.submit_q.array[ ((*ring.submit_q.head) - 3) & (*ring.submit_q.mask) ] 583 /* paranoid */ ); 584 585 // Append to the list of ready entries 586 587 /* paranoid */ verify( idx <= mask ); 588 ring.submit_q.array[ (*tail) & mask ] = idx; 589 __atomic_fetch_add(tail, 1ul32, __ATOMIC_SEQ_CST); 590 591 // Submit however, many entries need to be submitted 592 int ret = __io_uring_enter( ring, 1, false ); 593 if( ret < 0 ) { 594 switch((int)errno) { 595 default: 596 abort( "KERNEL ERROR: IO_URING SUBMIT - %s\n", strerror(errno) ); 597 } 598 } 599 600 /* paranoid */ verify(ret == 1); 601 602 // update statistics 603 __STATS__( false, 604 io.submit_q.submit_avg.csm += 1; 605 io.submit_q.submit_avg.cnt += 1; 606 ) 607 608 { 609 __attribute__((unused)) volatile __u32 * const head = ring.submit_q.head; 610 __attribute__((unused)) __u32 last_idx = ring.submit_q.array[ ((*head) - 1) & mask ]; 611 __attribute__((unused)) volatile struct io_uring_sqe * sqe = &ring.submit_q.sqes[last_idx]; 612 613 __cfadbg_print_safe( io, 614 "Kernel I/O : last submitted is %u (%p)\n" 615 " data: %p\n" 616 " opcode: %s\n" 617 " fd: %d\n" 618 " flags: %d\n" 619 " prio: %d\n" 620 " off: %p\n" 621 " addr: %p\n" 622 " len: %d\n" 623 " other flags: %d\n" 624 " splice fd: %d\n" 625 " pad[0]: %llu\n" 626 " pad[1]: %llu\n" 627 " pad[2]: %llu\n", 628 last_idx, sqe, 629 (void*)sqe->user_data, 630 opcodes[sqe->opcode], 631 sqe->fd, 632 sqe->flags, 633 sqe->ioprio, 634 (void*)sqe->off, 635 (void*)sqe->addr, 636 sqe->len, 637 sqe->accept_flags, 638 sqe->splice_fd_in, 639 sqe->__pad2[0], 640 sqe->__pad2[1], 641 sqe->__pad2[2] 642 ); 643 } 644 645 __atomic_thread_fence( __ATOMIC_SEQ_CST ); 646 // Release the consumed SQEs 647 648 __release_consumed_submission( ring ); 649 // ring.submit_q.sqes[idx].user_data = 3ul64; 650 651 #if defined(LEADER_LOCK) 652 next(ring.submit_q.submit_lock); 653 #else 654 unlock(ring.submit_q.submit_lock); 655 #endif 656 657 __cfadbg_print_safe( io, "Kernel I/O : submitted %u for %p\n", idx, active_thread() ); 658 } 659 } 660 661 // #define PARTIAL_SUBMIT 32 662 663 // go through the list of submissions in the ready array and moved them into 664 // the ring's submit queue 665 static unsigned __collect_submitions( struct __io_data & ring ) { 666 /* paranoid */ verify( ring.submit_q.ready != 0p ); 667 /* paranoid */ verify( ring.submit_q.ready_cnt > 0 ); 668 669 unsigned to_submit = 0; 670 __u32 tail = *ring.submit_q.tail; 671 const __u32 mask = *ring.submit_q.mask; 672 #if defined(PARTIAL_SUBMIT) 673 #if defined(LEADER_LOCK) 674 #error PARTIAL_SUBMIT and LEADER_LOCK cannot co-exist 675 #endif 676 const __u32 cnt = ring.submit_q.ready_cnt > PARTIAL_SUBMIT ? PARTIAL_SUBMIT : ring.submit_q.ready_cnt; 677 const __u32 offset = ring.submit_q.prev_ready; 678 ring.submit_q.prev_ready += cnt; 679 #else 680 const __u32 cnt = ring.submit_q.ready_cnt; 681 const __u32 offset = 0; 682 #endif 683 684 // Go through the list of ready submissions 685 for( c; cnt ) { 686 __u32 i = (offset + c) % ring.submit_q.ready_cnt; 687 688 // replace any submission with the sentinel, to consume it. 689 __u32 idx = __atomic_exchange_n( &ring.submit_q.ready[i], -1ul32, __ATOMIC_RELAXED); 690 691 // If it was already the sentinel, then we are done 692 if( idx == -1ul32 ) continue; 693 694 // If we got a real submission, append it to the list 695 ring.submit_q.array[ (tail + to_submit) & mask ] = idx & mask; 696 to_submit++; 697 } 698 699 // Increment the tail based on how many we are ready to submit 700 __atomic_fetch_add(ring.submit_q.tail, to_submit, __ATOMIC_SEQ_CST); 701 702 return to_submit; 703 } 704 316 __submit(ctx, idxs, have, lazy); 317 318 // Mark the instance as no longer in-use, re-enable interrupts and return 319 __STATS__( true, io.submit.fast += 1; ) 320 enable_interrupts(); 321 322 __cfadbg_print_safe(io, "Kernel I/O : submitted on fast path\n"); 323 return; 324 } 325 326 // Fast path failed, fallback on arbitration 327 __STATS__( true, io.submit.slow += 1; ) 328 enable_interrupts(); 329 330 __cfadbg_print_safe(io, "Kernel I/O : falling back on arbiter for submission\n"); 331 332 __ioarbiter_submit(inctx, idxs, have, lazy); 333 } 334 335 //============================================================================================= 336 // Flushing 705 337 // Go through the ring's submit queue and release everything that has already been consumed 706 338 // by io_uring 707 static __u32 __release_consumed_submission( struct __io_data & ring ) { 708 const __u32 smask = *ring.submit_q.mask; 709 710 // We need to get the lock to copy the old head and new head 711 if( !try_lock(ring.submit_q.release_lock __cfaabi_dbg_ctx2) ) return 0; 339 // This cannot be done by multiple threads 340 static __u32 __release_sqes( struct $io_context & ctx ) { 341 const __u32 mask = *ctx.sq.mask; 342 712 343 __attribute__((unused)) 713 __u32 ctail = * ring.submit_q.tail;// get the current tail of the queue714 __u32 chead = * ring.submit_q.head;// get the current head of the queue715 __u32 phead = ring.submit_q.prev_head;// get the head the last time we were here716 ring.submit_q.prev_head = chead; // note up to were we processed 717 unlock(ring.submit_q.release_lock);344 __u32 ctail = *ctx.sq.kring.tail; // get the current tail of the queue 345 __u32 chead = *ctx.sq.kring.head; // get the current head of the queue 346 __u32 phead = ctx.sq.kring.released; // get the head the last time we were here 347 348 __u32 ftail = ctx.sq.free_ring.tail; // get the current tail of the queue 718 349 719 350 // the 3 fields are organized like this diagram … … 734 365 __u32 count = chead - phead; 735 366 367 if(count == 0) { 368 return 0; 369 } 370 736 371 // We acquired an previous-head/current-head range 737 372 // go through the range and release the sqes 738 373 for( i; count ) { 739 __u32 idx = ring.submit_q.array[ (phead + i) & smask ]; 740 741 /* paranoid */ verify( 0 != ring.submit_q.sqes[ idx ].user_data ); 742 __clean( &ring.submit_q.sqes[ idx ] ); 743 } 374 __cfadbg_print_safe(io, "Kernel I/O : release loop\n"); 375 __u32 idx = ctx.sq.kring.array[ (phead + i) & mask ]; 376 ctx.sq.free_ring.array[ (ftail + i) & mask ] = idx; 377 } 378 379 ctx.sq.kring.released = chead; // note up to were we processed 380 __atomic_store_n(&ctx.sq.free_ring.tail, ftail + count, __ATOMIC_SEQ_CST); 381 382 __ioarbiter_notify(ctx); 383 744 384 return count; 745 385 } 746 386 747 void __sqe_clean( volatile struct io_uring_sqe * sqe ) { 748 __clean( sqe ); 749 } 750 751 static inline void __clean( volatile struct io_uring_sqe * sqe ) { 752 // If we are in debug mode, thrash the fields to make sure we catch reclamation errors 753 __cfaabi_dbg_debug_do( 754 memset(sqe, 0xde, sizeof(*sqe)); 755 sqe->opcode = (sizeof(opcodes) / sizeof(const char *)) - 1; 756 ); 757 758 // Mark the entry as unused 759 __atomic_store_n(&sqe->user_data, 3ul64, __ATOMIC_SEQ_CST); 387 //============================================================================================= 388 // I/O Arbiter 389 //============================================================================================= 390 static inline void block(__outstanding_io_queue & queue, __outstanding_io & item) { 391 // Lock the list, it's not thread safe 392 lock( queue.lock __cfaabi_dbg_ctx2 ); 393 { 394 // Add our request to the list 395 add( queue.queue, item ); 396 397 // Mark as pending 398 __atomic_store_n( &queue.empty, false, __ATOMIC_SEQ_CST ); 399 } 400 unlock( queue.lock ); 401 402 wait( item.sem ); 403 } 404 405 static inline bool empty(__outstanding_io_queue & queue ) { 406 return __atomic_load_n( &queue.empty, __ATOMIC_SEQ_CST); 407 } 408 409 static $io_context * __ioarbiter_allocate( $io_arbiter & this, __u32 idxs[], __u32 want ) { 410 __cfadbg_print_safe(io, "Kernel I/O : arbiter allocating\n"); 411 412 __STATS__( false, io.alloc.block += 1; ) 413 414 // No one has any resources left, wait for something to finish 415 // We need to add ourself to a list of pending allocs and wait for an answer 416 __pending_alloc pa; 417 pa.idxs = idxs; 418 pa.want = want; 419 420 block(this.pending, (__outstanding_io&)pa); 421 422 return pa.ctx; 423 424 } 425 426 static void __ioarbiter_notify( $io_arbiter & this, $io_context * ctx ) { 427 /* paranoid */ verify( !empty(this.pending.queue) ); 428 429 lock( this.pending.lock __cfaabi_dbg_ctx2 ); 430 { 431 while( !empty(this.pending.queue) ) { 432 __cfadbg_print_safe(io, "Kernel I/O : notifying\n"); 433 __u32 have = ctx->sq.free_ring.tail - ctx->sq.free_ring.head; 434 __pending_alloc & pa = (__pending_alloc&)head( this.pending.queue ); 435 436 if( have > pa.want ) goto DONE; 437 drop( this.pending.queue ); 438 439 /* paranoid */__attribute__((unused)) bool ret = 440 441 __alloc(ctx, pa.idxs, pa.want); 442 443 /* paranoid */ verify( ret ); 444 445 pa.ctx = ctx; 446 447 post( pa.sem ); 448 } 449 450 this.pending.empty = true; 451 DONE:; 452 } 453 unlock( this.pending.lock ); 454 } 455 456 static void __ioarbiter_notify( $io_context & ctx ) { 457 if(!empty( ctx.arbiter->pending )) { 458 __ioarbiter_notify( *ctx.arbiter, &ctx ); 459 } 460 } 461 462 // Simply append to the pending 463 static void __ioarbiter_submit( $io_context * ctx, __u32 idxs[], __u32 have, bool lazy ) { 464 __cfadbg_print_safe(io, "Kernel I/O : submitting %u from the arbiter to context %u\n", have, ctx->fd); 465 466 __cfadbg_print_safe(io, "Kernel I/O : waiting to submit %u\n", have); 467 468 __external_io ei; 469 ei.idxs = idxs; 470 ei.have = have; 471 ei.lazy = lazy; 472 473 block(ctx->ext_sq, (__outstanding_io&)ei); 474 475 __cfadbg_print_safe(io, "Kernel I/O : %u submitted from arbiter\n", have); 476 } 477 478 static void __ioarbiter_flush( $io_context & ctx ) { 479 if(!empty( ctx.ext_sq )) { 480 __STATS__( false, io.flush.external += 1; ) 481 482 __cfadbg_print_safe(io, "Kernel I/O : arbiter flushing\n"); 483 484 lock( ctx.ext_sq.lock __cfaabi_dbg_ctx2 ); 485 { 486 while( !empty(ctx.ext_sq.queue) ) { 487 __external_io & ei = (__external_io&)drop( ctx.ext_sq.queue ); 488 489 __submit(&ctx, ei.idxs, ei.have, ei.lazy); 490 491 post( ei.sem ); 492 } 493 494 ctx.ext_sq.empty = true; 495 } 496 unlock(ctx.ext_sq.lock ); 497 } 760 498 } 761 499 #endif -
libcfa/src/concurrency/io/call.cfa.in
rfeacef9 r5407cdc 54 54 | IOSQE_IO_DRAIN 55 55 #endif 56 #if defined(CFA_HAVE_IOSQE_IO_LINK) 57 | IOSQE_IO_LINK 58 #endif 59 #if defined(CFA_HAVE_IOSQE_IO_HARDLINK) 60 | IOSQE_IO_HARDLINK 61 #endif 56 62 #if defined(CFA_HAVE_IOSQE_ASYNC) 57 63 | IOSQE_ASYNC 58 64 #endif 59 ; 60 61 static const __u32 LINK_FLAGS = 0 62 #if defined(CFA_HAVE_IOSQE_IO_LINK) 63 | IOSQE_IO_LINK 64 #endif 65 #if defined(CFA_HAVE_IOSQE_IO_HARDLINK) 66 | IOSQE_IO_HARDLINK 65 #if defined(CFA_HAVE_IOSQE_BUFFER_SELECTED) 66 | IOSQE_BUFFER_SELECTED 67 67 #endif 68 68 ; … … 74 74 ; 75 75 76 extern [* volatile struct io_uring_sqe, __u32] __submit_alloc( struct __io_data & ring, __u64 data ); 77 extern void __submit( struct io_context * ctx, __u32 idx ) __attribute__((nonnull (1))); 78 79 static inline io_context * __get_io_context( void ) { 80 cluster * cltr = active_cluster(); 81 82 /* paranoid */ verifyf( cltr, "No active cluster for io operation\\n"); 83 assertf( cltr->io.cnt > 0, "Cluster %p has no default io contexts and no context was specified\\n", cltr ); 84 85 /* paranoid */ verifyf( cltr->io.ctxs, "default io contexts for cluster %p are missing\\n", cltr); 86 return &cltr->io.ctxs[ thread_rand() % cltr->io.cnt ]; 87 } 76 extern struct $io_context * cfa_io_allocate(struct io_uring_sqe * out_sqes[], __u32 out_idxs[], __u32 want) __attribute__((nonnull (1,2))); 77 extern void cfa_io_submit( struct $io_context * in_ctx, __u32 in_idxs[], __u32 have, bool lazy ) __attribute__((nonnull (1,2))); 88 78 #endif 89 79 … … 98 88 99 89 extern "C" { 100 #include < sys/types.h>90 #include <asm/types.h> 101 91 #include <sys/socket.h> 102 92 #include <sys/syscall.h> … … 142 132 extern int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 143 133 144 extern ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);134 extern ssize_t splice(int fd_in, __off64_t *off_in, int fd_out, __off64_t *off_out, size_t len, unsigned int flags); 145 135 extern ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags); 146 136 } … … 195 185 return ', '.join(args_a) 196 186 197 AsyncTemplate = """inline void async_{name}(io_future_t & future, {params}, int submit_flags, io_cancellation * cancellation, io_context * context) {{187 AsyncTemplate = """inline void async_{name}(io_future_t & future, {params}, __u64 submit_flags) {{ 198 188 #if !defined(CFA_HAVE_LINUX_IO_URING_H) || !defined(CFA_HAVE_IORING_OP_{op}) 199 189 ssize_t res = {name}({args}); … … 205 195 }} 206 196 #else 207 // we don't support LINK yet208 if( 0 != (submit_flags & LINK_FLAGS) ) {{209 errno = ENOTSUP; return -1;210 }}211 212 if( !context ) {{213 context = __get_io_context();214 }}215 if(cancellation) {{216 cancellation->target = (__u64)(uintptr_t)&future;217 }}218 219 197 __u8 sflags = REGULAR_FLAGS & submit_flags; 220 struct __io_data & ring = *context->thrd.ring;221 222 198 __u32 idx; 223 199 struct io_uring_sqe * sqe; 224 [(volatile struct io_uring_sqe *) sqe, idx] = __submit_alloc( ring, (__u64)(uintptr_t)&future);200 struct $io_context * ctx = cfa_io_allocate( &sqe, &idx, 1 ); 225 201 226 202 sqe->opcode = IORING_OP_{op}; 203 sqe->user_data = (uintptr_t)&future; 227 204 sqe->flags = sflags; 228 205 sqe->ioprio = 0; … … 238 215 asm volatile("": : :"memory"); 239 216 240 verify( sqe->user_data == ( __u64)(uintptr_t)&future );241 __submit( context, idx);217 verify( sqe->user_data == (uintptr_t)&future ); 218 cfa_io_submit( ctx, &idx, 1, 0 != (submit_flags & CFA_IO_LAZY) ); 242 219 #endif 243 220 }}""" 244 221 245 SyncTemplate = """{ret} cfa_{name}({params}, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context) {{ 246 if( timeout >= 0 ) {{ 247 errno = ENOTSUP; 248 return -1; 249 }} 222 SyncTemplate = """{ret} cfa_{name}({params}, __u64 submit_flags) {{ 250 223 io_future_t future; 251 224 252 async_{name}( future, {args}, submit_flags , cancellation, context);225 async_{name}( future, {args}, submit_flags ); 253 226 254 227 wait( future ); … … 265 238 'fd' : 'fd', 266 239 'off' : 'offset', 267 'addr': '( __u64)iov',240 'addr': '(uintptr_t)iov', 268 241 'len' : 'iovcnt', 269 242 }, define = 'CFA_HAVE_PREADV2'), … … 272 245 'fd' : 'fd', 273 246 'off' : 'offset', 274 'addr': '( __u64)iov',247 'addr': '(uintptr_t)iov', 275 248 'len' : 'iovcnt' 276 249 }, define = 'CFA_HAVE_PWRITEV2'), … … 284 257 'addr': 'fd', 285 258 'len': 'op', 286 'off': '( __u64)event'259 'off': '(uintptr_t)event' 287 260 }), 288 261 # CFA_HAVE_IORING_OP_SYNC_FILE_RANGE … … 296 269 Call('SENDMSG', 'ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)', { 297 270 'fd': 'sockfd', 298 'addr': '( __u64)(struct msghdr *)msg',271 'addr': '(uintptr_t)(struct msghdr *)msg', 299 272 'len': '1', 300 273 'msg_flags': 'flags' … … 303 276 Call('RECVMSG', 'ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags)', { 304 277 'fd': 'sockfd', 305 'addr': '( __u64)(struct msghdr *)msg',278 'addr': '(uintptr_t)(struct msghdr *)msg', 306 279 'len': '1', 307 280 'msg_flags': 'flags' … … 310 283 Call('SEND', 'ssize_t send(int sockfd, const void *buf, size_t len, int flags)', { 311 284 'fd': 'sockfd', 312 'addr': '( __u64)buf',285 'addr': '(uintptr_t)buf', 313 286 'len': 'len', 314 287 'msg_flags': 'flags' … … 317 290 Call('RECV', 'ssize_t recv(int sockfd, void *buf, size_t len, int flags)', { 318 291 'fd': 'sockfd', 319 'addr': '( __u64)buf',292 'addr': '(uintptr_t)buf', 320 293 'len': 'len', 321 294 'msg_flags': 'flags' … … 324 297 Call('ACCEPT', 'int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags)', { 325 298 'fd': 'sockfd', 326 'addr': '( __u64)addr',327 'addr2': '( __u64)addrlen',299 'addr': '(uintptr_t)addr', 300 'addr2': '(uintptr_t)addrlen', 328 301 'accept_flags': 'flags' 329 302 }), … … 331 304 Call('CONNECT', 'int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)', { 332 305 'fd': 'sockfd', 333 'addr': '( __u64)addr',306 'addr': '(uintptr_t)addr', 334 307 'off': 'addrlen' 335 308 }), … … 337 310 Call('FALLOCATE', 'int fallocate(int fd, int mode, off_t offset, off_t len)', { 338 311 'fd': 'fd', 339 'addr': '( __u64)len',312 'addr': '(uintptr_t)len', 340 313 'len': 'mode', 341 314 'off': 'offset' … … 350 323 # CFA_HAVE_IORING_OP_MADVISE 351 324 Call('MADVISE', 'int madvise(void *addr, size_t length, int advice)', { 352 'addr': '( __u64)addr',325 'addr': '(uintptr_t)addr', 353 326 'len': 'length', 354 327 'fadvise_advice': 'advice' … … 357 330 Call('OPENAT', 'int openat(int dirfd, const char *pathname, int flags, mode_t mode)', { 358 331 'fd': 'dirfd', 359 'addr': '( __u64)pathname',332 'addr': '(uintptr_t)pathname', 360 333 'len': 'mode', 361 334 'open_flags': 'flags;' … … 366 339 'addr': 'pathname', 367 340 'len': 'sizeof(*how)', 368 'off': '( __u64)how',341 'off': '(uintptr_t)how', 369 342 }, define = 'CFA_HAVE_OPENAT2'), 370 343 # CFA_HAVE_IORING_OP_CLOSE … … 375 348 Call('STATX', 'int statx(int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf)', { 376 349 'fd': 'dirfd', 377 'off': '( __u64)statxbuf',350 'off': '(uintptr_t)statxbuf', 378 351 'addr': 'pathname', 379 352 'len': 'mask', … … 383 356 Call('READ', 'ssize_t read(int fd, void * buf, size_t count)', { 384 357 'fd': 'fd', 385 'addr': '( __u64)buf',358 'addr': '(uintptr_t)buf', 386 359 'len': 'count' 387 360 }), … … 389 362 Call('WRITE', 'ssize_t write(int fd, void * buf, size_t count)', { 390 363 'fd': 'fd', 391 'addr': '( __u64)buf',364 'addr': '(uintptr_t)buf', 392 365 'len': 'count' 393 366 }), 394 367 # CFA_HAVE_IORING_OP_SPLICE 395 Call('SPLICE', 'ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags)', {368 Call('SPLICE', 'ssize_t splice(int fd_in, __off64_t *off_in, int fd_out, __off64_t *off_out, size_t len, unsigned int flags)', { 396 369 'splice_fd_in': 'fd_in', 397 370 'splice_off_in': 'off_in ? (__u64)*off_in : (__u64)-1', … … 415 388 if c.define: 416 389 print("""#if defined({define}) 417 {ret} cfa_{name}({params}, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);390 {ret} cfa_{name}({params}, __u64 submit_flags); 418 391 #endif""".format(define=c.define,ret=c.ret, name=c.name, params=c.params)) 419 392 else: 420 print("{ret} cfa_{name}({params}, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);"393 print("{ret} cfa_{name}({params}, __u64 submit_flags);" 421 394 .format(ret=c.ret, name=c.name, params=c.params)) 422 395 … … 426 399 if c.define: 427 400 print("""#if defined({define}) 428 void async_{name}(io_future_t & future, {params}, int submit_flags, io_cancellation * cancellation, io_context * context);401 void async_{name}(io_future_t & future, {params}, __u64 submit_flags); 429 402 #endif""".format(define=c.define,name=c.name, params=c.params)) 430 403 else: 431 print("void async_{name}(io_future_t & future, {params}, int submit_flags, io_cancellation * cancellation, io_context * context);"404 print("void async_{name}(io_future_t & future, {params}, __u64 submit_flags);" 432 405 .format(name=c.name, params=c.params)) 433 406 print("\n") … … 474 447 475 448 print(""" 476 //-----------------------------------------------------------------------------477 bool cancel(io_cancellation & this) {478 #if !defined(CFA_HAVE_LINUX_IO_URING_H) || !defined(CFA_HAVE_IORING_OP_ASYNC_CANCEL)479 return false;480 #else481 io_future_t future;482 483 io_context * context = __get_io_context();484 485 __u8 sflags = 0;486 struct __io_data & ring = *context->thrd.ring;487 488 __u32 idx;489 volatile struct io_uring_sqe * sqe;490 [sqe, idx] = __submit_alloc( ring, (__u64)(uintptr_t)&future );491 492 sqe->__pad2[0] = sqe->__pad2[1] = sqe->__pad2[2] = 0;493 sqe->opcode = IORING_OP_ASYNC_CANCEL;494 sqe->flags = sflags;495 sqe->addr = this.target;496 497 verify( sqe->user_data == (__u64)(uintptr_t)&future );498 __submit( context, idx );499 500 wait(future);501 502 if( future.result == 0 ) return true; // Entry found503 if( future.result == -EALREADY) return true; // Entry found but in progress504 if( future.result == -ENOENT ) return false; // Entry not found505 return false;506 #endif507 }508 509 449 //----------------------------------------------------------------------------- 510 450 // Check if a function is has asynchronous -
libcfa/src/concurrency/io/setup.cfa
rfeacef9 r5407cdc 26 26 27 27 #if !defined(CFA_HAVE_LINUX_IO_URING_H) 28 void __kernel_io_startup() {29 // Nothing to do without io_uring30 }31 32 void __kernel_io_shutdown() {33 // Nothing to do without io_uring34 }35 36 28 void ?{}(io_context_params & this) {} 37 29 38 void ?{}(io_context & this, struct cluster & cl) {} 39 void ?{}(io_context & this, struct cluster & cl, const io_context_params & params) {} 40 41 void ^?{}(io_context & this) {} 42 void ^?{}(io_context & this, bool cluster_context) {} 43 44 void register_fixed_files( io_context &, int *, unsigned ) {} 45 void register_fixed_files( cluster &, int *, unsigned ) {} 30 void ?{}($io_context & this, struct cluster & cl) {} 31 void ^?{}($io_context & this) {} 32 33 void __cfa_io_start( processor * proc ) {} 34 void __cfa_io_flush( processor * proc ) {} 35 void __cfa_io_stop ( processor * proc ) {} 36 37 $io_arbiter * create(void) { return 0p; } 38 void destroy($io_arbiter *) {} 46 39 47 40 #else … … 68 61 void ?{}(io_context_params & this) { 69 62 this.num_entries = 256; 70 this.num_ready = 256;71 this.submit_aff = -1;72 this.eager_submits = false;73 this.poller_submits = false;74 this.poll_submit = false;75 this.poll_complete = false;76 63 } 77 64 … … 106 93 107 94 //============================================================================================= 108 // I/O Startup / Shutdown logic + Master Poller109 //=============================================================================================110 111 // IO Master poller loop forward112 static void * iopoll_loop( __attribute__((unused)) void * args );113 114 static struct {115 pthread_t thrd; // pthread handle to io poller thread116 void * stack; // pthread stack for io poller thread117 int epollfd; // file descriptor to the epoll instance118 volatile bool run; // Whether or not to continue119 volatile bool stopped; // Whether the poller has finished running120 volatile uint64_t epoch; // Epoch used for memory reclamation121 } iopoll;122 123 void __kernel_io_startup(void) {124 __cfadbg_print_safe(io_core, "Kernel : Creating EPOLL instance\n" );125 126 iopoll.epollfd = epoll_create1(0);127 if (iopoll.epollfd == -1) {128 abort( "internal error, epoll_create1\n");129 }130 131 __cfadbg_print_safe(io_core, "Kernel : Starting io poller thread\n" );132 133 iopoll.stack = __create_pthread( &iopoll.thrd, iopoll_loop, 0p );134 iopoll.run = true;135 iopoll.stopped = false;136 iopoll.epoch = 0;137 }138 139 void __kernel_io_shutdown(void) {140 // Notify the io poller thread of the shutdown141 iopoll.run = false;142 sigval val = { 1 };143 pthread_sigqueue( iopoll.thrd, SIGUSR1, val );144 145 // Wait for the io poller thread to finish146 147 __destroy_pthread( iopoll.thrd, iopoll.stack, 0p );148 149 int ret = close(iopoll.epollfd);150 if (ret == -1) {151 abort( "internal error, close epoll\n");152 }153 154 // Io polling is now fully stopped155 156 __cfadbg_print_safe(io_core, "Kernel : IO poller stopped\n" );157 }158 159 static void * iopoll_loop( __attribute__((unused)) void * args ) {160 __processor_id_t id;161 id.full_proc = false;162 id.id = doregister(&id);163 __cfaabi_tls.this_proc_id = &id;164 __cfadbg_print_safe(io_core, "Kernel : IO poller thread starting\n" );165 166 // Block signals to control when they arrive167 sigset_t mask;168 sigfillset(&mask);169 if ( pthread_sigmask( SIG_BLOCK, &mask, 0p ) == -1 ) {170 abort( "internal error, pthread_sigmask" );171 }172 173 sigdelset( &mask, SIGUSR1 );174 175 // Create sufficient events176 struct epoll_event events[10];177 // Main loop178 while( iopoll.run ) {179 __cfadbg_print_safe(io_core, "Kernel I/O - epoll : waiting on io_uring contexts\n");180 181 // increment the epoch to notify any deleters we are starting a new cycle182 __atomic_fetch_add(&iopoll.epoch, 1, __ATOMIC_SEQ_CST);183 184 // Wait for events185 int nfds = epoll_pwait( iopoll.epollfd, events, 10, -1, &mask );186 187 __cfadbg_print_safe(io_core, "Kernel I/O - epoll : %d io contexts events, waking up\n", nfds);188 189 // Check if an error occured190 if (nfds == -1) {191 if( errno == EINTR ) continue;192 abort( "internal error, pthread_sigmask" );193 }194 195 for(i; nfds) {196 $io_ctx_thread * io_ctx = ($io_ctx_thread *)(uintptr_t)events[i].data.u64;197 /* paranoid */ verify( io_ctx );198 __cfadbg_print_safe(io_core, "Kernel I/O - epoll : Unparking io poller %d (%p)\n", io_ctx->ring->fd, io_ctx);199 #if !defined( __CFA_NO_STATISTICS__ )200 __cfaabi_tls.this_stats = io_ctx->self.curr_cluster->stats;201 #endif202 203 eventfd_t v;204 eventfd_read(io_ctx->ring->efd, &v);205 206 post( io_ctx->sem );207 }208 }209 210 __atomic_store_n(&iopoll.stopped, true, __ATOMIC_SEQ_CST);211 212 __cfadbg_print_safe(io_core, "Kernel : IO poller thread stopping\n" );213 unregister(&id);214 return 0p;215 }216 217 //=============================================================================================218 95 // I/O Context Constrution/Destruction 219 96 //============================================================================================= 220 97 221 void ?{}($io_ctx_thread & this, struct cluster & cl) { (this.self){ "IO Poller", cl }; } 222 void main( $io_ctx_thread & this ); 223 static inline $thread * get_thread( $io_ctx_thread & this ) { return &this.self; } 224 void ^?{}( $io_ctx_thread & mutex this ) {} 225 226 static void __io_create ( __io_data & this, const io_context_params & params_in ); 227 static void __io_destroy( __io_data & this ); 228 229 void ?{}(io_context & this, struct cluster & cl, const io_context_params & params) { 230 (this.thrd){ cl }; 231 this.thrd.ring = malloc(); 232 __cfadbg_print_safe(io_core, "Kernel I/O : Creating ring for io_context %p\n", &this); 233 __io_create( *this.thrd.ring, params ); 234 235 __cfadbg_print_safe(io_core, "Kernel I/O : Starting poller thread for io_context %p\n", &this); 236 this.thrd.done = false; 237 __thrd_start( this.thrd, main ); 238 239 __cfadbg_print_safe(io_core, "Kernel I/O : io_context %p ready\n", &this); 240 } 241 242 void ?{}(io_context & this, struct cluster & cl) { 243 io_context_params params; 244 (this){ cl, params }; 245 } 246 247 void ^?{}(io_context & this, bool cluster_context) { 248 __cfadbg_print_safe(io_core, "Kernel I/O : tearing down io_context %p\n", &this); 249 250 // Notify the thread of the shutdown 251 __atomic_store_n(&this.thrd.done, true, __ATOMIC_SEQ_CST); 252 253 // If this is an io_context within a cluster, things get trickier 254 $thread & thrd = this.thrd.self; 255 if( cluster_context ) { 256 // We are about to do weird things with the threads 257 // we don't need interrupts to complicate everything 258 disable_interrupts(); 259 260 // Get cluster info 261 cluster & cltr = *thrd.curr_cluster; 262 /* paranoid */ verify( cltr.idles.total == 0 || &cltr == mainCluster ); 263 /* paranoid */ verify( !ready_mutate_islocked() ); 264 265 // We need to adjust the clean-up based on where the thread is 266 if( thrd.state == Ready || thrd.preempted != __NO_PREEMPTION ) { 267 // This is the tricky case 268 // The thread was preempted or ready to run and now it is on the ready queue 269 // but the cluster is shutting down, so there aren't any processors to run the ready queue 270 // the solution is to steal the thread from the ready-queue and pretend it was blocked all along 271 272 ready_schedule_lock(); 273 // The thread should on the list 274 /* paranoid */ verify( thrd.link.next != 0p ); 275 276 // Remove the thread from the ready queue of this cluster 277 // The thread should be the last on the list 278 __attribute__((unused)) bool removed = remove_head( &cltr, &thrd ); 279 /* paranoid */ verify( removed ); 280 thrd.link.next = 0p; 281 thrd.link.prev = 0p; 282 283 // Fixup the thread state 284 thrd.state = Blocked; 285 thrd.ticket = TICKET_BLOCKED; 286 thrd.preempted = __NO_PREEMPTION; 287 288 ready_schedule_unlock(); 289 290 // Pretend like the thread was blocked all along 291 } 292 // !!! This is not an else if !!! 293 // Ok, now the thread is blocked (whether we cheated to get here or not) 294 if( thrd.state == Blocked ) { 295 // This is the "easy case" 296 // The thread is parked and can easily be moved to active cluster 297 verify( thrd.curr_cluster != active_cluster() || thrd.curr_cluster == mainCluster ); 298 thrd.curr_cluster = active_cluster(); 299 300 // unpark the fast io_poller 301 unpark( &thrd ); 302 } 303 else { 304 // The thread is in a weird state 305 // I don't know what to do here 306 abort("io_context poller thread is in unexpected state, cannot clean-up correctly\n"); 307 } 308 309 // The weird thread kidnapping stuff is over, restore interrupts. 310 enable_interrupts( __cfaabi_dbg_ctx ); 311 } else { 312 post( this.thrd.sem ); 313 } 314 315 ^(this.thrd){}; 316 __cfadbg_print_safe(io_core, "Kernel I/O : Stopped poller thread for io_context %p\n", &this); 317 318 __io_destroy( *this.thrd.ring ); 319 __cfadbg_print_safe(io_core, "Kernel I/O : Destroyed ring for io_context %p\n", &this); 320 321 free(this.thrd.ring); 322 } 323 324 void ^?{}(io_context & this) { 325 ^(this){ false }; 98 99 100 static void __io_uring_setup ( $io_context & this, const io_context_params & params_in, int procfd ); 101 static void __io_uring_teardown( $io_context & this ); 102 static void __epoll_register($io_context & ctx); 103 static void __epoll_unregister($io_context & ctx); 104 void __ioarbiter_register( $io_arbiter & mutex, $io_context & ctx ); 105 void __ioarbiter_unregister( $io_arbiter & mutex, $io_context & ctx ); 106 107 void ?{}($io_context & this, processor * proc, struct cluster & cl) { 108 /* paranoid */ verify( cl.io.arbiter ); 109 this.proc = proc; 110 this.arbiter = cl.io.arbiter; 111 this.ext_sq.empty = true; 112 (this.ext_sq.queue){}; 113 __io_uring_setup( this, cl.io.params, proc->idle ); 114 __cfadbg_print_safe(io_core, "Kernel I/O : Created ring for io_context %u (%p)\n", this.fd, &this); 115 } 116 117 void ^?{}($io_context & this) { 118 __cfadbg_print_safe(io_core, "Kernel I/O : tearing down io_context %u\n", this.fd); 119 120 __io_uring_teardown( this ); 121 __cfadbg_print_safe(io_core, "Kernel I/O : Destroyed ring for io_context %u\n", this.fd); 326 122 } 327 123 … … 329 125 extern void __enable_interrupts_hard(); 330 126 331 static void __io_ create( __io_data & this, const io_context_params & params_in) {127 static void __io_uring_setup( $io_context & this, const io_context_params & params_in, int procfd ) { 332 128 // Step 1 : call to setup 333 129 struct io_uring_params params; 334 130 memset(¶ms, 0, sizeof(params)); 335 if( params_in.poll_submit ) params.flags |= IORING_SETUP_SQPOLL;336 if( params_in.poll_complete ) params.flags |= IORING_SETUP_IOPOLL;131 // if( params_in.poll_submit ) params.flags |= IORING_SETUP_SQPOLL; 132 // if( params_in.poll_complete ) params.flags |= IORING_SETUP_IOPOLL; 337 133 338 134 __u32 nentries = params_in.num_entries != 0 ? params_in.num_entries : 256; … … 340 136 abort("ERROR: I/O setup 'num_entries' must be a power of 2\n"); 341 137 } 342 if( params_in.poller_submits && params_in.eager_submits ) {343 abort("ERROR: I/O setup 'poller_submits' and 'eager_submits' cannot be used together\n");344 }345 138 346 139 int fd = syscall(__NR_io_uring_setup, nentries, ¶ms ); … … 350 143 351 144 // Step 2 : mmap result 352 memset( &this, 0, sizeof(struct __io_data) ); 353 struct __submition_data & sq = this.submit_q; 354 struct __completion_data & cq = this.completion_q; 145 struct __sub_ring_t & sq = this.sq; 146 struct __cmp_ring_t & cq = this.cq; 355 147 356 148 // calculate the right ring size … … 401 193 // Get the pointers from the kernel to fill the structure 402 194 // submit queue 403 sq.head = (volatile __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.head); 404 sq.tail = (volatile __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.tail); 405 sq.mask = ( const __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.ring_mask); 406 sq.num = ( const __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.ring_entries); 407 sq.flags = ( __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.flags); 408 sq.dropped = ( __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.dropped); 409 sq.array = ( __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.array); 410 sq.prev_head = *sq.head; 411 412 { 413 const __u32 num = *sq.num; 414 for( i; num ) { 415 __sqe_clean( &sq.sqes[i] ); 416 } 417 } 418 419 (sq.submit_lock){}; 420 (sq.release_lock){}; 421 422 if( params_in.poller_submits || params_in.eager_submits ) { 423 /* paranoid */ verify( is_pow2( params_in.num_ready ) || (params_in.num_ready < 8) ); 424 sq.ready_cnt = max( params_in.num_ready, 8 ); 425 sq.ready = alloc( sq.ready_cnt, 64`align ); 426 for(i; sq.ready_cnt) { 427 sq.ready[i] = -1ul32; 428 } 429 sq.prev_ready = 0; 430 } 431 else { 432 sq.ready_cnt = 0; 433 sq.ready = 0p; 434 sq.prev_ready = 0; 435 } 195 sq.kring.head = (volatile __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.head); 196 sq.kring.tail = (volatile __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.tail); 197 sq.kring.array = ( __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.array); 198 sq.mask = ( const __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.ring_mask); 199 sq.num = ( const __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.ring_entries); 200 sq.flags = ( __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.flags); 201 sq.dropped = ( __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.dropped); 202 203 sq.kring.released = 0; 204 205 sq.free_ring.head = 0; 206 sq.free_ring.tail = *sq.num; 207 sq.free_ring.array = alloc( *sq.num, 128`align ); 208 for(i; (__u32)*sq.num) { 209 sq.free_ring.array[i] = i; 210 } 211 212 sq.to_submit = 0; 436 213 437 214 // completion queue … … 446 223 // io_uring_register is so f*cking slow on some machine that it 447 224 // will never succeed if preemption isn't hard blocked 225 __cfadbg_print_safe(io_core, "Kernel I/O : registering %d for completion with ring %d\n", procfd, fd); 226 448 227 __disable_interrupts_hard(); 449 228 450 int efd = eventfd(0, 0); 451 if (efd < 0) { 452 abort("KERNEL ERROR: IO_URING EVENTFD - %s\n", strerror(errno)); 453 } 454 455 int ret = syscall( __NR_io_uring_register, fd, IORING_REGISTER_EVENTFD, &efd, 1); 229 int ret = syscall( __NR_io_uring_register, fd, IORING_REGISTER_EVENTFD, &procfd, 1); 456 230 if (ret < 0) { 457 231 abort("KERNEL ERROR: IO_URING EVENTFD REGISTER - %s\n", strerror(errno)); … … 459 233 460 234 __enable_interrupts_hard(); 235 236 __cfadbg_print_safe(io_core, "Kernel I/O : registered %d for completion with ring %d\n", procfd, fd); 461 237 462 238 // some paranoid checks … … 468 244 /* paranoid */ verifyf( (*sq.mask) == ((*sq.num) - 1ul32), "IO_URING Expected mask to be %u (%u entries), was %u", (*sq.num) - 1ul32, *sq.num, *sq.mask ); 469 245 /* paranoid */ verifyf( (*sq.num) >= nentries, "IO_URING Expected %u entries, got %u", nentries, *sq.num ); 470 /* paranoid */ verifyf( (*sq. head) == 0, "IO_URING Expected head to be 0, got %u", *sq.head );471 /* paranoid */ verifyf( (*sq. tail) == 0, "IO_URING Expected tail to be 0, got %u", *sq.tail );246 /* paranoid */ verifyf( (*sq.kring.head) == 0, "IO_URING Expected head to be 0, got %u", *sq.kring.head ); 247 /* paranoid */ verifyf( (*sq.kring.tail) == 0, "IO_URING Expected tail to be 0, got %u", *sq.kring.tail ); 472 248 473 249 // Update the global ring info 474 this.ring_flags = params.flags;250 this.ring_flags = 0; 475 251 this.fd = fd; 476 this.efd = efd; 477 this.eager_submits = params_in.eager_submits; 478 this.poller_submits = params_in.poller_submits; 479 } 480 481 static void __io_destroy( __io_data & this ) { 252 } 253 254 static void __io_uring_teardown( $io_context & this ) { 482 255 // Shutdown the io rings 483 struct __sub mition_data & sq = this.submit_q;484 struct __c ompletion_data & cq = this.completion_q;256 struct __sub_ring_t & sq = this.sq; 257 struct __cmp_ring_t & cq = this.cq; 485 258 486 259 // unmap the submit queue entries … … 497 270 // close the file descriptor 498 271 close(this.fd); 499 close(this.efd); 500 501 free( this.submit_q.ready ); // Maybe null, doesn't matter 272 273 free( this.sq.free_ring.array ); // Maybe null, doesn't matter 274 } 275 276 void __cfa_io_start( processor * proc ) { 277 proc->io.ctx = alloc(); 278 (*proc->io.ctx){proc, *proc->cltr}; 279 } 280 void __cfa_io_stop ( processor * proc ) { 281 ^(*proc->io.ctx){}; 282 free(proc->io.ctx); 502 283 } 503 284 … … 505 286 // I/O Context Sleep 506 287 //============================================================================================= 507 static inline void __ioctx_epoll_ctl($io_ctx_thread & ctx, int op, const char * error) { 508 struct epoll_event ev; 509 ev.events = EPOLLIN | EPOLLONESHOT; 510 ev.data.u64 = (__u64)&ctx; 511 int ret = epoll_ctl(iopoll.epollfd, op, ctx.ring->efd, &ev); 512 if (ret < 0) { 513 abort( "KERNEL ERROR: EPOLL %s - (%d) %s\n", error, (int)errno, strerror(errno) ); 514 } 515 } 516 517 void __ioctx_register($io_ctx_thread & ctx) { 518 __ioctx_epoll_ctl(ctx, EPOLL_CTL_ADD, "ADD"); 519 } 520 521 void __ioctx_prepare_block($io_ctx_thread & ctx) { 522 __cfadbg_print_safe(io_core, "Kernel I/O - epoll : Re-arming io poller %d (%p)\n", ctx.ring->fd, &ctx); 523 __ioctx_epoll_ctl(ctx, EPOLL_CTL_MOD, "REARM"); 524 } 525 526 void __ioctx_unregister($io_ctx_thread & ctx) { 527 // Read the current epoch so we know when to stop 528 size_t curr = __atomic_load_n(&iopoll.epoch, __ATOMIC_SEQ_CST); 529 530 // Remove the fd from the iopoller 531 __ioctx_epoll_ctl(ctx, EPOLL_CTL_DEL, "REMOVE"); 532 533 // Notify the io poller thread of the shutdown 534 iopoll.run = false; 535 sigval val = { 1 }; 536 pthread_sigqueue( iopoll.thrd, SIGUSR1, val ); 537 538 // Make sure all this is done 539 __atomic_thread_fence(__ATOMIC_SEQ_CST); 540 541 // Wait for the next epoch 542 while(curr == iopoll.epoch && !iopoll.stopped) Pause(); 543 } 288 // static inline void __epoll_ctl($io_context & ctx, int op, const char * error) { 289 // struct epoll_event ev; 290 // ev.events = EPOLLIN | EPOLLONESHOT; 291 // ev.data.u64 = (__u64)&ctx; 292 // int ret = epoll_ctl(iopoll.epollfd, op, ctx.efd, &ev); 293 // if (ret < 0) { 294 // abort( "KERNEL ERROR: EPOLL %s - (%d) %s\n", error, (int)errno, strerror(errno) ); 295 // } 296 // } 297 298 // static void __epoll_register($io_context & ctx) { 299 // __epoll_ctl(ctx, EPOLL_CTL_ADD, "ADD"); 300 // } 301 302 // static void __epoll_unregister($io_context & ctx) { 303 // // Read the current epoch so we know when to stop 304 // size_t curr = __atomic_load_n(&iopoll.epoch, __ATOMIC_SEQ_CST); 305 306 // // Remove the fd from the iopoller 307 // __epoll_ctl(ctx, EPOLL_CTL_DEL, "REMOVE"); 308 309 // // Notify the io poller thread of the shutdown 310 // iopoll.run = false; 311 // sigval val = { 1 }; 312 // pthread_sigqueue( iopoll.thrd, SIGUSR1, val ); 313 314 // // Make sure all this is done 315 // __atomic_thread_fence(__ATOMIC_SEQ_CST); 316 317 // // Wait for the next epoch 318 // while(curr == iopoll.epoch && !iopoll.stopped) Pause(); 319 // } 320 321 // void __ioctx_prepare_block($io_context & ctx) { 322 // __cfadbg_print_safe(io_core, "Kernel I/O - epoll : Re-arming io poller %d (%p)\n", ctx.fd, &ctx); 323 // __epoll_ctl(ctx, EPOLL_CTL_MOD, "REARM"); 324 // } 325 544 326 545 327 //============================================================================================= 546 328 // I/O Context Misc Setup 547 329 //============================================================================================= 548 void register_fixed_files( io_context & ctx, int * files, unsigned count ) { 549 int ret = syscall( __NR_io_uring_register, ctx.thrd.ring->fd, IORING_REGISTER_FILES, files, count ); 550 if( ret < 0 ) { 551 abort( "KERNEL ERROR: IO_URING REGISTER - (%d) %s\n", (int)errno, strerror(errno) ); 552 } 553 554 __cfadbg_print_safe( io_core, "Kernel I/O : Performed io_register for %p, returned %d\n", active_thread(), ret ); 555 } 556 557 void register_fixed_files( cluster & cltr, int * files, unsigned count ) { 558 for(i; cltr.io.cnt) { 559 register_fixed_files( cltr.io.ctxs[i], files, count ); 560 } 561 } 330 void ?{}( $io_arbiter & this ) { 331 this.pending.empty = true; 332 } 333 334 void ^?{}( $io_arbiter & this ) {} 335 336 $io_arbiter * create(void) { 337 return new(); 338 } 339 void destroy($io_arbiter * arbiter) { 340 delete(arbiter); 341 } 342 343 //============================================================================================= 344 // I/O Context Misc Setup 345 //============================================================================================= 346 562 347 #endif -
libcfa/src/concurrency/io/types.hfa
rfeacef9 r5407cdc 22 22 23 23 #include "bits/locks.hfa" 24 #include "bits/queue.hfa" 24 25 #include "kernel/fwd.hfa" 25 26 26 27 #if defined(CFA_HAVE_LINUX_IO_URING_H) 27 #define LEADER_LOCK 28 struct __leaderlock_t { 29 struct $thread * volatile value; // ($thread) next_leader | (bool:1) is_locked 30 }; 28 #include "bits/sequence.hfa" 29 #include "monitor.hfa" 31 30 32 static inline void ?{}( __leaderlock_t & this ) { this.value = 0p; } 31 struct processor; 32 monitor $io_arbiter; 33 33 34 34 //----------------------------------------------------------------------- 35 35 // Ring Data structure 36 struct __submition_data { 37 // Head and tail of the ring (associated with array) 38 volatile __u32 * head; 39 volatile __u32 * tail; 40 volatile __u32 prev_head; 36 struct __sub_ring_t { 37 struct { 38 // Head and tail of the ring (associated with array) 39 volatile __u32 * head; // one passed last index consumed by the kernel 40 volatile __u32 * tail; // one passed last index visible to the kernel 41 volatile __u32 released; // one passed last index released back to the free list 41 42 42 // The actual kernel ring which uses head/tail 43 // indexes into the sqes arrays 44 __u32 * array; 43 // The actual kernel ring which uses head/tail 44 // indexes into the sqes arrays 45 __u32 * array; 46 } kring; 47 48 struct { 49 volatile __u32 head; 50 volatile __u32 tail; 51 // The ring which contains free allocations 52 // indexes into the sqes arrays 53 __u32 * array; 54 } free_ring; 55 56 // number of sqes to submit on next system call. 57 __u32 to_submit; 45 58 46 59 // number of entries and mask to go with it … … 48 61 const __u32 * mask; 49 62 50 // Submission flags (Not sure what for)63 // Submission flags, currently only IORING_SETUP_SQPOLL 51 64 __u32 * flags; 52 65 53 // number of sqes not submitted (whatever that means) 66 // number of sqes not submitted 67 // From documentation : [dropped] is incremented for each invalid submission queue entry encountered in the ring buffer. 54 68 __u32 * dropped; 55 69 56 // Like head/tail but not seen by the kernel57 volatile __u32 * ready;58 __u32 ready_cnt;59 __u32 prev_ready;60 61 #if defined(LEADER_LOCK)62 __leaderlock_t submit_lock;63 #else64 __spinlock_t submit_lock;65 #endif66 __spinlock_t release_lock;67 68 70 // A buffer of sqes (not the actual ring) 69 volatilestruct io_uring_sqe * sqes;71 struct io_uring_sqe * sqes; 70 72 71 73 // The location and size of the mmaped area … … 74 76 }; 75 77 76 struct __c ompletion_data{78 struct __cmp_ring_t { 77 79 // Head and tail of the ring 78 80 volatile __u32 * head; … … 83 85 const __u32 * num; 84 86 85 // number of cqes not submitted (whatever that means)87 // I don't know what this value is for 86 88 __u32 * overflow; 87 89 … … 94 96 }; 95 97 96 struct __io_data { 97 struct __submition_data submit_q; 98 struct __completion_data completion_q; 98 struct __outstanding_io { 99 inline Colable; 100 single_sem sem; 101 }; 102 static inline __outstanding_io *& Next( __outstanding_io * n ) { return (__outstanding_io *)Next( (Colable *)n ); } 103 104 struct __outstanding_io_queue { 105 __spinlock_t lock; 106 Queue(__outstanding_io) queue; 107 volatile bool empty; 108 }; 109 110 struct __external_io { 111 inline __outstanding_io; 112 __u32 * idxs; 113 __u32 have; 114 bool lazy; 115 }; 116 117 118 struct __attribute__((aligned(128))) $io_context { 119 $io_arbiter * arbiter; 120 processor * proc; 121 122 __outstanding_io_queue ext_sq; 123 124 struct __sub_ring_t sq; 125 struct __cmp_ring_t cq; 99 126 __u32 ring_flags; 100 127 int fd; 101 int efd; 102 bool eager_submits:1; 103 bool poller_submits:1; 128 }; 129 130 struct __pending_alloc { 131 inline __outstanding_io; 132 __u32 * idxs; 133 __u32 want; 134 $io_context * ctx; 135 }; 136 137 struct __attribute__((aligned(128))) $io_arbiter { 138 __outstanding_io_queue pending; 104 139 }; 105 140 … … 133 168 #endif 134 169 135 struct $io_ctx_thread; 136 void __ioctx_register($io_ctx_thread & ctx); 137 void __ioctx_unregister($io_ctx_thread & ctx); 138 void __ioctx_prepare_block($io_ctx_thread & ctx); 139 void __sqe_clean( volatile struct io_uring_sqe * sqe ); 170 // void __ioctx_prepare_block($io_context & ctx); 140 171 #endif 141 172 … … 148 179 149 180 static inline { 150 bool fulfil( io_future_t & this, __s32 result) {181 $thread * fulfil( io_future_t & this, __s32 result, bool do_unpark = true ) { 151 182 this.result = result; 152 return fulfil(this.self );183 return fulfil(this.self, do_unpark); 153 184 } 154 185 -
libcfa/src/concurrency/iofwd.hfa
rfeacef9 r5407cdc 18 18 #include <unistd.h> 19 19 extern "C" { 20 #include < sys/types.h>20 #include <asm/types.h> 21 21 #if CFA_HAVE_LINUX_IO_URING_H 22 22 #include <linux/io_uring.h> … … 48 48 struct cluster; 49 49 struct io_future_t; 50 struct io_context; 51 struct io_cancellation; 50 struct $io_context; 52 51 53 52 struct iovec; … … 55 54 struct sockaddr; 56 55 struct statx; 56 struct epoll_event; 57 58 struct io_uring_sqe; 59 60 //---------- 61 // underlying calls 62 extern struct $io_context * cfa_io_allocate(struct io_uring_sqe * out_sqes[], __u32 out_idxs[], __u32 want) __attribute__((nonnull (1,2))); 63 extern void cfa_io_submit( struct $io_context * in_ctx, __u32 in_idxs[], __u32 have, bool lazy ) __attribute__((nonnull (1,2))); 57 64 58 65 //---------- 59 66 // synchronous calls 60 67 #if defined(CFA_HAVE_PREADV2) 61 extern ssize_t cfa_preadv2(int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);68 extern ssize_t cfa_preadv2(int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags, __u64 submit_flags); 62 69 #endif 63 70 #if defined(CFA_HAVE_PWRITEV2) 64 extern ssize_t cfa_pwritev2(int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);71 extern ssize_t cfa_pwritev2(int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags, __u64 submit_flags); 65 72 #endif 66 extern int cfa_fsync(int fd, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);67 extern int cfa_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);68 extern int cfa_sync_file_range(int fd, off64_t offset, off64_t nbytes, unsigned int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);69 extern ssize_t cfa_sendmsg(int sockfd, const struct msghdr *msg, int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);70 extern ssize_t cfa_recvmsg(int sockfd, struct msghdr *msg, int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);71 extern ssize_t cfa_send(int sockfd, const void *buf, size_t len, int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);72 extern ssize_t cfa_recv(int sockfd, void *buf, size_t len, int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);73 extern int cfa_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);74 extern int cfa_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);75 extern int cfa_fallocate(int fd, int mode, off_t offset, off_t len, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);76 extern int cfa_posix_fadvise(int fd, off_t offset, off_t len, int advice, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);77 extern int cfa_madvise(void *addr, size_t length, int advice, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);78 extern int cfa_openat(int dirfd, const char *pathname, int flags, mode_t mode, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);73 extern int cfa_fsync(int fd, __u64 submit_flags); 74 extern int cfa_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event, __u64 submit_flags); 75 extern int cfa_sync_file_range(int fd, off64_t offset, off64_t nbytes, unsigned int flags, __u64 submit_flags); 76 extern ssize_t cfa_sendmsg(int sockfd, const struct msghdr *msg, int flags, __u64 submit_flags); 77 extern ssize_t cfa_recvmsg(int sockfd, struct msghdr *msg, int flags, __u64 submit_flags); 78 extern ssize_t cfa_send(int sockfd, const void *buf, size_t len, int flags, __u64 submit_flags); 79 extern ssize_t cfa_recv(int sockfd, void *buf, size_t len, int flags, __u64 submit_flags); 80 extern int cfa_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags, __u64 submit_flags); 81 extern int cfa_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen, __u64 submit_flags); 82 extern int cfa_fallocate(int fd, int mode, off_t offset, off_t len, __u64 submit_flags); 83 extern int cfa_posix_fadvise(int fd, off_t offset, off_t len, int advice, __u64 submit_flags); 84 extern int cfa_madvise(void *addr, size_t length, int advice, __u64 submit_flags); 85 extern int cfa_openat(int dirfd, const char *pathname, int flags, mode_t mode, __u64 submit_flags); 79 86 #if defined(CFA_HAVE_OPENAT2) 80 extern int cfa_openat2(int dirfd, const char *pathname, struct open_how * how, size_t size, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);87 extern int cfa_openat2(int dirfd, const char *pathname, struct open_how * how, size_t size, __u64 submit_flags); 81 88 #endif 82 extern int cfa_close(int fd, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);89 extern int cfa_close(int fd, __u64 submit_flags); 83 90 #if defined(CFA_HAVE_STATX) 84 extern int cfa_statx(int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);91 extern int cfa_statx(int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf, __u64 submit_flags); 85 92 #endif 86 extern ssize_t cfa_read(int fd, void * buf, size_t count, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);87 extern ssize_t cfa_write(int fd, void * buf, size_t count, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);88 extern ssize_t cfa_splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);89 extern ssize_t cfa_tee(int fd_in, int fd_out, size_t len, unsigned int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);93 extern ssize_t cfa_read(int fd, void * buf, size_t count, __u64 submit_flags); 94 extern ssize_t cfa_write(int fd, void * buf, size_t count, __u64 submit_flags); 95 extern ssize_t cfa_splice(int fd_in, __off64_t *off_in, int fd_out, __off64_t *off_out, size_t len, unsigned int flags, __u64 submit_flags); 96 extern ssize_t cfa_tee(int fd_in, int fd_out, size_t len, unsigned int flags, __u64 submit_flags); 90 97 91 98 //---------- 92 99 // asynchronous calls 93 100 #if defined(CFA_HAVE_PREADV2) 94 extern void async_preadv2(io_future_t & future, int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags, int submit_flags, io_cancellation * cancellation, io_context * context);101 extern void async_preadv2(io_future_t & future, int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags, __u64 submit_flags); 95 102 #endif 96 103 #if defined(CFA_HAVE_PWRITEV2) 97 extern void async_pwritev2(io_future_t & future, int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags, int submit_flags, io_cancellation * cancellation, io_context * context);104 extern void async_pwritev2(io_future_t & future, int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags, __u64 submit_flags); 98 105 #endif 99 extern void async_fsync(io_future_t & future, int fd, int submit_flags, io_cancellation * cancellation, io_context * context);100 extern void async_epoll_ctl(io_future_t & future, int epfd, int op, int fd, struct epoll_event *event, int submit_flags, io_cancellation * cancellation, io_context * context);101 extern void async_sync_file_range(io_future_t & future, int fd, off64_t offset, off64_t nbytes, unsigned int flags, int submit_flags, io_cancellation * cancellation, io_context * context);102 extern void async_sendmsg(io_future_t & future, int sockfd, const struct msghdr *msg, int flags, int submit_flags, io_cancellation * cancellation, io_context * context);103 extern void async_recvmsg(io_future_t & future, int sockfd, struct msghdr *msg, int flags, int submit_flags, io_cancellation * cancellation, io_context * context);104 extern void async_send(io_future_t & future, int sockfd, const void *buf, size_t len, int flags, int submit_flags, io_cancellation * cancellation, io_context * context);105 extern void async_recv(io_future_t & future, int sockfd, void *buf, size_t len, int flags, int submit_flags, io_cancellation * cancellation, io_context * context);106 extern void async_accept4(io_future_t & future, int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags, int submit_flags, io_cancellation * cancellation, io_context * context);107 extern void async_connect(io_future_t & future, int sockfd, const struct sockaddr *addr, socklen_t addrlen, int submit_flags, io_cancellation * cancellation, io_context * context);108 extern void async_fallocate(io_future_t & future, int fd, int mode, off_t offset, off_t len, int submit_flags, io_cancellation * cancellation, io_context * context);109 extern void async_posix_fadvise(io_future_t & future, int fd, off_t offset, off_t len, int advice, int submit_flags, io_cancellation * cancellation, io_context * context);110 extern void async_madvise(io_future_t & future, void *addr, size_t length, int advice, int submit_flags, io_cancellation * cancellation, io_context * context);111 extern void async_openat(io_future_t & future, int dirfd, const char *pathname, int flags, mode_t mode, int submit_flags, io_cancellation * cancellation, io_context * context);106 extern void async_fsync(io_future_t & future, int fd, __u64 submit_flags); 107 extern void async_epoll_ctl(io_future_t & future, int epfd, int op, int fd, struct epoll_event *event, __u64 submit_flags); 108 extern void async_sync_file_range(io_future_t & future, int fd, off64_t offset, off64_t nbytes, unsigned int flags, __u64 submit_flags); 109 extern void async_sendmsg(io_future_t & future, int sockfd, const struct msghdr *msg, int flags, __u64 submit_flags); 110 extern void async_recvmsg(io_future_t & future, int sockfd, struct msghdr *msg, int flags, __u64 submit_flags); 111 extern void async_send(io_future_t & future, int sockfd, const void *buf, size_t len, int flags, __u64 submit_flags); 112 extern void async_recv(io_future_t & future, int sockfd, void *buf, size_t len, int flags, __u64 submit_flags); 113 extern void async_accept4(io_future_t & future, int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags, __u64 submit_flags); 114 extern void async_connect(io_future_t & future, int sockfd, const struct sockaddr *addr, socklen_t addrlen, __u64 submit_flags); 115 extern void async_fallocate(io_future_t & future, int fd, int mode, off_t offset, off_t len, __u64 submit_flags); 116 extern void async_posix_fadvise(io_future_t & future, int fd, off_t offset, off_t len, int advice, __u64 submit_flags); 117 extern void async_madvise(io_future_t & future, void *addr, size_t length, int advice, __u64 submit_flags); 118 extern void async_openat(io_future_t & future, int dirfd, const char *pathname, int flags, mode_t mode, __u64 submit_flags); 112 119 #if defined(CFA_HAVE_OPENAT2) 113 extern void async_openat2(io_future_t & future, int dirfd, const char *pathname, struct open_how * how, size_t size, int submit_flags, io_cancellation * cancellation, io_context * context);120 extern void async_openat2(io_future_t & future, int dirfd, const char *pathname, struct open_how * how, size_t size, __u64 submit_flags); 114 121 #endif 115 extern void async_close(io_future_t & future, int fd, int submit_flags, io_cancellation * cancellation, io_context * context);122 extern void async_close(io_future_t & future, int fd, __u64 submit_flags); 116 123 #if defined(CFA_HAVE_STATX) 117 extern void async_statx(io_future_t & future, int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf, int submit_flags, io_cancellation * cancellation, io_context * context);124 extern void async_statx(io_future_t & future, int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf, __u64 submit_flags); 118 125 #endif 119 void async_read(io_future_t & future, int fd, void * buf, size_t count, int submit_flags, io_cancellation * cancellation, io_context * context);120 extern void async_write(io_future_t & future, int fd, void * buf, size_t count, int submit_flags, io_cancellation * cancellation, io_context * context);121 extern void async_splice(io_future_t & future, int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags, int submit_flags, io_cancellation * cancellation, io_context * context);122 extern void async_tee(io_future_t & future, int fd_in, int fd_out, size_t len, unsigned int flags, int submit_flags, io_cancellation * cancellation, io_context * context);126 void async_read(io_future_t & future, int fd, void * buf, size_t count, __u64 submit_flags); 127 extern void async_write(io_future_t & future, int fd, void * buf, size_t count, __u64 submit_flags); 128 extern void async_splice(io_future_t & future, int fd_in, __off64_t *off_in, int fd_out, __off64_t *off_out, size_t len, unsigned int flags, __u64 submit_flags); 129 extern void async_tee(io_future_t & future, int fd_in, int fd_out, size_t len, unsigned int flags, __u64 submit_flags); 123 130 124 131 … … 126 133 // Check if a function is blocks a only the user thread 127 134 bool has_user_level_blocking( fptr_t func ); 128 129 //-----------------------------------------------------------------------------130 void register_fixed_files( io_context & ctx , int * files, unsigned count );131 void register_fixed_files( cluster & cltr, int * files, unsigned count ); -
libcfa/src/concurrency/kernel.cfa
rfeacef9 r5407cdc 22 22 #include <signal.h> 23 23 #include <unistd.h> 24 extern "C" { 25 #include <sys/eventfd.h> 26 } 24 27 25 28 //CFA Includes … … 31 34 #include "invoke.h" 32 35 36 #if !defined(__CFA_NO_STATISTICS__) 37 #define __STATS( ...) __VA_ARGS__ 38 #else 39 #define __STATS( ...) 40 #endif 33 41 34 42 //----------------------------------------------------------------------------- … … 107 115 static $thread * __next_thread(cluster * this); 108 116 static $thread * __next_thread_slow(cluster * this); 117 static inline bool __must_unpark( $thread * thrd ) __attribute((nonnull(1))); 109 118 static void __run_thread(processor * this, $thread * dst); 110 119 static void __wake_one(cluster * cltr); 111 120 112 static void push (__cluster_idles & idles, processor & proc); 113 static void remove(__cluster_idles & idles, processor & proc); 114 static [unsigned idle, unsigned total, * processor] query( & __cluster_idles idles ); 115 121 static void mark_idle (__cluster_proc_list & idles, processor & proc); 122 static void mark_awake(__cluster_proc_list & idles, processor & proc); 123 static [unsigned idle, unsigned total, * processor] query_idles( & __cluster_proc_list idles ); 124 125 extern void __cfa_io_start( processor * ); 126 extern bool __cfa_io_drain( processor * ); 127 extern void __cfa_io_flush( processor * ); 128 extern void __cfa_io_stop ( processor * ); 129 static inline bool __maybe_io_drain( processor * ); 130 131 extern void __disable_interrupts_hard(); 132 extern void __enable_interrupts_hard(); 133 134 static inline void __disable_interrupts_checked() { 135 /* paranoid */ verify( __preemption_enabled() ); 136 disable_interrupts(); 137 /* paranoid */ verify( ! __preemption_enabled() ); 138 } 139 140 static inline void __enable_interrupts_checked( bool poll = true ) { 141 /* paranoid */ verify( ! __preemption_enabled() ); 142 enable_interrupts( poll ); 143 /* paranoid */ verify( __preemption_enabled() ); 144 } 116 145 117 146 //============================================================================================= … … 129 158 verify(this); 130 159 160 __cfa_io_start( this ); 161 131 162 __cfadbg_print_safe(runtime_core, "Kernel : core %p starting\n", this); 132 163 #if !defined(__CFA_NO_STATISTICS__) … … 140 171 preemption_scope scope = { this }; 141 172 142 #if !defined(__CFA_NO_STATISTICS__) 143 unsigned long long last_tally = rdtscl(); 144 #endif 145 173 __STATS( unsigned long long last_tally = rdtscl(); ) 174 175 // if we need to run some special setup, now is the time to do it. 176 if(this->init.thrd) { 177 this->init.thrd->curr_cluster = this->cltr; 178 __run_thread(this, this->init.thrd); 179 } 146 180 147 181 __cfadbg_print_safe(runtime_core, "Kernel : core %p started\n", this); … … 150 184 MAIN_LOOP: 151 185 for() { 186 // Check if there is pending io 187 __maybe_io_drain( this ); 188 152 189 // Try to get the next thread 153 190 readyThread = __next_thread( this->cltr ); 154 191 155 192 if( !readyThread ) { 193 __cfa_io_flush( this ); 156 194 readyThread = __next_thread_slow( this->cltr ); 157 195 } … … 167 205 168 206 // Push self to idle stack 169 push(this->cltr->idles, * this);207 mark_idle(this->cltr->procs, * this); 170 208 171 209 // Confirm the ready-queue is empty … … 173 211 if( readyThread ) { 174 212 // A thread was found, cancel the halt 175 remove(this->cltr->idles, * this);213 mark_awake(this->cltr->procs, * this); 176 214 177 215 #if !defined(__CFA_NO_STATISTICS__) … … 189 227 #endif 190 228 191 wait( this->idle ); 229 __cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle); 230 231 __disable_interrupts_hard(); 232 eventfd_t val; 233 eventfd_read( this->idle, &val ); 234 __enable_interrupts_hard(); 192 235 193 236 #if !defined(__CFA_NO_STATISTICS__) … … 198 241 199 242 // We were woken up, remove self from idle 200 remove(this->cltr->idles, * this);243 mark_awake(this->cltr->procs, * this); 201 244 202 245 // DON'T just proceed, start looking again … … 205 248 206 249 /* paranoid */ verify( readyThread ); 250 251 // Reset io dirty bit 252 this->io.dirty = false; 207 253 208 254 // We found a thread run it … … 219 265 } 220 266 #endif 267 268 if(this->io.pending && !this->io.dirty) { 269 __cfa_io_flush( this ); 270 } 271 272 // SEARCH: { 273 // /* paranoid */ verify( ! __preemption_enabled() ); 274 // /* paranoid */ verify( kernelTLS().this_proc_id ); 275 276 // // First, lock the scheduler since we are searching for a thread 277 278 // // Try to get the next thread 279 // ready_schedule_lock(); 280 // readyThread = pop_fast( this->cltr ); 281 // ready_schedule_unlock(); 282 // if(readyThread) { break SEARCH; } 283 284 // // If we can't find a thread, might as well flush any outstanding I/O 285 // if(this->io.pending) { __cfa_io_flush( this ); } 286 287 // // Spin a little on I/O, just in case 288 // for(25) { 289 // __maybe_io_drain( this ); 290 // ready_schedule_lock(); 291 // readyThread = pop_fast( this->cltr ); 292 // ready_schedule_unlock(); 293 // if(readyThread) { break SEARCH; } 294 // } 295 296 // // no luck, try stealing a few times 297 // for(25) { 298 // if( __maybe_io_drain( this ) ) { 299 // ready_schedule_lock(); 300 // readyThread = pop_fast( this->cltr ); 301 // } else { 302 // ready_schedule_lock(); 303 // readyThread = pop_slow( this->cltr ); 304 // } 305 // ready_schedule_unlock(); 306 // if(readyThread) { break SEARCH; } 307 // } 308 309 // // still no luck, search for a thread 310 // ready_schedule_lock(); 311 // readyThread = pop_search( this->cltr ); 312 // ready_schedule_unlock(); 313 // if(readyThread) { break SEARCH; } 314 315 // // Don't block if we are done 316 // if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP; 317 318 // __STATS( __tls_stats()->ready.sleep.halts++; ) 319 320 // // Push self to idle stack 321 // mark_idle(this->cltr->procs, * this); 322 323 // // Confirm the ready-queue is empty 324 // __maybe_io_drain( this ); 325 // ready_schedule_lock(); 326 // readyThread = pop_search( this->cltr ); 327 // ready_schedule_unlock(); 328 329 // if( readyThread ) { 330 // // A thread was found, cancel the halt 331 // mark_awake(this->cltr->procs, * this); 332 333 // __STATS( __tls_stats()->ready.sleep.cancels++; ) 334 335 // // continue the main loop 336 // break SEARCH; 337 // } 338 339 // __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->id, rdtscl()); ) 340 // __cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle); 341 342 // // __disable_interrupts_hard(); 343 // eventfd_t val; 344 // eventfd_read( this->idle, &val ); 345 // // __enable_interrupts_hard(); 346 347 // __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 1\n", this->id, rdtscl()); ) 348 349 // // We were woken up, remove self from idle 350 // mark_awake(this->cltr->procs, * this); 351 352 // // DON'T just proceed, start looking again 353 // continue MAIN_LOOP; 354 // } 355 356 // RUN_THREAD: 357 // /* paranoid */ verify( kernelTLS().this_proc_id ); 358 // /* paranoid */ verify( ! __preemption_enabled() ); 359 // /* paranoid */ verify( readyThread ); 360 361 // // Reset io dirty bit 362 // this->io.dirty = false; 363 364 // // We found a thread run it 365 // __run_thread(this, readyThread); 366 367 // // Are we done? 368 // if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP; 369 370 // #if !defined(__CFA_NO_STATISTICS__) 371 // unsigned long long curr = rdtscl(); 372 // if(curr > (last_tally + 500000000)) { 373 // __tally_stats(this->cltr->stats, __cfaabi_tls.this_stats); 374 // last_tally = curr; 375 // } 376 // #endif 377 378 // if(this->io.pending && !this->io.dirty) { 379 // __cfa_io_flush( this ); 380 // } 381 382 // // Check if there is pending io 383 // __maybe_io_drain( this ); 221 384 } 222 385 … … 224 387 } 225 388 389 __cfa_io_stop( this ); 390 226 391 post( this->terminated ); 392 227 393 228 394 if(this == mainProcessor) { … … 247 413 /* paranoid */ verifyf( thrd_dst->link.next == 0p, "Expected null got %p", thrd_dst->link.next ); 248 414 __builtin_prefetch( thrd_dst->context.SP ); 415 416 __cfadbg_print_safe(runtime_core, "Kernel : core %p running thread %p (%s)\n", this, thrd_dst, thrd_dst->self_cor.name); 249 417 250 418 $coroutine * proc_cor = get_coroutine(this->runner); … … 297 465 if(unlikely(thrd_dst->preempted != __NO_PREEMPTION)) { 298 466 // The thread was preempted, reschedule it and reset the flag 299 __schedule_thread( thrd_dst );467 schedule_thread$( thrd_dst ); 300 468 break RUNNING; 301 469 } … … 318 486 break RUNNING; 319 487 case TICKET_UNBLOCK: 488 #if !defined(__CFA_NO_STATISTICS__) 489 __tls_stats()->ready.threads.threads++; 490 __push_stat( __tls_stats(), __tls_stats()->ready.threads.threads, false, "Processor", this ); 491 #endif 320 492 // This is case 2, the racy case, someone tried to run this thread before it finished blocking 321 493 // In this case, just run it again. … … 330 502 proc_cor->state = Active; 331 503 504 __cfadbg_print_safe(runtime_core, "Kernel : core %p finished running thread %p\n", this, thrd_dst); 505 506 #if !defined(__CFA_NO_STATISTICS__) 507 __tls_stats()->ready.threads.threads--; 508 __push_stat( __tls_stats(), __tls_stats()->ready.threads.threads, false, "Processor", this ); 509 #endif 510 332 511 /* paranoid */ verify( ! __preemption_enabled() ); 333 512 } … … 339 518 $thread * thrd_src = kernelTLS().this_thread; 340 519 341 #if !defined(__CFA_NO_STATISTICS__) 342 struct processor * last_proc = kernelTLS().this_processor; 343 #endif 520 __STATS( thrd_src->last_proc = kernelTLS().this_processor; ) 344 521 345 522 // Run the thread on this processor … … 360 537 361 538 #if !defined(__CFA_NO_STATISTICS__) 362 if(last_proc != kernelTLS().this_processor) { 539 /* paranoid */ verify( thrd_src->last_proc != 0p ); 540 if(thrd_src->last_proc != kernelTLS().this_processor) { 363 541 __tls_stats()->ready.threads.migration++; 364 542 } … … 373 551 // Scheduler routines 374 552 // KERNEL ONLY 375 void __schedule_thread( $thread * thrd ) {553 static void __schedule_thread( $thread * thrd ) { 376 554 /* paranoid */ verify( ! __preemption_enabled() ); 377 555 /* paranoid */ verify( kernelTLS().this_proc_id ); 556 /* paranoid */ verify( ready_schedule_islocked()); 378 557 /* paranoid */ verify( thrd ); 379 558 /* paranoid */ verify( thrd->state != Halted ); … … 391 570 if (thrd->preempted == __NO_PREEMPTION) thrd->state = Ready; 392 571 572 // Dereference the thread now because once we push it, there is not guaranteed it's still valid. 573 struct cluster * cl = thrd->curr_cluster; 574 __STATS(bool outside = thrd->last_proc && thrd->last_proc != kernelTLS().this_processor; ) 575 576 // push the thread to the cluster ready-queue 577 push( cl, thrd ); 578 579 // variable thrd is no longer safe to use 580 thrd = 0xdeaddeaddeaddeadp; 581 582 // wake the cluster using the save variable. 583 __wake_one( cl ); 584 585 #if !defined(__CFA_NO_STATISTICS__) 586 if( kernelTLS().this_stats ) { 587 __tls_stats()->ready.threads.threads++; 588 if(outside) { 589 __tls_stats()->ready.threads.extunpark++; 590 } 591 __push_stat( __tls_stats(), __tls_stats()->ready.threads.threads, false, "Processor", kernelTLS().this_processor ); 592 } 593 else { 594 __atomic_fetch_add(&cl->stats->ready.threads.threads, 1, __ATOMIC_RELAXED); 595 __atomic_fetch_add(&cl->stats->ready.threads.extunpark, 1, __ATOMIC_RELAXED); 596 __push_stat( cl->stats, cl->stats->ready.threads.threads, true, "Cluster", cl ); 597 } 598 #endif 599 600 /* paranoid */ verify( ready_schedule_islocked()); 601 /* paranoid */ verify( ! __preemption_enabled() ); 602 } 603 604 void schedule_thread$( $thread * thrd ) { 393 605 ready_schedule_lock(); 394 // Dereference the thread now because once we push it, there is not guaranteed it's still valid. 395 struct cluster * cl = thrd->curr_cluster; 396 397 // push the thread to the cluster ready-queue 398 push( cl, thrd ); 399 400 // variable thrd is no longer safe to use 401 402 // wake the cluster using the save variable. 403 __wake_one( cl ); 606 __schedule_thread( thrd ); 404 607 ready_schedule_unlock(); 405 406 /* paranoid */ verify( ! __preemption_enabled() );407 608 } 408 609 … … 413 614 414 615 ready_schedule_lock(); 415 $thread * thrd = pop ( this );616 $thread * thrd = pop_fast( this ); 416 617 ready_schedule_unlock(); 417 618 … … 427 628 428 629 ready_schedule_lock(); 429 $thread * thrd = pop_slow( this ); 630 $thread * thrd; 631 for(25) { 632 thrd = pop_slow( this ); 633 if(thrd) goto RET; 634 } 635 thrd = pop_search( this ); 636 637 RET: 430 638 ready_schedule_unlock(); 431 639 … … 435 643 } 436 644 437 void unpark( $thread * thrd ) { 438 if( !thrd ) return; 439 645 static inline bool __must_unpark( $thread * thrd ) { 440 646 int old_ticket = __atomic_fetch_add(&thrd->ticket, 1, __ATOMIC_SEQ_CST); 441 647 switch(old_ticket) { 442 648 case TICKET_RUNNING: 443 649 // Wake won the race, the thread will reschedule/rerun itself 444 break;650 return false; 445 651 case TICKET_BLOCKED: 446 652 /* paranoid */ verify( ! thrd->preempted != __NO_PREEMPTION ); 447 653 /* paranoid */ verify( thrd->state == Blocked ); 448 449 { 450 /* paranoid */ verify( publicTLS_get(this_proc_id) ); 451 bool full = publicTLS_get(this_proc_id)->full_proc; 452 if(full) disable_interrupts(); 453 454 /* paranoid */ verify( ! __preemption_enabled() ); 455 456 // Wake lost the race, 457 __schedule_thread( thrd ); 458 459 /* paranoid */ verify( ! __preemption_enabled() ); 460 461 if(full) enable_interrupts( __cfaabi_dbg_ctx ); 462 /* paranoid */ verify( publicTLS_get(this_proc_id) ); 463 } 464 465 break; 654 return true; 466 655 default: 467 656 // This makes no sense, something is wrong abort … … 470 659 } 471 660 661 void __kernel_unpark( $thread * thrd ) { 662 /* paranoid */ verify( ! __preemption_enabled() ); 663 /* paranoid */ verify( ready_schedule_islocked()); 664 665 if( !thrd ) return; 666 667 if(__must_unpark(thrd)) { 668 // Wake lost the race, 669 __schedule_thread( thrd ); 670 } 671 672 /* paranoid */ verify( ready_schedule_islocked()); 673 /* paranoid */ verify( ! __preemption_enabled() ); 674 } 675 676 void unpark( $thread * thrd ) { 677 if( !thrd ) return; 678 679 if(__must_unpark(thrd)) { 680 disable_interrupts(); 681 // Wake lost the race, 682 schedule_thread$( thrd ); 683 enable_interrupts(false); 684 } 685 } 686 472 687 void park( void ) { 473 /* paranoid */ verify( __preemption_enabled() ); 474 disable_interrupts(); 475 /* paranoid */ verify( ! __preemption_enabled() ); 476 /* paranoid */ verify( kernelTLS().this_thread->preempted == __NO_PREEMPTION ); 477 478 returnToKernel(); 479 480 /* paranoid */ verify( ! __preemption_enabled() ); 481 enable_interrupts( __cfaabi_dbg_ctx ); 482 /* paranoid */ verify( __preemption_enabled() ); 688 __disable_interrupts_checked(); 689 /* paranoid */ verify( kernelTLS().this_thread->preempted == __NO_PREEMPTION ); 690 returnToKernel(); 691 __enable_interrupts_checked(); 483 692 484 693 } … … 520 729 // KERNEL ONLY 521 730 bool force_yield( __Preemption_Reason reason ) { 522 /* paranoid */ verify( __preemption_enabled() ); 523 disable_interrupts(); 524 /* paranoid */ verify( ! __preemption_enabled() ); 525 526 $thread * thrd = kernelTLS().this_thread; 527 /* paranoid */ verify(thrd->state == Active); 528 529 // SKULLDUGGERY: It is possible that we are preempting this thread just before 530 // it was going to park itself. If that is the case and it is already using the 531 // intrusive fields then we can't use them to preempt the thread 532 // If that is the case, abandon the preemption. 533 bool preempted = false; 534 if(thrd->link.next == 0p) { 535 preempted = true; 536 thrd->preempted = reason; 537 returnToKernel(); 538 } 539 540 /* paranoid */ verify( ! __preemption_enabled() ); 541 enable_interrupts_noPoll(); 542 /* paranoid */ verify( __preemption_enabled() ); 543 731 __disable_interrupts_checked(); 732 $thread * thrd = kernelTLS().this_thread; 733 /* paranoid */ verify(thrd->state == Active); 734 735 // SKULLDUGGERY: It is possible that we are preempting this thread just before 736 // it was going to park itself. If that is the case and it is already using the 737 // intrusive fields then we can't use them to preempt the thread 738 // If that is the case, abandon the preemption. 739 bool preempted = false; 740 if(thrd->link.next == 0p) { 741 preempted = true; 742 thrd->preempted = reason; 743 returnToKernel(); 744 } 745 __enable_interrupts_checked( false ); 544 746 return preempted; 545 747 } … … 557 759 unsigned idle; 558 760 unsigned total; 559 [idle, total, p] = query (this->idles);761 [idle, total, p] = query_idles(this->procs); 560 762 561 763 // If no one is sleeping, we are done … … 563 765 564 766 // We found a processor, wake it up 565 post( p->idle ); 767 eventfd_t val; 768 val = 1; 769 eventfd_write( p->idle, val ); 566 770 567 771 #if !defined(__CFA_NO_STATISTICS__) 568 __tls_stats()->ready.sleep.wakes++; 772 if( kernelTLS().this_stats ) { 773 __tls_stats()->ready.sleep.wakes++; 774 } 775 else { 776 __atomic_fetch_add(&this->stats->ready.sleep.wakes, 1, __ATOMIC_RELAXED); 777 } 569 778 #endif 570 779 … … 579 788 __cfadbg_print_safe(runtime_core, "Kernel : waking Processor %p\n", this); 580 789 581 disable_interrupts();790 __disable_interrupts_checked(); 582 791 /* paranoid */ verify( ! __preemption_enabled() ); 583 post( this->idle ); 584 enable_interrupts( __cfaabi_dbg_ctx ); 585 } 586 587 static void push (__cluster_idles & this, processor & proc) { 792 eventfd_t val; 793 val = 1; 794 eventfd_write( this->idle, val ); 795 __enable_interrupts_checked(); 796 } 797 798 static void mark_idle(__cluster_proc_list & this, processor & proc) { 588 799 /* paranoid */ verify( ! __preemption_enabled() ); 589 800 lock( this ); 590 801 this.idle++; 591 802 /* paranoid */ verify( this.idle <= this.total ); 592 593 insert_first(this. list, proc);803 remove(proc); 804 insert_first(this.idles, proc); 594 805 unlock( this ); 595 806 /* paranoid */ verify( ! __preemption_enabled() ); 596 807 } 597 808 598 static void remove(__cluster_idles& this, processor & proc) {809 static void mark_awake(__cluster_proc_list & this, processor & proc) { 599 810 /* paranoid */ verify( ! __preemption_enabled() ); 600 811 lock( this ); 601 812 this.idle--; 602 813 /* paranoid */ verify( this.idle >= 0 ); 603 604 814 remove(proc); 815 insert_last(this.actives, proc); 605 816 unlock( this ); 606 817 /* paranoid */ verify( ! __preemption_enabled() ); 607 818 } 608 819 609 static [unsigned idle, unsigned total, * processor] query( & __cluster_idles this ) { 820 static [unsigned idle, unsigned total, * processor] query_idles( & __cluster_proc_list this ) { 821 /* paranoid */ verify( ! __preemption_enabled() ); 822 /* paranoid */ verify( ready_schedule_islocked() ); 823 610 824 for() { 611 825 uint64_t l = __atomic_load_n(&this.lock, __ATOMIC_SEQ_CST); … … 613 827 unsigned idle = this.idle; 614 828 unsigned total = this.total; 615 processor * proc = &this. list`first;829 processor * proc = &this.idles`first; 616 830 // Compiler fence is unnecessary, but gcc-8 and older incorrectly reorder code without it 617 831 asm volatile("": : :"memory"); … … 619 833 return [idle, total, proc]; 620 834 } 835 836 /* paranoid */ verify( ready_schedule_islocked() ); 837 /* paranoid */ verify( ! __preemption_enabled() ); 621 838 } 622 839 … … 664 881 // Kernel Utilities 665 882 //============================================================================================= 883 #if defined(CFA_HAVE_LINUX_IO_URING_H) 884 #include "io/types.hfa" 885 #endif 886 887 static inline bool __maybe_io_drain( processor * proc ) { 888 bool ret = false; 889 #if defined(CFA_HAVE_LINUX_IO_URING_H) 890 __cfadbg_print_safe(runtime_core, "Kernel : core %p checking io for ring %d\n", proc, proc->io.ctx->fd); 891 892 // Check if we should drain the queue 893 $io_context * ctx = proc->io.ctx; 894 unsigned head = *ctx->cq.head; 895 unsigned tail = *ctx->cq.tail; 896 if(head == tail) return false; 897 ready_schedule_lock(); 898 ret = __cfa_io_drain( proc ); 899 ready_schedule_unlock(); 900 #endif 901 return ret; 902 } 903 666 904 //----------------------------------------------------------------------------- 667 905 // Debug … … 691 929 __print_stats( this.stats, this.print_stats, "Cluster", this.name, (void*)&this ); 692 930 } 693 694 extern int __print_alarm_stats;695 void print_alarm_stats() {696 __print_alarm_stats = -1;697 }698 931 #endif 699 932 // Local Variables: // -
libcfa/src/concurrency/kernel.hfa
rfeacef9 r5407cdc 28 28 } 29 29 30 //-----------------------------------------------------------------------------31 // Underlying Locks32 30 #ifdef __CFA_WITH_VERIFY__ 33 31 extern bool __cfaabi_dbg_in_kernel(); 34 32 #endif 35 33 36 extern "C" { 37 char * strerror(int); 38 } 39 #define CHECKED(x) { int err = x; if( err != 0 ) abort("KERNEL ERROR: Operation \"" #x "\" return error %d - %s\n", err, strerror(err)); } 40 41 struct __bin_sem_t { 42 pthread_mutex_t lock; 43 pthread_cond_t cond; 44 int val; 45 }; 46 47 static inline void ?{}(__bin_sem_t & this) with( this ) { 48 // Create the mutex with error checking 49 pthread_mutexattr_t mattr; 50 pthread_mutexattr_init( &mattr ); 51 pthread_mutexattr_settype( &mattr, PTHREAD_MUTEX_ERRORCHECK_NP); 52 pthread_mutex_init(&lock, &mattr); 53 54 pthread_cond_init (&cond, (const pthread_condattr_t *)0p); // workaround trac#208: cast should not be required 55 val = 0; 56 } 57 58 static inline void ^?{}(__bin_sem_t & this) with( this ) { 59 CHECKED( pthread_mutex_destroy(&lock) ); 60 CHECKED( pthread_cond_destroy (&cond) ); 61 } 62 63 static inline void wait(__bin_sem_t & this) with( this ) { 64 verify(__cfaabi_dbg_in_kernel()); 65 CHECKED( pthread_mutex_lock(&lock) ); 66 while(val < 1) { 67 pthread_cond_wait(&cond, &lock); 68 } 69 val -= 1; 70 CHECKED( pthread_mutex_unlock(&lock) ); 71 } 72 73 static inline bool post(__bin_sem_t & this) with( this ) { 74 bool needs_signal = false; 75 76 CHECKED( pthread_mutex_lock(&lock) ); 77 if(val < 1) { 78 val += 1; 79 pthread_cond_signal(&cond); 80 needs_signal = true; 81 } 82 CHECKED( pthread_mutex_unlock(&lock) ); 83 84 return needs_signal; 85 } 86 87 #undef CHECKED 88 34 //----------------------------------------------------------------------------- 35 // I/O 36 struct cluster; 37 struct $io_context; 38 struct $io_arbiter; 39 40 struct io_context_params { 41 int num_entries; 42 }; 43 44 void ?{}(io_context_params & this); 89 45 90 46 //----------------------------------------------------------------------------- … … 95 51 struct __processor_id_t { 96 52 unsigned id:24; 97 bool full_proc:1;98 53 99 54 #if !defined(__CFA_NO_STATISTICS__) … … 114 69 struct cluster * cltr; 115 70 71 // Ready Queue state per processor 72 struct { 73 unsigned short its; 74 unsigned short itr; 75 unsigned id; 76 unsigned target; 77 unsigned long long int cutoff; 78 } rdq; 79 116 80 // Set to true to notify the processor should terminate 117 81 volatile bool do_terminate; … … 125 89 // Handle to pthreads 126 90 pthread_t kernel_thread; 91 92 struct { 93 $io_context * ctx; 94 bool pending; 95 bool dirty; 96 } io; 127 97 128 98 // Preemption data … … 134 104 135 105 // Idle lock (kernel semaphore) 136 __bin_sem_t idle;106 int idle; 137 107 138 108 // Termination synchronisation (user semaphore) … … 144 114 // Link lists fields 145 115 DLISTED_MGD_IMPL_IN(processor) 116 117 // special init fields 118 // This is needed for memcached integration 119 // once memcached experiments are done this should probably be removed 120 // it is not a particularly safe scheme as it can make processors less homogeneous 121 struct { 122 $thread * thrd; 123 } init; 146 124 147 125 #if !defined(__CFA_NO_STATISTICS__) … … 159 137 void ^?{}(processor & this); 160 138 161 static inline void ?{}(processor & this) { this{ "Anonymous Processor", *mainCluster}; }162 static inline void ?{}(processor & this, struct cluster & cltr) { this{ "Anonymous Processor", cltr}; }163 static inline void ?{}(processor & this, const char name[]) { this{name, *mainCluster}; }139 static inline void ?{}(processor & this) { this{ "Anonymous Processor", *mainCluster}; } 140 static inline void ?{}(processor & this, struct cluster & cltr) { this{ "Anonymous Processor", cltr}; } 141 static inline void ?{}(processor & this, const char name[]) { this{name, *mainCluster}; } 164 142 165 143 DLISTED_MGD_IMPL_OUT(processor) 166 144 167 145 //----------------------------------------------------------------------------- 168 // I/O169 struct __io_data;170 171 // IO poller user-thread172 // Not using the "thread" keyword because we want to control173 // more carefully when to start/stop it174 struct $io_ctx_thread {175 struct __io_data * ring;176 single_sem sem;177 volatile bool done;178 $thread self;179 };180 181 182 struct io_context {183 $io_ctx_thread thrd;184 };185 186 struct io_context_params {187 int num_entries;188 int num_ready;189 int submit_aff;190 bool eager_submits:1;191 bool poller_submits:1;192 bool poll_submit:1;193 bool poll_complete:1;194 };195 196 void ?{}(io_context_params & this);197 198 void ?{}(io_context & this, struct cluster & cl);199 void ?{}(io_context & this, struct cluster & cl, const io_context_params & params);200 void ^?{}(io_context & this);201 202 struct io_cancellation {203 __u64 target;204 };205 206 static inline void ?{}(io_cancellation & this) { this.target = -1u; }207 static inline void ^?{}(io_cancellation &) {}208 bool cancel(io_cancellation & this);209 210 //-----------------------------------------------------------------------------211 146 // Cluster Tools 212 147 213 // Intrusives lanes which are used by the re laxed ready queue148 // Intrusives lanes which are used by the ready queue 214 149 struct __attribute__((aligned(128))) __intrusive_lane_t; 215 150 void ?{}(__intrusive_lane_t & this); 216 151 void ^?{}(__intrusive_lane_t & this); 217 152 218 // Counter used for wether or not the lanes are all empty 219 struct __attribute__((aligned(128))) __snzi_node_t; 220 struct __snzi_t { 221 unsigned mask; 222 int root; 223 __snzi_node_t * nodes; 224 }; 225 226 void ?{}( __snzi_t & this, unsigned depth ); 227 void ^?{}( __snzi_t & this ); 153 // Aligned timestamps which are used by the relaxed ready queue 154 struct __attribute__((aligned(128))) __timestamp_t; 155 void ?{}(__timestamp_t & this); 156 void ^?{}(__timestamp_t & this); 228 157 229 158 //TODO adjust cache size to ARCHITECTURE 230 159 // Structure holding the relaxed ready queue 231 160 struct __ready_queue_t { 232 // Data tracking how many/which lanes are used233 // Aligned to 128 for cache locality234 __snzi_t snzi;235 236 161 // Data tracking the actual lanes 237 162 // On a seperate cacheline from the used struct since … … 242 167 __intrusive_lane_t * volatile data; 243 168 169 // Array of times 170 __timestamp_t * volatile tscs; 171 244 172 // Number of lanes (empty or not) 245 173 volatile size_t count; … … 251 179 252 180 // Idle Sleep 253 struct __cluster_ idles{181 struct __cluster_proc_list { 254 182 // Spin lock protecting the queue 255 183 volatile uint64_t lock; … … 262 190 263 191 // List of idle processors 264 dlist(processor, processor) list; 192 dlist(processor, processor) idles; 193 194 // List of active processors 195 dlist(processor, processor) actives; 265 196 }; 266 197 … … 278 209 279 210 // List of idle processors 280 __cluster_ idles idles;211 __cluster_proc_list procs; 281 212 282 213 // List of threads … … 292 223 293 224 struct { 294 io_context * ctxs;295 unsigned cnt;225 $io_arbiter * arbiter; 226 io_context_params params; 296 227 } io; 297 228 -
libcfa/src/concurrency/kernel/fwd.hfa
rfeacef9 r5407cdc 108 108 109 109 extern void disable_interrupts(); 110 extern void enable_interrupts_noPoll(); 111 extern void enable_interrupts( __cfaabi_dbg_ctx_param ); 110 extern void enable_interrupts( bool poll = false ); 112 111 113 112 extern "Cforall" { … … 220 219 // Mark as fulfilled, wake thread if needed 221 220 // return true if a thread was unparked 222 bool post(oneshot & this) {221 $thread * post(oneshot & this, bool do_unpark = true) { 223 222 struct $thread * got = __atomic_exchange_n( &this.ptr, 1p, __ATOMIC_SEQ_CST); 224 if( got == 0p ) return false;225 unpark( got );226 return true;223 if( got == 0p ) return 0p; 224 if(do_unpark) unpark( got ); 225 return got; 227 226 } 228 227 } … … 336 335 // from the server side, mark the future as fulfilled 337 336 // delete it if needed 338 bool fulfil( future_t & this) {337 $thread * fulfil( future_t & this, bool do_unpark = true ) { 339 338 for() { 340 339 struct oneshot * expected = this.ptr; … … 344 343 #pragma GCC diagnostic ignored "-Wfree-nonheap-object" 345 344 #endif 346 if( expected == 3p ) { free( &this ); return false; }345 if( expected == 3p ) { free( &this ); return 0p; } 347 346 #if defined(__GNUC__) && __GNUC__ >= 7 348 347 #pragma GCC diagnostic pop … … 356 355 struct oneshot * want = expected == 0p ? 1p : 2p; 357 356 if(__atomic_compare_exchange_n(&this.ptr, &expected, want, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 358 if( expected == 0p ) { /* paranoid */ verify( this.ptr == 1p); return false; }359 bool ret = post( *expected);357 if( expected == 0p ) { /* paranoid */ verify( this.ptr == 1p); return 0p; } 358 $thread * ret = post( *expected, do_unpark ); 360 359 __atomic_store_n( &this.ptr, 1p, __ATOMIC_SEQ_CST); 361 360 return ret; … … 403 402 __VA_ARGS__ \ 404 403 } \ 405 if( !(in_kernel) ) enable_interrupts( __cfaabi_dbg_ctx); \404 if( !(in_kernel) ) enable_interrupts(); \ 406 405 } 407 406 #else -
libcfa/src/concurrency/kernel/startup.cfa
rfeacef9 r5407cdc 22 22 extern "C" { 23 23 #include <limits.h> // PTHREAD_STACK_MIN 24 #include <sys/eventfd.h> // eventfd 24 25 #include <sys/mman.h> // mprotect 25 26 #include <sys/resource.h> // getrlimit … … 72 73 static void __kernel_first_resume( processor * this ); 73 74 static void __kernel_last_resume ( processor * this ); 74 static void init(processor & this, const char name[], cluster & _cltr );75 static void init(processor & this, const char name[], cluster & _cltr, $thread * initT); 75 76 static void deinit(processor & this); 76 77 static void doregister( struct cluster & cltr ); … … 89 90 extern void __kernel_alarm_startup(void); 90 91 extern void __kernel_alarm_shutdown(void); 91 extern void __kernel_io_startup (void);92 extern void __kernel_io_shutdown(void);93 92 94 93 //----------------------------------------------------------------------------- … … 102 101 KERNEL_STORAGE($thread, mainThread); 103 102 KERNEL_STORAGE(__stack_t, mainThreadCtx); 104 KERNEL_STORAGE(io_context, mainPollerThread);105 103 KERNEL_STORAGE(__scheduler_RWLock_t, __scheduler_lock); 106 104 #if !defined(__CFA_NO_STATISTICS__) … … 198 196 199 197 void ?{}(processor & this) with( this ) { 200 ( this.idle ){};201 198 ( this.terminated ){}; 202 199 ( this.runner ){}; 203 init( this, "Main Processor", *mainCluster );200 init( this, "Main Processor", *mainCluster, 0p ); 204 201 kernel_thread = pthread_self(); 205 202 … … 226 223 __kernel_alarm_startup(); 227 224 228 // Start IO229 __kernel_io_startup();230 231 225 // Add the main thread to the ready queue 232 226 // once resume is called on mainProcessor->runner the mainThread needs to be scheduled like any normal thread 233 __schedule_thread(mainThread);227 schedule_thread$(mainThread); 234 228 235 229 // SKULLDUGGERY: Force a context switch to the main processor to set the main thread's context to the current UNIX … … 241 235 // THE SYSTEM IS NOW COMPLETELY RUNNING 242 236 243 244 // SKULLDUGGERY: The constructor for the mainCluster will call alloc with a dimension of 0245 // malloc *can* return a non-null value, we should free it if that is the case246 free( mainCluster->io.ctxs );247 248 // Now that the system is up, finish creating systems that need threading249 mainCluster->io.ctxs = (io_context *)&storage_mainPollerThread;250 mainCluster->io.cnt = 1;251 (*mainCluster->io.ctxs){ *mainCluster };252 253 237 __cfadbg_print_safe(runtime_core, "Kernel : Started\n--------------------------------------------------\n\n"); 254 238 255 239 /* paranoid */ verify( ! __preemption_enabled() ); 256 enable_interrupts( __cfaabi_dbg_ctx);240 enable_interrupts(); 257 241 /* paranoid */ verify( __preemption_enabled() ); 258 242 … … 260 244 261 245 static void __kernel_shutdown(void) { 262 //Before we start shutting things down, wait for systems that need threading to shutdown263 ^(*mainCluster->io.ctxs){};264 mainCluster->io.cnt = 0;265 mainCluster->io.ctxs = 0p;266 267 246 /* paranoid */ verify( __preemption_enabled() ); 268 247 disable_interrupts(); … … 283 262 __kernel_alarm_shutdown(); 284 263 285 // Stop IO 286 __kernel_io_shutdown(); 264 #if !defined( __CFA_NO_STATISTICS__ ) 265 __stats_t * st = (__stats_t *)& storage_mainProcStats; 266 __tally_stats(mainCluster->stats, st); 267 if( 0 != mainProcessor->print_stats ) { 268 __print_stats( st, mainProcessor->print_stats, "Processor ", mainProcessor->name, (void*)mainProcessor ); 269 } 270 #if defined(CFA_STATS_ARRAY) 271 __flush_stat( st, "Processor", mainProcessor ); 272 #endif 273 #endif 287 274 288 275 // Destroy the main processor and its context in reverse order of construction … … 364 351 __print_stats( &local_stats, proc->print_stats, "Processor ", proc->name, (void*)proc ); 365 352 } 353 #if defined(CFA_STATS_ARRAY) 354 __flush_stat( &local_stats, "Processor", proc ); 355 #endif 366 356 #endif 367 357 … … 457 447 link.next = 0p; 458 448 link.prev = 0p; 449 link.preferred = -1u; 450 last_proc = 0p; 459 451 #if defined( __CFA_WITH_VERIFY__ ) 460 452 canary = 0x0D15EA5E0D15EA5Ep; … … 476 468 } 477 469 478 static void init(processor & this, const char name[], cluster & _cltr ) with( this ) {470 static void init(processor & this, const char name[], cluster & _cltr, $thread * initT) with( this ) { 479 471 this.name = name; 480 472 this.cltr = &_cltr; 481 full_proc = true; 473 this.rdq.its = 0; 474 this.rdq.itr = 0; 475 this.rdq.id = -1u; 476 this.rdq.target = -1u; 477 this.rdq.cutoff = -1ull; 482 478 do_terminate = false; 483 479 preemption_alarm = 0p; 484 480 pending_preemption = false; 485 481 482 this.io.ctx = 0p; 483 this.io.pending = false; 484 this.io.dirty = false; 485 486 this.init.thrd = initT; 487 488 this.idle = eventfd(0, 0); 489 if (idle < 0) { 490 abort("KERNEL ERROR: PROCESSOR EVENTFD - %s\n", strerror(errno)); 491 } 492 486 493 #if !defined(__CFA_NO_STATISTICS__) 487 494 print_stats = 0; … … 489 496 #endif 490 497 491 lock( this.cltr->idles ); 492 int target = this.cltr->idles.total += 1u; 493 unlock( this.cltr->idles ); 494 495 id = doregister((__processor_id_t*)&this); 496 498 // Register and Lock the RWlock so no-one pushes/pops while we are changing the queue 499 uint_fast32_t last_size = ready_mutate_register((__processor_id_t*)&this); 500 this.cltr->procs.total += 1u; 501 insert_last(this.cltr->procs.actives, this); 502 503 // Adjust the ready queue size 504 ready_queue_grow( cltr ); 505 506 // Unlock the RWlock 507 ready_mutate_unlock( last_size ); 508 509 __cfadbg_print_safe(runtime_core, "Kernel : core %p created\n", &this); 510 } 511 512 // Not a ctor, it just preps the destruction but should not destroy members 513 static void deinit(processor & this) { 497 514 // Lock the RWlock so no-one pushes/pops while we are changing the queue 498 515 uint_fast32_t last_size = ready_mutate_lock(); 516 this.cltr->procs.total -= 1u; 517 remove(this); 499 518 500 519 // Adjust the ready queue size 501 ready_queue_grow( cltr, target ); 502 503 // Unlock the RWlock 504 ready_mutate_unlock( last_size ); 505 506 __cfadbg_print_safe(runtime_core, "Kernel : core %p created\n", &this); 507 } 508 509 // Not a ctor, it just preps the destruction but should not destroy members 510 static void deinit(processor & this) { 511 lock( this.cltr->idles ); 512 int target = this.cltr->idles.total -= 1u; 513 unlock( this.cltr->idles ); 514 515 // Lock the RWlock so no-one pushes/pops while we are changing the queue 516 uint_fast32_t last_size = ready_mutate_lock(); 517 518 // Adjust the ready queue size 519 ready_queue_shrink( this.cltr, target ); 520 521 // Unlock the RWlock 522 ready_mutate_unlock( last_size ); 523 524 // Finally we don't need the read_lock any more 525 unregister((__processor_id_t*)&this); 526 } 527 528 void ?{}(processor & this, const char name[], cluster & _cltr) { 529 ( this.idle ){}; 520 ready_queue_shrink( this.cltr ); 521 522 // Unlock the RWlock and unregister: we don't need the read_lock any more 523 ready_mutate_unregister((__processor_id_t*)&this, last_size ); 524 525 close(this.idle); 526 } 527 528 void ?{}(processor & this, const char name[], cluster & _cltr, $thread * initT) { 530 529 ( this.terminated ){}; 531 530 ( this.runner ){}; 532 531 533 532 disable_interrupts(); 534 init( this, name, _cltr );535 enable_interrupts( __cfaabi_dbg_ctx);533 init( this, name, _cltr, initT ); 534 enable_interrupts(); 536 535 537 536 __cfadbg_print_safe(runtime_core, "Kernel : Starting core %p\n", &this); 538 537 539 538 this.stack = __create_pthread( &this.kernel_thread, __invoke_processor, (void *)&this ); 540 539 } 540 541 void ?{}(processor & this, const char name[], cluster & _cltr) { 542 (this){name, _cltr, 0p}; 541 543 } 542 544 … … 557 559 disable_interrupts(); 558 560 deinit( this ); 559 enable_interrupts( __cfaabi_dbg_ctx);561 enable_interrupts(); 560 562 } 561 563 562 564 //----------------------------------------------------------------------------- 563 565 // Cluster 564 static void ?{}(__cluster_ idles& this) {566 static void ?{}(__cluster_proc_list & this) { 565 567 this.lock = 0; 566 568 this.idle = 0; 567 569 this.total = 0; 568 (this.list){};569 570 } 570 571 … … 582 583 threads{ __get }; 583 584 585 io.arbiter = create(); 586 io.params = io_params; 587 584 588 doregister(this); 585 589 … … 589 593 590 594 // Adjust the ready queue size 591 ready_queue_grow( &this , 0);595 ready_queue_grow( &this ); 592 596 593 597 // Unlock the RWlock 594 598 ready_mutate_unlock( last_size ); 595 enable_interrupts_noPoll(); // Don't poll, could be in main cluster 596 597 598 this.io.cnt = num_io; 599 this.io.ctxs = aalloc(num_io); 600 for(i; this.io.cnt) { 601 (this.io.ctxs[i]){ this, io_params }; 602 } 599 enable_interrupts( false ); // Don't poll, could be in main cluster 603 600 } 604 601 605 602 void ^?{}(cluster & this) { 606 for(i; this.io.cnt) { 607 ^(this.io.ctxs[i]){ true }; 608 } 609 free(this.io.ctxs); 603 destroy(this.io.arbiter); 610 604 611 605 // Lock the RWlock so no-one pushes/pops while we are changing the queue … … 614 608 615 609 // Adjust the ready queue size 616 ready_queue_shrink( &this , 0);610 ready_queue_shrink( &this ); 617 611 618 612 // Unlock the RWlock 619 613 ready_mutate_unlock( last_size ); 620 enable_interrupts _noPoll(); // Don't poll, could be in main cluster614 enable_interrupts( false ); // Don't poll, could be in main cluster 621 615 622 616 #if !defined(__CFA_NO_STATISTICS__) … … 624 618 __print_stats( this.stats, this.print_stats, "Cluster", this.name, (void*)&this ); 625 619 } 620 #if defined(CFA_STATS_ARRAY) 621 __flush_stat( this.stats, "Cluster", &this ); 622 #endif 626 623 free( this.stats ); 627 624 #endif … … 736 733 } 737 734 738 739 735 #if defined(__CFA_WITH_VERIFY__) 740 736 static bool verify_fwd_bck_rng(void) { -
libcfa/src/concurrency/kernel_private.hfa
rfeacef9 r5407cdc 29 29 extern "C" { 30 30 void disable_interrupts() OPTIONAL_THREAD; 31 void enable_interrupts_noPoll(); 32 void enable_interrupts( __cfaabi_dbg_ctx_param ); 33 } 34 35 void __schedule_thread( $thread * ) 36 #if defined(NDEBUG) || (!defined(__CFA_DEBUG__) && !defined(__CFA_VERIFY__)) 37 __attribute__((nonnull (1))) 38 #endif 39 ; 31 void enable_interrupts( bool poll = true ); 32 } 33 34 void schedule_thread$( $thread * ) __attribute__((nonnull (1))); 40 35 41 36 extern bool __preemption_enabled(); … … 77 72 //----------------------------------------------------------------------------- 78 73 // I/O 79 void ^?{}(io_context & this, bool ); 74 $io_arbiter * create(void); 75 void destroy($io_arbiter *); 80 76 81 77 //======================================================================= 82 78 // Cluster lock API 83 79 //======================================================================= 84 // Cells use by the reader writer lock85 // while not generic it only relies on a opaque pointer86 struct __attribute__((aligned(128))) __scheduler_lock_id_t {87 // Spin lock used as the underlying lock88 volatile bool lock;89 90 // Handle pointing to the proc owning this cell91 // Used for allocating cells and debugging92 __processor_id_t * volatile handle;93 94 #ifdef __CFA_WITH_VERIFY__95 // Debug, check if this is owned for reading96 bool owned;97 #endif98 };99 100 static_assert( sizeof(struct __scheduler_lock_id_t) <= __alignof(struct __scheduler_lock_id_t));101 102 80 // Lock-Free registering/unregistering of threads 103 81 // Register a processor to a given cluster and get its unique id in return 104 unsigned doregister( struct __processor_id_t * proc);82 void register_proc_id( struct __processor_id_t * ); 105 83 106 84 // Unregister a processor from a given cluster using its id, getting back the original pointer 107 void unregister( struct __processor_id_t * proc ); 108 109 //----------------------------------------------------------------------- 110 // Cluster idle lock/unlock 111 static inline void lock(__cluster_idles & this) { 112 for() { 113 uint64_t l = this.lock; 114 if( 115 (0 == (l % 2)) 116 && __atomic_compare_exchange_n(&this.lock, &l, l + 1, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) 117 ) return; 118 Pause(); 119 } 120 } 121 122 static inline void unlock(__cluster_idles & this) { 123 /* paranoid */ verify( 1 == (this.lock % 2) ); 124 __atomic_fetch_add( &this.lock, 1, __ATOMIC_SEQ_CST ); 125 } 85 void unregister_proc_id( struct __processor_id_t * proc ); 126 86 127 87 //======================================================================= … … 151 111 __atomic_store_n(ll, (bool)false, __ATOMIC_RELEASE); 152 112 } 113 114 // Cells use by the reader writer lock 115 // while not generic it only relies on a opaque pointer 116 struct __attribute__((aligned(128))) __scheduler_lock_id_t { 117 // Spin lock used as the underlying lock 118 volatile bool lock; 119 120 // Handle pointing to the proc owning this cell 121 // Used for allocating cells and debugging 122 __processor_id_t * volatile handle; 123 124 #ifdef __CFA_WITH_VERIFY__ 125 // Debug, check if this is owned for reading 126 bool owned; 127 #endif 128 }; 129 130 static_assert( sizeof(struct __scheduler_lock_id_t) <= __alignof(struct __scheduler_lock_id_t)); 153 131 154 132 //----------------------------------------------------------------------- … … 246 224 void ready_mutate_unlock( uint_fast32_t /* value returned by lock */ ); 247 225 226 //----------------------------------------------------------------------- 227 // Lock-Free registering/unregistering of threads 228 // Register a processor to a given cluster and get its unique id in return 229 // For convenience, also acquires the lock 230 static inline uint_fast32_t ready_mutate_register( struct __processor_id_t * proc ) { 231 register_proc_id( proc ); 232 return ready_mutate_lock(); 233 } 234 235 // Unregister a processor from a given cluster using its id, getting back the original pointer 236 // assumes the lock is acquired 237 static inline void ready_mutate_unregister( struct __processor_id_t * proc, uint_fast32_t last_s ) { 238 ready_mutate_unlock( last_s ); 239 unregister_proc_id( proc ); 240 } 241 242 //----------------------------------------------------------------------- 243 // Cluster idle lock/unlock 244 static inline void lock(__cluster_proc_list & this) { 245 /* paranoid */ verify( ! __preemption_enabled() ); 246 247 // Start by locking the global RWlock so that we know no-one is 248 // adding/removing processors while we mess with the idle lock 249 ready_schedule_lock(); 250 251 // Simple counting lock, acquired, acquired by incrementing the counter 252 // to an odd number 253 for() { 254 uint64_t l = this.lock; 255 if( 256 (0 == (l % 2)) 257 && __atomic_compare_exchange_n(&this.lock, &l, l + 1, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) 258 ) return; 259 Pause(); 260 } 261 262 /* paranoid */ verify( ! __preemption_enabled() ); 263 } 264 265 static inline void unlock(__cluster_proc_list & this) { 266 /* paranoid */ verify( ! __preemption_enabled() ); 267 268 /* paranoid */ verify( 1 == (this.lock % 2) ); 269 // Simple couting lock, release by incrementing to an even number 270 __atomic_fetch_add( &this.lock, 1, __ATOMIC_SEQ_CST ); 271 272 // Release the global lock, which we acquired when locking 273 ready_schedule_unlock(); 274 275 /* paranoid */ verify( ! __preemption_enabled() ); 276 } 277 248 278 //======================================================================= 249 279 // Ready-Queue API 250 280 //----------------------------------------------------------------------- 251 // pop thread from the ready queue of a cluster252 // returns 0p if empty253 __attribute__((hot)) bool query(struct cluster * cltr);254 255 //-----------------------------------------------------------------------256 281 // push thread onto a ready queue for a cluster 257 282 // returns true if the list was previously empty, false otherwise 258 __attribute__((hot)) boolpush(struct cluster * cltr, struct $thread * thrd);259 260 //----------------------------------------------------------------------- 261 // pop thread from the ready queueof a cluster283 __attribute__((hot)) void push(struct cluster * cltr, struct $thread * thrd); 284 285 //----------------------------------------------------------------------- 286 // pop thread from the local queues of a cluster 262 287 // returns 0p if empty 263 288 // May return 0p spuriously 264 __attribute__((hot)) struct $thread * pop(struct cluster * cltr); 265 266 //----------------------------------------------------------------------- 267 // pop thread from the ready queue of a cluster 289 __attribute__((hot)) struct $thread * pop_fast(struct cluster * cltr); 290 291 //----------------------------------------------------------------------- 292 // pop thread from any ready queue of a cluster 293 // returns 0p if empty 294 // May return 0p spuriously 295 __attribute__((hot)) struct $thread * pop_slow(struct cluster * cltr); 296 297 //----------------------------------------------------------------------- 298 // search all ready queues of a cluster for any thread 268 299 // returns 0p if empty 269 300 // guaranteed to find any threads added before this call 270 __attribute__((hot)) struct $thread * pop_slow(struct cluster * cltr); 271 272 //----------------------------------------------------------------------- 273 // remove thread from the ready queue of a cluster 274 // returns bool if it wasn't found 275 bool remove_head(struct cluster * cltr, struct $thread * thrd); 301 __attribute__((hot)) struct $thread * pop_search(struct cluster * cltr); 276 302 277 303 //----------------------------------------------------------------------- 278 304 // Increase the width of the ready queue (number of lanes) by 4 279 void ready_queue_grow (struct cluster * cltr , int target);305 void ready_queue_grow (struct cluster * cltr); 280 306 281 307 //----------------------------------------------------------------------- 282 308 // Decrease the width of the ready queue (number of lanes) by 4 283 void ready_queue_shrink(struct cluster * cltr , int target);309 void ready_queue_shrink(struct cluster * cltr); 284 310 285 311 -
libcfa/src/concurrency/locks.cfa
rfeacef9 r5407cdc 134 134 lock( lock __cfaabi_dbg_ctx2 ); 135 135 /* paranoid */ verifyf( owner != 0p, "Attempt to release lock %p that isn't held", &this ); 136 /* paranoid */ verifyf( owner == active_thread() || !strict_owner, "Thread %p other than the owner %p attempted to release owner lock %p", owner, active_thread(), &this ); 136 /* paranoid */ verifyf( owner == active_thread() || !strict_owner , "Thread %p other than the owner %p attempted to release owner lock %p", owner, active_thread(), &this ); 137 /* paranoid */ verifyf( recursion_count == 1 || multi_acquisition, "Thread %p attempted to release owner lock %p which is not recursive but has a recursive count of %zu", active_thread(), &this, recursion_count ); 137 138 138 139 // if recursion count is zero release lock and set new owner if one is waiting … … 146 147 size_t wait_count( blocking_lock & this ) with( this ) { 147 148 return wait_count; 148 }149 150 void set_recursion_count( blocking_lock & this, size_t recursion ) with( this ) {151 recursion_count = recursion;152 }153 154 size_t get_recursion_count( blocking_lock & this ) with( this ) {155 return recursion_count;156 149 } 157 150 … … 173 166 } 174 167 175 voidon_wait( blocking_lock & this ) with( this ) {168 size_t on_wait( blocking_lock & this ) with( this ) { 176 169 lock( lock __cfaabi_dbg_ctx2 ); 177 170 /* paranoid */ verifyf( owner != 0p, "Attempt to release lock %p that isn't held", &this ); 178 171 /* paranoid */ verifyf( owner == active_thread() || !strict_owner, "Thread %p other than the owner %p attempted to release owner lock %p", owner, active_thread(), &this ); 179 172 173 size_t ret = recursion_count; 174 180 175 pop_and_set_new_owner( this ); 181 176 unlock( lock ); 177 return ret; 178 } 179 180 void on_wakeup( blocking_lock & this, size_t recursion ) with( this ) { 181 recursion_count = recursion; 182 182 } 183 183 … … 274 274 } 275 275 276 bool empty( condition_variable(L) & this ) with(this) { return empty(blocked_threads); } 276 bool empty( condition_variable(L) & this ) with(this) { 277 lock( lock __cfaabi_dbg_ctx2 ); 278 bool ret = empty(blocked_threads); 279 unlock( lock ); 280 return ret; 281 } 277 282 278 283 int counter( condition_variable(L) & this ) with(this) { return count; } … … 285 290 if (i->lock) { 286 291 // if lock was passed get recursion count to reset to after waking thread 287 recursion_count = get_recursion_count(*i->lock); 288 on_wait( *i->lock ); 292 recursion_count = on_wait( *i->lock ); 289 293 } 290 294 return recursion_count; … … 301 305 302 306 // resets recursion count here after waking 303 if (i.lock) set_recursion_count(*i.lock, recursion_count);307 if (i.lock) on_wakeup(*i.lock, recursion_count); 304 308 } 305 309 … … 323 327 324 328 // resets recursion count here after waking 325 if (info.lock) set_recursion_count(*info.lock, recursion_count);329 if (info.lock) on_wakeup(*info.lock, recursion_count); 326 330 } 327 331 … … 373 377 } 374 378 375 bool V(semaphore & this) with( this ) {379 $thread * V (semaphore & this, const bool doUnpark ) with( this ) { 376 380 $thread * thrd = 0p; 377 381 lock( lock __cfaabi_dbg_ctx2 ); … … 385 389 386 390 // make new owner 387 unpark( thrd ); 388 391 if( doUnpark ) unpark( thrd ); 392 393 return thrd; 394 } 395 396 bool V(semaphore & this) with( this ) { 397 $thread * thrd = V(this, true); 389 398 return thrd != 0p; 390 399 } -
libcfa/src/concurrency/locks.hfa
rfeacef9 r5407cdc 20 20 21 21 #include "bits/weakso_locks.hfa" 22 #include "containers/queueLockFree.hfa" 23 24 #include "thread.hfa" 22 25 23 26 #include "time_t.hfa" 24 27 #include "time.hfa" 28 29 //----------------------------------------------------------------------------- 30 // Semaphores 31 32 // '0-nary' semaphore 33 // Similar to a counting semaphore except the value of one is never reached 34 // as a consequence, a V() that would bring the value to 1 *spins* until 35 // a P consumes it 36 struct Semaphore0nary { 37 __spinlock_t lock; // needed to protect 38 mpsc_queue($thread) queue; 39 }; 40 41 static inline bool P(Semaphore0nary & this, $thread * thrd) { 42 /* paranoid */ verify(!(thrd->seqable.next)); 43 /* paranoid */ verify(!(thrd`next)); 44 45 push(this.queue, thrd); 46 return true; 47 } 48 49 static inline bool P(Semaphore0nary & this) { 50 $thread * thrd = active_thread(); 51 P(this, thrd); 52 park(); 53 return true; 54 } 55 56 static inline $thread * V(Semaphore0nary & this, bool doUnpark = true) { 57 $thread * next; 58 lock(this.lock __cfaabi_dbg_ctx2); 59 for (;;) { 60 next = pop(this.queue); 61 if (next) break; 62 Pause(); 63 } 64 unlock(this.lock); 65 66 if (doUnpark) unpark(next); 67 return next; 68 } 69 70 // Wrapper used on top of any sempahore to avoid potential locking 71 struct BinaryBenaphore { 72 volatile ssize_t counter; 73 }; 74 75 static inline { 76 void ?{}(BinaryBenaphore & this) { this.counter = 0; } 77 void ?{}(BinaryBenaphore & this, zero_t) { this.counter = 0; } 78 void ?{}(BinaryBenaphore & this, one_t ) { this.counter = 1; } 79 80 // returns true if no blocking needed 81 bool P(BinaryBenaphore & this) { 82 return __atomic_fetch_sub(&this.counter, 1, __ATOMIC_SEQ_CST) > 0; 83 } 84 85 bool tryP(BinaryBenaphore & this) { 86 ssize_t c = this.counter; 87 return (c >= 1) && __atomic_compare_exchange_n(&this.counter, &c, c-1, false, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED); 88 } 89 90 // returns true if notify needed 91 bool V(BinaryBenaphore & this) { 92 ssize_t c = 0; 93 for () { 94 if (__atomic_compare_exchange_n(&this.counter, &c, c+1, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 95 if (c == 0) return true; 96 /* paranoid */ verify(c < 0); 97 return false; 98 } else { 99 if (c == 1) return true; 100 /* paranoid */ verify(c < 1); 101 Pause(); 102 } 103 } 104 } 105 } 106 107 // Binary Semaphore based on the BinaryBenaphore on top of the 0-nary Semaphore 108 struct ThreadBenaphore { 109 BinaryBenaphore ben; 110 Semaphore0nary sem; 111 }; 112 113 static inline void ?{}(ThreadBenaphore & this) {} 114 static inline void ?{}(ThreadBenaphore & this, zero_t) { (this.ben){ 0 }; } 115 static inline void ?{}(ThreadBenaphore & this, one_t ) { (this.ben){ 1 }; } 116 117 static inline bool P(ThreadBenaphore & this) { return P(this.ben) ? false : P(this.sem); } 118 static inline bool tryP(ThreadBenaphore & this) { return tryP(this.ben); } 119 static inline bool P(ThreadBenaphore & this, bool wait) { return wait ? P(this) : tryP(this); } 120 121 static inline $thread * V(ThreadBenaphore & this, bool doUnpark = true) { 122 if (V(this.ben)) return 0p; 123 return V(this.sem, doUnpark); 124 } 125 126 //----------------------------------------------------------------------------- 127 // Semaphore 128 struct semaphore { 129 __spinlock_t lock; 130 int count; 131 __queue_t($thread) waiting; 132 }; 133 134 void ?{}(semaphore & this, int count = 1); 135 void ^?{}(semaphore & this); 136 bool P (semaphore & this); 137 bool V (semaphore & this); 138 bool V (semaphore & this, unsigned count); 139 $thread * V (semaphore & this, bool ); 25 140 26 141 //---------- … … 31 146 static inline void ?{}( single_acquisition_lock & this ) {((blocking_lock &)this){ false, false };} 32 147 static inline void ^?{}( single_acquisition_lock & this ) {} 33 static inline void lock ( single_acquisition_lock & this ) { lock( (blocking_lock &)this ); }34 static inline void unlock ( single_acquisition_lock & this ) { unlock( (blocking_lock &)this ); }35 static inline void on_wait ( single_acquisition_lock & this ) { on_wait( (blocking_lock &)this ); }36 static inline void on_notify ( single_acquisition_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t); }37 static inline void set_recursion_count( single_acquisition_lock & this, size_t recursion ) { set_recursion_count( (blocking_lock &)this, recursion); }38 static inline size_t get_recursion_count( single_acquisition_lock & this ) { return get_recursion_count( (blocking_lock &)this); }148 static inline void lock ( single_acquisition_lock & this ) { lock ( (blocking_lock &)this ); } 149 static inline bool try_lock ( single_acquisition_lock & this ) { return try_lock( (blocking_lock &)this ); } 150 static inline void unlock ( single_acquisition_lock & this ) { unlock ( (blocking_lock &)this ); } 151 static inline size_t on_wait ( single_acquisition_lock & this ) { return on_wait ( (blocking_lock &)this ); } 152 static inline void on_wakeup( single_acquisition_lock & this, size_t v ) { on_wakeup ( (blocking_lock &)this, v ); } 153 static inline void on_notify( single_acquisition_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); } 39 154 40 155 //---------- … … 45 160 static inline void ?{}( owner_lock & this ) {((blocking_lock &)this){ true, true };} 46 161 static inline void ^?{}( owner_lock & this ) {} 47 static inline void lock ( owner_lock & this ) { lock ( (blocking_lock &)this ); } 48 static inline void unlock ( owner_lock & this ) { unlock ( (blocking_lock &)this ); } 49 static inline void on_wait ( owner_lock & this ) { on_wait( (blocking_lock &)this ); } 162 static inline void lock ( owner_lock & this ) { lock ( (blocking_lock &)this ); } 163 static inline bool try_lock ( owner_lock & this ) { return try_lock( (blocking_lock &)this ); } 164 static inline void unlock ( owner_lock & this ) { unlock ( (blocking_lock &)this ); } 165 static inline size_t on_wait ( owner_lock & this ) { return on_wait ( (blocking_lock &)this ); } 166 static inline void on_wakeup( owner_lock & this, size_t v ) { on_wakeup ( (blocking_lock &)this, v ); } 50 167 static inline void on_notify( owner_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); } 51 static inline void set_recursion_count( owner_lock & this, size_t recursion ) { set_recursion_count( (blocking_lock &)this, recursion ); } 52 static inline size_t get_recursion_count( owner_lock & this ) { return get_recursion_count( (blocking_lock &)this ); } 168 169 struct fast_lock { 170 $thread * volatile owner; 171 ThreadBenaphore sem; 172 }; 173 174 static inline bool $try_lock(fast_lock & this, $thread * thrd) { 175 $thread * exp = 0p; 176 return __atomic_compare_exchange_n(&this.owner, &exp, thrd, false, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED); 177 } 178 179 static inline void lock( fast_lock & this ) __attribute__((artificial)); 180 static inline void lock( fast_lock & this ) { 181 $thread * thrd = active_thread(); 182 /* paranoid */verify(thrd != this.owner); 183 184 for (;;) { 185 if ($try_lock(this, thrd)) return; 186 P(this.sem); 187 } 188 } 189 190 static inline bool try_lock( fast_lock & this ) __attribute__((artificial)); 191 static inline bool try_lock ( fast_lock & this ) { 192 $thread * thrd = active_thread(); 193 /* paranoid */ verify(thrd != this.owner); 194 return $try_lock(this, thrd); 195 } 196 197 static inline $thread * unlock( fast_lock & this ) __attribute__((artificial)); 198 static inline $thread * unlock( fast_lock & this ) { 199 /* paranoid */ verify(active_thread() == this.owner); 200 201 // open 'owner' before unlocking anyone 202 // so new and unlocked threads don't park incorrectly. 203 // This may require additional fencing on ARM. 204 this.owner = 0p; 205 206 return V(this.sem); 207 } 208 209 static inline size_t on_wait( fast_lock & this ) { unlock(this); return 0; } 210 static inline void on_wakeup( fast_lock & this, size_t ) { lock(this); } 211 static inline void on_notify( fast_lock &, struct $thread * t ) { unpark(t); } 212 213 struct mcs_node { 214 mcs_node * volatile next; 215 single_sem sem; 216 }; 217 218 static inline void ?{}(mcs_node & this) { this.next = 0p; } 219 220 static inline mcs_node * volatile & ?`next ( mcs_node * node ) { 221 return node->next; 222 } 223 224 struct mcs_lock { 225 mcs_queue(mcs_node) queue; 226 }; 227 228 static inline void lock(mcs_lock & l, mcs_node & n) { 229 if(push(l.queue, &n)) 230 wait(n.sem); 231 } 232 233 static inline void unlock(mcs_lock & l, mcs_node & n) { 234 mcs_node * next = advance(l.queue, &n); 235 if(next) post(next->sem); 236 } 53 237 54 238 //----------------------------------------------------------------------------- … … 59 243 60 244 // For synchronization locks to use when releasing 61 void on_wait( L & ); 62 63 // to get recursion count for cond lock to reset after waking 64 size_t get_recursion_count( L & ); 245 size_t on_wait( L & ); 65 246 66 247 // to set recursion count after getting signalled; 67 void set_recursion_count( L &, size_t recursion );248 void on_wakeup( L &, size_t recursion ); 68 249 }; 69 250 … … 119 300 bool wait( condition_variable(L) & this, L & l, uintptr_t info, Time time ); 120 301 } 121 122 //-----------------------------------------------------------------------------123 // Semaphore124 struct semaphore {125 __spinlock_t lock;126 int count;127 __queue_t($thread) waiting;128 };129 130 void ?{}(semaphore & this, int count = 1);131 void ^?{}(semaphore & this);132 bool P (semaphore & this);133 bool V (semaphore & this);134 bool V (semaphore & this, unsigned count); -
libcfa/src/concurrency/monitor.hfa
rfeacef9 r5407cdc 61 61 static inline forall( T & | sized(T) | { void ^?{}( T & mutex ); } ) 62 62 void delete( T * th ) { 63 ^(*th){};63 if(th) ^(*th){}; 64 64 free( th ); 65 65 } -
libcfa/src/concurrency/preemption.cfa
rfeacef9 r5407cdc 15 15 16 16 #define __cforall_thread__ 17 // #define __CFA_DEBUG_PRINT_PREEMPTION__ 17 18 18 19 #include "preemption.hfa" … … 28 29 #include "kernel_private.hfa" 29 30 31 30 32 #if !defined(__CFA_DEFAULT_PREEMPTION__) 31 33 #define __CFA_DEFAULT_PREEMPTION__ 10`ms 32 34 #endif 33 35 34 Duration default_preemption() __attribute__((weak)) { 35 return __CFA_DEFAULT_PREEMPTION__; 36 __attribute__((weak)) Duration default_preemption() { 37 const char * preempt_rate_s = getenv("CFA_DEFAULT_PREEMPTION"); 38 if(!preempt_rate_s) { 39 __cfadbg_print_safe(preemption, "No CFA_DEFAULT_PREEMPTION in ENV\n"); 40 return __CFA_DEFAULT_PREEMPTION__; 41 } 42 43 char * endptr = 0p; 44 long int preempt_rate_l = strtol(preempt_rate_s, &endptr, 10); 45 if(preempt_rate_l < 0 || preempt_rate_l > 65535) { 46 __cfadbg_print_safe(preemption, "CFA_DEFAULT_PREEMPTION out of range : %ld\n", preempt_rate_l); 47 return __CFA_DEFAULT_PREEMPTION__; 48 } 49 if('\0' != *endptr) { 50 __cfadbg_print_safe(preemption, "CFA_DEFAULT_PREEMPTION not a decimal number : %s\n", preempt_rate_s); 51 return __CFA_DEFAULT_PREEMPTION__; 52 } 53 54 return preempt_rate_l`ms; 36 55 } 37 56 … … 98 117 //Loop throught every thing expired 99 118 while( node = get_expired( alarms, currtime ) ) { 100 // __cfaabi_dbg_print_buffer_decl( " KERNEL: preemption tick.\n");119 __cfadbg_print_buffer_decl( preemption, " KERNEL: preemption tick %lu\n", currtime.tn); 101 120 Duration period = node->period; 102 121 if( period == 0) { … … 104 123 } 105 124 125 __cfadbg_print_buffer_local( preemption, " KERNEL: alarm ticking node %p.\n", node ); 126 127 106 128 // Check if this is a kernel 107 129 if( node->type == Kernel ) { … … 109 131 } 110 132 else if( node->type == User ) { 133 __cfadbg_print_buffer_local( preemption, " KERNEL: alarm unparking %p.\n", node->thrd ); 111 134 timeout( node->thrd ); 112 135 } … … 117 140 // Check if this is a periodic alarm 118 141 if( period > 0 ) { 119 // __cfaabi_dbg_print_buffer_local( " KERNEL: alarm period is %lu.\n", period.tv);142 __cfadbg_print_buffer_local( preemption, " KERNEL: alarm period is %lu.\n", period`ns ); 120 143 node->alarm = currtime + period; // Alarm is periodic, add currtime to it (used cached current time) 121 144 insert( alarms, node ); // Reinsert the node for the next time it triggers … … 125 148 // If there are still alarms pending, reset the timer 126 149 if( & (*alarms)`first ) { 127 __cfadbg_print_buffer_decl(preemption, " KERNEL: @%ju(%ju) resetting alarm to %ju.\n", currtime.tv, __kernel_get_time().tv, (alarms->head->alarm - currtime).tv);128 150 Duration delta = (*alarms)`first.alarm - currtime; 129 151 Duration capped = max(delta, 50`us); 130 // itimerval tim = { caped };131 // __cfaabi_dbg_print_buffer_local( " Values are %lu, %lu, %lu %lu.\n", delta.tv, caped.tv, tim.it_value.tv_sec, tim.it_value.tv_usec);132 133 152 __kernel_set_timer( capped ); 134 153 } … … 296 315 // Enable interrupts by decrementing the counter 297 316 // If counter reaches 0, execute any pending __cfactx_switch 298 void enable_interrupts( __cfaabi_dbg_ctx_param) {317 void enable_interrupts( bool poll ) { 299 318 // Cache the processor now since interrupts can start happening after the atomic store 300 319 processor * proc = __cfaabi_tls.this_processor; 301 /* paranoid */ verify( proc );320 /* paranoid */ verify( !poll || proc ); 302 321 303 322 with( __cfaabi_tls.preemption_state ){ … … 321 340 // Signal the compiler that a fence is needed but only for signal handlers 322 341 __atomic_signal_fence(__ATOMIC_RELEASE); 323 if( p roc->pending_preemption ) {342 if( poll && proc->pending_preemption ) { 324 343 proc->pending_preemption = false; 325 344 force_yield( __POLL_PREEMPTION ); 326 345 } 327 346 } 328 }329 330 // For debugging purposes : keep track of the last person to enable the interrupts331 __cfaabi_dbg_debug_do( proc->last_enable = caller; )332 }333 334 // Disable interrupts by incrementint the counter335 // Don't execute any pending __cfactx_switch even if counter reaches 0336 void enable_interrupts_noPoll() {337 unsigned short prev = __cfaabi_tls.preemption_state.disable_count;338 __cfaabi_tls.preemption_state.disable_count -= 1;339 // If this triggers someone is enabled already enabled interrupts340 /* paranoid */ verifyf( prev != 0u, "Incremented from %u\n", prev );341 if( prev == 1 ) {342 #if GCC_VERSION > 50000343 static_assert(__atomic_always_lock_free(sizeof(__cfaabi_tls.preemption_state.enabled), &__cfaabi_tls.preemption_state.enabled), "Must be lock-free");344 #endif345 // Set enabled flag to true346 // should be atomic to avoid preemption in the middle of the operation.347 // use memory order RELAXED since there is no inter-thread on this variable requirements348 __atomic_store_n(&__cfaabi_tls.preemption_state.enabled, true, __ATOMIC_RELAXED);349 350 // Signal the compiler that a fence is needed but only for signal handlers351 __atomic_signal_fence(__ATOMIC_RELEASE);352 347 } 353 348 } … … 585 580 586 581 // Setup proper signal handlers 587 __cfaabi_sigaction( SIGUSR1, sigHandler_ctxSwitch, SA_SIGINFO | SA_RESTART); // __cfactx_switch handler588 __cfaabi_sigaction( SIGALRM, sigHandler_alarm , SA_SIGINFO | SA_RESTART); // debug handler582 __cfaabi_sigaction( SIGUSR1, sigHandler_ctxSwitch, SA_SIGINFO ); // __cfactx_switch handler 583 __cfaabi_sigaction( SIGALRM, sigHandler_alarm , SA_SIGINFO ); // debug handler 589 584 590 585 signal_block( SIGALRM ); … … 689 684 } 690 685 691 #if !defined(__CFA_NO_STATISTICS__)692 int __print_alarm_stats = 0;693 #endif694 695 686 // Main of the alarm thread 696 687 // Waits on SIGALRM and send SIGUSR1 to whom ever needs it 697 688 static void * alarm_loop( __attribute__((unused)) void * args ) { 698 689 __processor_id_t id; 699 id.full_proc = false; 700 id.id = doregister(&id); 690 register_proc_id(&id); 701 691 __cfaabi_tls.this_proc_id = &id; 702 692 703 #if !defined(__CFA_NO_STATISTICS__)704 struct __stats_t local_stats;705 __cfaabi_tls.this_stats = &local_stats;706 __init_stats( &local_stats );707 #endif708 693 709 694 // Block sigalrms to control when they arrive … … 764 749 EXIT: 765 750 __cfaabi_dbg_print_safe( "Kernel : Preemption thread stopping\n" ); 766 unregister(&id); 767 768 #if !defined(__CFA_NO_STATISTICS__) 769 if( 0 != __print_alarm_stats ) { 770 __print_stats( &local_stats, __print_alarm_stats, "Alarm", "Thread", 0p ); 771 } 772 #endif 751 register_proc_id(&id); 752 773 753 return 0p; 774 754 } -
libcfa/src/concurrency/ready_queue.cfa
rfeacef9 r5407cdc 17 17 // #define __CFA_DEBUG_PRINT_READY_QUEUE__ 18 18 19 // #define USE_SNZI 19 // #define USE_MPSC 20 21 #define USE_RELAXED_FIFO 22 // #define USE_WORK_STEALING 20 23 21 24 #include "bits/defs.hfa" … … 28 31 #include <unistd.h> 29 32 30 #include "snzi.hfa"31 33 #include "ready_subqueue.hfa" 32 34 33 35 static const size_t cache_line_size = 64; 36 37 #if !defined(__CFA_NO_STATISTICS__) 38 #define __STATS(...) __VA_ARGS__ 39 #else 40 #define __STATS(...) 41 #endif 34 42 35 43 // No overriden function, no environment variable, no define … … 39 47 #endif 40 48 41 #define BIAS 16 49 #if defined(USE_RELAXED_FIFO) 50 #define BIAS 4 51 #define READYQ_SHARD_FACTOR 4 52 #define SEQUENTIAL_SHARD 1 53 #elif defined(USE_WORK_STEALING) 54 #define READYQ_SHARD_FACTOR 2 55 #define SEQUENTIAL_SHARD 2 56 #else 57 #error no scheduling strategy selected 58 #endif 59 60 static inline struct $thread * try_pop(struct cluster * cltr, unsigned w __STATS(, __stats_readyQ_pop_t & stats)); 61 static inline struct $thread * try_pop(struct cluster * cltr, unsigned i, unsigned j __STATS(, __stats_readyQ_pop_t & stats)); 62 static inline struct $thread * search(struct cluster * cltr); 63 static inline [unsigned, bool] idx_from_r(unsigned r, unsigned preferred); 64 42 65 43 66 // returns the maximum number of processors the RWLock support … … 93 116 //======================================================================= 94 117 // Lock-Free registering/unregistering of threads 95 unsigned doregister( struct __processor_id_t * proc ) with(*__scheduler_lock) {118 void register_proc_id( struct __processor_id_t * proc ) with(*__scheduler_lock) { 96 119 __cfadbg_print_safe(ready_queue, "Kernel : Registering proc %p for RW-Lock\n", proc); 97 120 … … 107 130 /*paranoid*/ verify(0 == (__alignof__(data[i]) % cache_line_size)); 108 131 /*paranoid*/ verify((((uintptr_t)&data[i]) % cache_line_size) == 0); 109 returni;132 proc->id = i; 110 133 } 111 134 } … … 134 157 /*paranoid*/ verify(__alignof__(data[n]) == (2 * cache_line_size)); 135 158 /*paranoid*/ verify((((uintptr_t)&data[n]) % cache_line_size) == 0); 136 returnn;137 } 138 139 void unregister ( struct __processor_id_t * proc ) with(*__scheduler_lock) {159 proc->id = n; 160 } 161 162 void unregister_proc_id( struct __processor_id_t * proc ) with(*__scheduler_lock) { 140 163 unsigned id = proc->id; 141 164 /*paranoid*/ verify(id < ready); … … 192 215 193 216 //======================================================================= 194 // Cforall Re qdy Queue used for scheduling217 // Cforall Ready Queue used for scheduling 195 218 //======================================================================= 196 219 void ?{}(__ready_queue_t & this) with (this) { 197 220 lanes.data = 0p; 221 lanes.tscs = 0p; 198 222 lanes.count = 0; 199 223 } 200 224 201 225 void ^?{}(__ready_queue_t & this) with (this) { 202 verify( 1 == lanes.count ); 203 #ifdef USE_SNZI 204 verify( !query( snzi ) ); 205 #endif 226 verify( SEQUENTIAL_SHARD == lanes.count ); 206 227 free(lanes.data); 228 free(lanes.tscs); 207 229 } 208 230 209 231 //----------------------------------------------------------------------- 210 __attribute__((hot)) bool query(struct cluster * cltr) { 211 #ifdef USE_SNZI 212 return query(cltr->ready_queue.snzi); 213 #endif 214 return true; 215 } 216 217 static inline [unsigned, bool] idx_from_r(unsigned r, unsigned preferred) { 218 unsigned i; 219 bool local; 220 #if defined(BIAS) 232 #if defined(USE_RELAXED_FIFO) 233 //----------------------------------------------------------------------- 234 // get index from random number with or without bias towards queues 235 static inline [unsigned, bool] idx_from_r(unsigned r, unsigned preferred) { 236 unsigned i; 237 bool local; 221 238 unsigned rlow = r % BIAS; 222 239 unsigned rhigh = r / BIAS; … … 224 241 // (BIAS - 1) out of BIAS chances 225 242 // Use perferred queues 226 i = preferred + (rhigh % 4);243 i = preferred + (rhigh % READYQ_SHARD_FACTOR); 227 244 local = true; 228 245 } … … 233 250 local = false; 234 251 } 235 #else 236 i = r; 237 local = false; 252 return [i, local]; 253 } 254 255 __attribute__((hot)) void push(struct cluster * cltr, struct $thread * thrd) with (cltr->ready_queue) { 256 __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr); 257 258 const bool external = (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr); 259 /* paranoid */ verify(external || kernelTLS().this_processor->rdq.id < lanes.count ); 260 261 // write timestamp 262 thrd->link.ts = rdtscl(); 263 264 bool local; 265 int preferred = external ? -1 : kernelTLS().this_processor->rdq.id; 266 267 // Try to pick a lane and lock it 268 unsigned i; 269 do { 270 // Pick the index of a lane 271 unsigned r = __tls_rand_fwd(); 272 [i, local] = idx_from_r(r, preferred); 273 274 i %= __atomic_load_n( &lanes.count, __ATOMIC_RELAXED ); 275 276 #if !defined(__CFA_NO_STATISTICS__) 277 if(unlikely(external)) __atomic_fetch_add(&cltr->stats->ready.push.extrn.attempt, 1, __ATOMIC_RELAXED); 278 else if(local) __tls_stats()->ready.push.local.attempt++; 279 else __tls_stats()->ready.push.share.attempt++; 280 #endif 281 282 #if defined(USE_MPSC) 283 // mpsc always succeeds 284 } while( false ); 285 #else 286 // If we can't lock it retry 287 } while( !__atomic_try_acquire( &lanes.data[i].lock ) ); 288 #endif 289 290 // Actually push it 291 push(lanes.data[i], thrd); 292 293 #if !defined(USE_MPSC) 294 // Unlock and return 295 __atomic_unlock( &lanes.data[i].lock ); 296 #endif 297 298 // Mark the current index in the tls rng instance as having an item 299 __tls_rand_advance_bck(); 300 301 __cfadbg_print_safe(ready_queue, "Kernel : Pushed %p on cluster %p (idx: %u, mask %llu, first %d)\n", thrd, cltr, i, used.mask[0], lane_first); 302 303 // Update statistics 304 #if !defined(__CFA_NO_STATISTICS__) 305 if(unlikely(external)) __atomic_fetch_add(&cltr->stats->ready.push.extrn.success, 1, __ATOMIC_RELAXED); 306 else if(local) __tls_stats()->ready.push.local.success++; 307 else __tls_stats()->ready.push.share.success++; 308 #endif 309 } 310 311 // Pop from the ready queue from a given cluster 312 __attribute__((hot)) $thread * pop_fast(struct cluster * cltr) with (cltr->ready_queue) { 313 /* paranoid */ verify( lanes.count > 0 ); 314 /* paranoid */ verify( kernelTLS().this_processor ); 315 /* paranoid */ verify( kernelTLS().this_processor->rdq.id < lanes.count ); 316 317 unsigned count = __atomic_load_n( &lanes.count, __ATOMIC_RELAXED ); 318 int preferred = kernelTLS().this_processor->rdq.id; 319 320 321 // As long as the list is not empty, try finding a lane that isn't empty and pop from it 322 for(25) { 323 // Pick two lists at random 324 unsigned ri = __tls_rand_bck(); 325 unsigned rj = __tls_rand_bck(); 326 327 unsigned i, j; 328 __attribute__((unused)) bool locali, localj; 329 [i, locali] = idx_from_r(ri, preferred); 330 [j, localj] = idx_from_r(rj, preferred); 331 332 i %= count; 333 j %= count; 334 335 // try popping from the 2 picked lists 336 struct $thread * thrd = try_pop(cltr, i, j __STATS(, *(locali || localj ? &__tls_stats()->ready.pop.local : &__tls_stats()->ready.pop.help))); 337 if(thrd) { 338 return thrd; 339 } 340 } 341 342 // All lanes where empty return 0p 343 return 0p; 344 } 345 346 __attribute__((hot)) struct $thread * pop_slow(struct cluster * cltr) { return pop_fast(cltr); } 347 __attribute__((hot)) struct $thread * pop_search(struct cluster * cltr) { 348 return search(cltr); 349 } 350 #endif 351 #if defined(USE_WORK_STEALING) 352 __attribute__((hot)) void push(struct cluster * cltr, struct $thread * thrd) with (cltr->ready_queue) { 353 __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr); 354 355 const bool external = (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr); 356 /* paranoid */ verify(external || kernelTLS().this_processor->rdq.id < lanes.count ); 357 358 // write timestamp 359 thrd->link.ts = rdtscl(); 360 361 // Try to pick a lane and lock it 362 unsigned i; 363 do { 364 #if !defined(__CFA_NO_STATISTICS__) 365 if(unlikely(external)) __atomic_fetch_add(&cltr->stats->ready.push.extrn.attempt, 1, __ATOMIC_RELAXED); 366 else __tls_stats()->ready.push.local.attempt++; 367 #endif 368 369 if(unlikely(external)) { 370 i = __tls_rand() % lanes.count; 371 } 372 else { 373 processor * proc = kernelTLS().this_processor; 374 unsigned r = proc->rdq.its++; 375 i = proc->rdq.id + (r % READYQ_SHARD_FACTOR); 376 } 377 378 379 #if defined(USE_MPSC) 380 // mpsc always succeeds 381 } while( false ); 382 #else 383 // If we can't lock it retry 384 } while( !__atomic_try_acquire( &lanes.data[i].lock ) ); 385 #endif 386 387 // Actually push it 388 push(lanes.data[i], thrd); 389 390 #if !defined(USE_MPSC) 391 // Unlock and return 392 __atomic_unlock( &lanes.data[i].lock ); 393 #endif 394 395 #if !defined(__CFA_NO_STATISTICS__) 396 if(unlikely(external)) __atomic_fetch_add(&cltr->stats->ready.push.extrn.success, 1, __ATOMIC_RELAXED); 397 else __tls_stats()->ready.push.local.success++; 398 #endif 399 400 __cfadbg_print_safe(ready_queue, "Kernel : Pushed %p on cluster %p (idx: %u, mask %llu, first %d)\n", thrd, cltr, i, used.mask[0], lane_first); 401 } 402 403 // Pop from the ready queue from a given cluster 404 __attribute__((hot)) $thread * pop_fast(struct cluster * cltr) with (cltr->ready_queue) { 405 /* paranoid */ verify( lanes.count > 0 ); 406 /* paranoid */ verify( kernelTLS().this_processor ); 407 /* paranoid */ verify( kernelTLS().this_processor->rdq.id < lanes.count ); 408 409 processor * proc = kernelTLS().this_processor; 410 411 if(proc->rdq.target == -1u) { 412 proc->rdq.target = __tls_rand() % lanes.count; 413 unsigned it1 = proc->rdq.itr; 414 unsigned it2 = proc->rdq.itr + 1; 415 unsigned idx1 = proc->rdq.id + (it1 % READYQ_SHARD_FACTOR); 416 unsigned idx2 = proc->rdq.id + (it2 % READYQ_SHARD_FACTOR); 417 unsigned long long tsc1 = ts(lanes.data[idx1]); 418 unsigned long long tsc2 = ts(lanes.data[idx2]); 419 proc->rdq.cutoff = min(tsc1, tsc2); 420 if(proc->rdq.cutoff == 0) proc->rdq.cutoff = -1ull; 421 } 422 else { 423 unsigned target = proc->rdq.target; 424 proc->rdq.target = -1u; 425 if(lanes.tscs[target].tv < proc->rdq.cutoff) { 426 $thread * t = try_pop(cltr, target __STATS(, __tls_stats()->ready.pop.help)); 427 if(t) return t; 428 } 429 } 430 431 for(READYQ_SHARD_FACTOR) { 432 unsigned i = proc->rdq.id + (--proc->rdq.itr % READYQ_SHARD_FACTOR); 433 if($thread * t = try_pop(cltr, i __STATS(, __tls_stats()->ready.pop.local))) return t; 434 } 435 return 0p; 436 } 437 438 __attribute__((hot)) struct $thread * pop_slow(struct cluster * cltr) with (cltr->ready_queue) { 439 unsigned i = __tls_rand() % lanes.count; 440 return try_pop(cltr, i __STATS(, __tls_stats()->ready.pop.steal)); 441 } 442 443 __attribute__((hot)) struct $thread * pop_search(struct cluster * cltr) with (cltr->ready_queue) { 444 return search(cltr); 445 } 446 #endif 447 448 //======================================================================= 449 // Various Ready Queue utilities 450 //======================================================================= 451 // these function work the same or almost the same 452 // whether they are using work-stealing or relaxed fifo scheduling 453 454 //----------------------------------------------------------------------- 455 // try to pop from a lane given by index w 456 static inline struct $thread * try_pop(struct cluster * cltr, unsigned w __STATS(, __stats_readyQ_pop_t & stats)) with (cltr->ready_queue) { 457 __STATS( stats.attempt++; ) 458 459 // Get relevant elements locally 460 __intrusive_lane_t & lane = lanes.data[w]; 461 462 // If list looks empty retry 463 if( is_empty(lane) ) { 464 __STATS( stats.espec++; ) 465 return 0p; 466 } 467 468 // If we can't get the lock retry 469 if( !__atomic_try_acquire(&lane.lock) ) { 470 __STATS( stats.elock++; ) 471 return 0p; 472 } 473 474 // If list is empty, unlock and retry 475 if( is_empty(lane) ) { 476 __atomic_unlock(&lane.lock); 477 __STATS( stats.eempty++; ) 478 return 0p; 479 } 480 481 // Actually pop the list 482 struct $thread * thrd; 483 thrd = pop(lane); 484 485 /* paranoid */ verify(thrd); 486 /* paranoid */ verify(lane.lock); 487 488 // Unlock and return 489 __atomic_unlock(&lane.lock); 490 491 // Update statistics 492 __STATS( stats.success++; ) 493 494 #if defined(USE_WORK_STEALING) 495 lanes.tscs[w].tv = thrd->link.ts; 238 496 #endif 239 return [i, local]; 497 498 // return the popped thread 499 return thrd; 240 500 } 241 501 242 502 //----------------------------------------------------------------------- 243 __attribute__((hot)) bool push(struct cluster * cltr, struct $thread * thrd) with (cltr->ready_queue) { 244 __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr); 245 246 // write timestamp 247 thrd->link.ts = rdtscl(); 248 249 __attribute__((unused)) bool local; 250 __attribute__((unused)) int preferred; 251 #if defined(BIAS) 252 preferred = 253 //* 254 kernelTLS().this_processor ? kernelTLS().this_processor->id * 4 : -1; 255 /*/ 256 thrd->link.preferred * 4; 257 //*/ 258 #endif 259 260 // Try to pick a lane and lock it 261 unsigned i; 262 do { 263 // Pick the index of a lane 264 // unsigned r = __tls_rand(); 265 unsigned r = __tls_rand_fwd(); 266 [i, local] = idx_from_r(r, preferred); 267 268 #if !defined(__CFA_NO_STATISTICS__) 269 if(local) { 270 __tls_stats()->ready.pick.push.local++; 271 } 272 #endif 273 274 i %= __atomic_load_n( &lanes.count, __ATOMIC_RELAXED ); 275 276 #if !defined(__CFA_NO_STATISTICS__) 277 __tls_stats()->ready.pick.push.attempt++; 278 #endif 279 280 // If we can't lock it retry 281 } while( !__atomic_try_acquire( &lanes.data[i].lock ) ); 282 283 bool first = false; 284 285 // Actually push it 286 #ifdef USE_SNZI 287 bool lane_first = 288 #endif 289 290 push(lanes.data[i], thrd); 291 292 #ifdef USE_SNZI 293 // If this lane used to be empty we need to do more 294 if(lane_first) { 295 // Check if the entire queue used to be empty 296 first = !query(snzi); 297 298 // Update the snzi 299 arrive( snzi, i ); 300 } 301 #endif 302 303 __tls_rand_advance_bck(); 304 305 // Unlock and return 306 __atomic_unlock( &lanes.data[i].lock ); 307 308 __cfadbg_print_safe(ready_queue, "Kernel : Pushed %p on cluster %p (idx: %u, mask %llu, first %d)\n", thrd, cltr, i, used.mask[0], lane_first); 309 310 // Update statistics 311 #if !defined(__CFA_NO_STATISTICS__) 312 #if defined(BIAS) 313 if( local ) __tls_stats()->ready.pick.push.lsuccess++; 314 #endif 315 __tls_stats()->ready.pick.push.success++; 316 #endif 317 318 // return whether or not the list was empty before this push 319 return first; 320 } 321 322 static struct $thread * try_pop(struct cluster * cltr, unsigned i, unsigned j); 323 static struct $thread * try_pop(struct cluster * cltr, unsigned i); 324 325 // Pop from the ready queue from a given cluster 326 __attribute__((hot)) $thread * pop(struct cluster * cltr) with (cltr->ready_queue) { 327 /* paranoid */ verify( lanes.count > 0 ); 328 unsigned count = __atomic_load_n( &lanes.count, __ATOMIC_RELAXED ); 329 int preferred; 330 #if defined(BIAS) 331 // Don't bother trying locally too much 332 int local_tries = 8; 333 preferred = kernelTLS().this_processor->id * 4; 334 #endif 335 336 337 // As long as the list is not empty, try finding a lane that isn't empty and pop from it 338 #ifdef USE_SNZI 339 while( query(snzi) ) { 340 #else 341 for(25) { 342 #endif 343 // Pick two lists at random 344 // unsigned ri = __tls_rand(); 345 // unsigned rj = __tls_rand(); 346 unsigned ri = __tls_rand_bck(); 347 unsigned rj = __tls_rand_bck(); 348 349 unsigned i, j; 350 __attribute__((unused)) bool locali, localj; 351 [i, locali] = idx_from_r(ri, preferred); 352 [j, localj] = idx_from_r(rj, preferred); 353 354 #if !defined(__CFA_NO_STATISTICS__) 355 if(locali) { 356 __tls_stats()->ready.pick.pop.local++; 357 } 358 if(localj) { 359 __tls_stats()->ready.pick.pop.local++; 360 } 361 #endif 362 363 i %= count; 364 j %= count; 365 366 // try popping from the 2 picked lists 367 struct $thread * thrd = try_pop(cltr, i, j); 368 if(thrd) { 369 #if defined(BIAS) && !defined(__CFA_NO_STATISTICS__) 370 if( locali || localj ) __tls_stats()->ready.pick.pop.lsuccess++; 371 #endif 372 return thrd; 373 } 374 } 375 376 // All lanes where empty return 0p 377 return 0p; 378 } 379 380 __attribute__((hot)) struct $thread * pop_slow(struct cluster * cltr) with (cltr->ready_queue) { 503 // try to pop from any lanes making sure you don't miss any threads push 504 // before the start of the function 505 static inline struct $thread * search(struct cluster * cltr) with (cltr->ready_queue) { 381 506 /* paranoid */ verify( lanes.count > 0 ); 382 507 unsigned count = __atomic_load_n( &lanes.count, __ATOMIC_RELAXED ); … … 384 509 for(i; count) { 385 510 unsigned idx = (offset + i) % count; 386 struct $thread * thrd = try_pop(cltr, idx );511 struct $thread * thrd = try_pop(cltr, idx __STATS(, __tls_stats()->ready.pop.search)); 387 512 if(thrd) { 388 513 return thrd; … … 394 519 } 395 520 396 397 521 //----------------------------------------------------------------------- 398 // Given 2 indexes, pick the list with the oldest push an try to pop from it 399 static inline struct $thread * try_pop(struct cluster * cltr, unsigned i, unsigned j) with (cltr->ready_queue) { 400 #if !defined(__CFA_NO_STATISTICS__) 401 __tls_stats()->ready.pick.pop.attempt++; 402 #endif 403 404 // Pick the bet list 405 int w = i; 406 if( __builtin_expect(!is_empty(lanes.data[j]), true) ) { 407 w = (ts(lanes.data[i]) < ts(lanes.data[j])) ? i : j; 408 } 409 410 return try_pop(cltr, w); 411 } 412 413 static inline struct $thread * try_pop(struct cluster * cltr, unsigned w) with (cltr->ready_queue) { 414 // Get relevant elements locally 415 __intrusive_lane_t & lane = lanes.data[w]; 416 417 // If list looks empty retry 418 if( is_empty(lane) ) return 0p; 419 420 // If we can't get the lock retry 421 if( !__atomic_try_acquire(&lane.lock) ) return 0p; 422 423 424 // If list is empty, unlock and retry 425 if( is_empty(lane) ) { 426 __atomic_unlock(&lane.lock); 427 return 0p; 428 } 429 430 // Actually pop the list 431 struct $thread * thrd; 432 thrd = pop(lane); 433 434 /* paranoid */ verify(thrd); 435 /* paranoid */ verify(lane.lock); 436 437 #ifdef USE_SNZI 438 // If this was the last element in the lane 439 if(emptied) { 440 depart( snzi, w ); 441 } 442 #endif 443 444 // Unlock and return 445 __atomic_unlock(&lane.lock); 446 447 // Update statistics 448 #if !defined(__CFA_NO_STATISTICS__) 449 __tls_stats()->ready.pick.pop.success++; 450 #endif 451 452 // Update the thread bias 453 thrd->link.preferred = w / 4; 454 455 // return the popped thread 456 return thrd; 457 } 458 //----------------------------------------------------------------------- 459 460 bool remove_head(struct cluster * cltr, struct $thread * thrd) with (cltr->ready_queue) { 461 for(i; lanes.count) { 462 __intrusive_lane_t & lane = lanes.data[i]; 463 464 bool removed = false; 465 466 __atomic_acquire(&lane.lock); 467 if(head(lane)->link.next == thrd) { 468 $thread * pthrd; 469 pthrd = pop(lane); 470 471 /* paranoid */ verify( pthrd == thrd ); 472 473 removed = true; 474 #ifdef USE_SNZI 475 if(emptied) { 476 depart( snzi, i ); 477 } 478 #endif 479 } 480 __atomic_unlock(&lane.lock); 481 482 if( removed ) return true; 483 } 484 return false; 485 } 486 487 //----------------------------------------------------------------------- 488 522 // Check that all the intrusive queues in the data structure are still consistent 489 523 static void check( __ready_queue_t & q ) with (q) { 490 #if defined(__CFA_WITH_VERIFY__) 524 #if defined(__CFA_WITH_VERIFY__) && !defined(USE_MPSC) 491 525 { 492 526 for( idx ; lanes.count ) { … … 499 533 assert(tail(sl)->link.prev->link.next == tail(sl) ); 500 534 501 if( sl.before.link.ts == 0l) {535 if(is_empty(sl)) { 502 536 assert(tail(sl)->link.prev == head(sl)); 503 537 assert(head(sl)->link.next == tail(sl)); … … 511 545 } 512 546 547 //----------------------------------------------------------------------- 548 // Given 2 indexes, pick the list with the oldest push an try to pop from it 549 static inline struct $thread * try_pop(struct cluster * cltr, unsigned i, unsigned j __STATS(, __stats_readyQ_pop_t & stats)) with (cltr->ready_queue) { 550 // Pick the bet list 551 int w = i; 552 if( __builtin_expect(!is_empty(lanes.data[j]), true) ) { 553 w = (ts(lanes.data[i]) < ts(lanes.data[j])) ? i : j; 554 } 555 556 return try_pop(cltr, w __STATS(, stats)); 557 } 558 513 559 // Call this function of the intrusive list was moved using memcpy 514 560 // fixes the list so that the pointers back to anchors aren't left dangling 515 561 static inline void fix(__intrusive_lane_t & ll) { 516 // if the list is not empty then follow he pointer and fix its reverse 517 if(!is_empty(ll)) { 518 head(ll)->link.next->link.prev = head(ll); 519 tail(ll)->link.prev->link.next = tail(ll); 520 } 521 // Otherwise just reset the list 522 else { 523 verify(tail(ll)->link.next == 0p); 524 tail(ll)->link.prev = head(ll); 525 head(ll)->link.next = tail(ll); 526 verify(head(ll)->link.prev == 0p); 527 } 562 #if !defined(USE_MPSC) 563 // if the list is not empty then follow he pointer and fix its reverse 564 if(!is_empty(ll)) { 565 head(ll)->link.next->link.prev = head(ll); 566 tail(ll)->link.prev->link.next = tail(ll); 567 } 568 // Otherwise just reset the list 569 else { 570 verify(tail(ll)->link.next == 0p); 571 tail(ll)->link.prev = head(ll); 572 head(ll)->link.next = tail(ll); 573 verify(head(ll)->link.prev == 0p); 574 } 575 #endif 576 } 577 578 static void assign_list(unsigned & value, dlist(processor, processor) & list, unsigned count) { 579 processor * it = &list`first; 580 for(unsigned i = 0; i < count; i++) { 581 /* paranoid */ verifyf( it, "Unexpected null iterator, at index %u of %u\n", i, count); 582 it->rdq.id = value; 583 it->rdq.target = -1u; 584 value += READYQ_SHARD_FACTOR; 585 it = &(*it)`next; 586 } 587 } 588 589 static void reassign_cltr_id(struct cluster * cltr) { 590 unsigned preferred = 0; 591 assign_list(preferred, cltr->procs.actives, cltr->procs.total - cltr->procs.idle); 592 assign_list(preferred, cltr->procs.idles , cltr->procs.idle ); 593 } 594 595 static void fix_times( struct cluster * cltr ) with( cltr->ready_queue ) { 596 #if defined(USE_WORK_STEALING) 597 lanes.tscs = alloc(lanes.count, lanes.tscs`realloc); 598 for(i; lanes.count) { 599 lanes.tscs[i].tv = ts(lanes.data[i]); 600 } 601 #endif 528 602 } 529 603 530 604 // Grow the ready queue 531 void ready_queue_grow (struct cluster * cltr, int target) { 605 void ready_queue_grow(struct cluster * cltr) { 606 size_t ncount; 607 int target = cltr->procs.total; 608 532 609 /* paranoid */ verify( ready_mutate_islocked() ); 533 610 __cfadbg_print_safe(ready_queue, "Kernel : Growing ready queue\n"); … … 538 615 // grow the ready queue 539 616 with( cltr->ready_queue ) { 540 #ifdef USE_SNZI541 ^(snzi){};542 #endif543 544 617 // Find new count 545 618 // Make sure we always have atleast 1 list 546 size_t ncount = target >= 2 ? target * 4: 1; 619 if(target >= 2) { 620 ncount = target * READYQ_SHARD_FACTOR; 621 } else { 622 ncount = SEQUENTIAL_SHARD; 623 } 547 624 548 625 // Allocate new array (uses realloc and memcpies the data) … … 561 638 // Update original 562 639 lanes.count = ncount; 563 564 #ifdef USE_SNZI 565 // Re-create the snzi 566 snzi{ log2( lanes.count / 8 ) }; 567 for( idx; (size_t)lanes.count ) { 568 if( !is_empty(lanes.data[idx]) ) { 569 arrive(snzi, idx); 570 } 571 } 572 #endif 573 } 640 } 641 642 fix_times(cltr); 643 644 reassign_cltr_id(cltr); 574 645 575 646 // Make sure that everything is consistent … … 582 653 583 654 // Shrink the ready queue 584 void ready_queue_shrink(struct cluster * cltr , int target) {655 void ready_queue_shrink(struct cluster * cltr) { 585 656 /* paranoid */ verify( ready_mutate_islocked() ); 586 657 __cfadbg_print_safe(ready_queue, "Kernel : Shrinking ready queue\n"); … … 589 660 /* paranoid */ check( cltr->ready_queue ); 590 661 662 int target = cltr->procs.total; 663 591 664 with( cltr->ready_queue ) { 592 #ifdef USE_SNZI593 ^(snzi){};594 #endif595 596 665 // Remember old count 597 666 size_t ocount = lanes.count; … … 599 668 // Find new count 600 669 // Make sure we always have atleast 1 list 601 lanes.count = target >= 2 ? target * 4: 1;670 lanes.count = target >= 2 ? target * READYQ_SHARD_FACTOR: SEQUENTIAL_SHARD; 602 671 /* paranoid */ verify( ocount >= lanes.count ); 603 /* paranoid */ verify( lanes.count == target * 4|| target < 2 );672 /* paranoid */ verify( lanes.count == target * READYQ_SHARD_FACTOR || target < 2 ); 604 673 605 674 // for printing count the number of displaced threads … … 644 713 fix(lanes.data[idx]); 645 714 } 646 647 #ifdef USE_SNZI 648 // Re-create the snzi 649 snzi{ log2( lanes.count / 8 ) }; 650 for( idx; (size_t)lanes.count ) { 651 if( !is_empty(lanes.data[idx]) ) { 652 arrive(snzi, idx); 653 } 654 } 655 #endif 656 } 715 } 716 717 fix_times(cltr); 718 719 reassign_cltr_id(cltr); 657 720 658 721 // Make sure that everything is consistent -
libcfa/src/concurrency/ready_subqueue.hfa
rfeacef9 r5407cdc 2 2 3 3 #define __CFA_NO_SCHED_STATS__ 4 5 #include "containers/queueLockFree.hfa" 4 6 5 7 // Intrusives lanes which are used by the relaxed ready queue 6 8 struct __attribute__((aligned(128))) __intrusive_lane_t { 7 9 8 // anchor for the head and the tail of the queue 9 __attribute__((aligned(128))) struct __sentinel_t { 10 // Link lists fields 11 // instrusive link field for threads 12 // must be exactly as in $thread 13 __thread_desc_link link; 14 } before, after; 10 #if defined(USE_MPSC) 11 mpsc_queue($thread) queue; 12 __attribute__((aligned(128))) 13 #else 14 // anchor for the head and the tail of the queue 15 __attribute__((aligned(128))) struct __sentinel_t { 16 // Link lists fields 17 // instrusive link field for threads 18 // must be exactly as in $thread 19 __thread_desc_link link; 20 } before, after; 21 #endif 15 22 16 23 // spin lock protecting the queue … … 35 42 // Get the head pointer (one before the first element) from the anchor 36 43 static inline $thread * head(const __intrusive_lane_t & this) { 37 $thread * rhead = ($thread *)( 38 (uintptr_t)( &this.before ) - offsetof( $thread, link ) 39 ); 40 /* paranoid */ verify(rhead); 41 return rhead; 44 #if defined(USE_MPSC) 45 return this.queue.head; 46 #else 47 $thread * rhead = ($thread *)( 48 (uintptr_t)( &this.before ) - offsetof( $thread, link ) 49 ); 50 /* paranoid */ verify(rhead); 51 return rhead; 52 #endif 42 53 } 43 54 44 55 // Get the tail pointer (one after the last element) from the anchor 45 56 static inline $thread * tail(const __intrusive_lane_t & this) { 46 $thread * rtail = ($thread *)( 47 (uintptr_t)( &this.after ) - offsetof( $thread, link ) 48 ); 49 /* paranoid */ verify(rtail); 50 return rtail; 57 #if defined(USE_MPSC) 58 return this.queue.tail; 59 #else 60 $thread * rtail = ($thread *)( 61 (uintptr_t)( &this.after ) - offsetof( $thread, link ) 62 ); 63 /* paranoid */ verify(rtail); 64 return rtail; 65 #endif 51 66 } 52 67 … … 55 70 this.lock = false; 56 71 57 this.before.link.prev = 0p; 58 this.before.link.next = tail(this); 59 this.before.link.ts = 0; 60 61 this.after .link.prev = head(this); 62 this.after .link.next = 0p; 63 this.after .link.ts = 0; 64 65 #if !defined(__CFA_NO_SCHED_STATS__) 66 this.stat.diff = 0; 67 this.stat.push = 0; 68 this.stat.pop = 0; 69 #endif 70 71 // We add a boat-load of assertions here because the anchor code is very fragile 72 /* paranoid */ verify(((uintptr_t)( head(this) ) + offsetof( $thread, link )) == (uintptr_t)(&this.before)); 73 /* paranoid */ verify(((uintptr_t)( tail(this) ) + offsetof( $thread, link )) == (uintptr_t)(&this.after )); 74 /* paranoid */ verify(head(this)->link.prev == 0p ); 75 /* paranoid */ verify(head(this)->link.next == tail(this) ); 76 /* paranoid */ verify(tail(this)->link.next == 0p ); 77 /* paranoid */ verify(tail(this)->link.prev == head(this) ); 78 /* paranoid */ verify(&head(this)->link.prev == &this.before.link.prev ); 79 /* paranoid */ verify(&head(this)->link.next == &this.before.link.next ); 80 /* paranoid */ verify(&tail(this)->link.prev == &this.after .link.prev ); 81 /* paranoid */ verify(&tail(this)->link.next == &this.after .link.next ); 82 /* paranoid */ verify(__alignof__(__intrusive_lane_t) == 128); 83 /* paranoid */ verify(__alignof__(this) == 128); 84 /* paranoid */ verifyf(((intptr_t)(&this) % 128) == 0, "Expected address to be aligned %p %% 128 == %zd", &this, ((intptr_t)(&this) % 128)); 72 #if !defined(USE_MPSC) 73 this.before.link.prev = 0p; 74 this.before.link.next = tail(this); 75 this.before.link.ts = 0; 76 77 this.after .link.prev = head(this); 78 this.after .link.next = 0p; 79 this.after .link.ts = 0; 80 81 #if !defined(__CFA_NO_SCHED_STATS__) 82 this.stat.diff = 0; 83 this.stat.push = 0; 84 this.stat.pop = 0; 85 #endif 86 87 // We add a boat-load of assertions here because the anchor code is very fragile 88 /* paranoid */ verify(((uintptr_t)( head(this) ) + offsetof( $thread, link )) == (uintptr_t)(&this.before)); 89 /* paranoid */ verify(((uintptr_t)( tail(this) ) + offsetof( $thread, link )) == (uintptr_t)(&this.after )); 90 /* paranoid */ verify(head(this)->link.prev == 0p ); 91 /* paranoid */ verify(head(this)->link.next == tail(this) ); 92 /* paranoid */ verify(tail(this)->link.next == 0p ); 93 /* paranoid */ verify(tail(this)->link.prev == head(this) ); 94 /* paranoid */ verify(&head(this)->link.prev == &this.before.link.prev ); 95 /* paranoid */ verify(&head(this)->link.next == &this.before.link.next ); 96 /* paranoid */ verify(&tail(this)->link.prev == &this.after .link.prev ); 97 /* paranoid */ verify(&tail(this)->link.next == &this.after .link.next ); 98 /* paranoid */ verify(__alignof__(__intrusive_lane_t) == 128); 99 /* paranoid */ verify(__alignof__(this) == 128); 100 /* paranoid */ verifyf(((intptr_t)(&this) % 128) == 0, "Expected address to be aligned %p %% 128 == %zd", &this, ((intptr_t)(&this) % 128)); 101 #endif 85 102 } 86 103 87 104 // Dtor is trivial 88 105 void ^?{}( __intrusive_lane_t & this ) { 89 // Make sure the list is empty 90 /* paranoid */ verify(head(this)->link.prev == 0p ); 91 /* paranoid */ verify(head(this)->link.next == tail(this) ); 92 /* paranoid */ verify(tail(this)->link.next == 0p ); 93 /* paranoid */ verify(tail(this)->link.prev == head(this) ); 106 #if !defined(USE_MPSC) 107 // Make sure the list is empty 108 /* paranoid */ verify(head(this)->link.prev == 0p ); 109 /* paranoid */ verify(head(this)->link.next == tail(this) ); 110 /* paranoid */ verify(tail(this)->link.next == 0p ); 111 /* paranoid */ verify(tail(this)->link.prev == head(this) ); 112 #endif 94 113 } 95 114 … … 97 116 // returns true of lane was empty before push, false otherwise 98 117 bool push(__intrusive_lane_t & this, $thread * node) { 99 #if defined(__CFA_WITH_VERIFY__) 100 /* paranoid */ verify(this.lock); 101 /* paranoid */ verify(node->link.ts != 0); 102 /* paranoid */ verify(node->link.next == 0p); 103 /* paranoid */ verify(node->link.prev == 0p); 104 /* paranoid */ verify(tail(this)->link.next == 0p); 105 /* paranoid */ verify(head(this)->link.prev == 0p); 106 118 #if defined(USE_MPSC) 119 inline $thread * volatile & ?`next ( $thread * this ) __attribute__((const)) { 120 return this->link.next; 121 } 122 push(this.queue, node); 123 #else 124 #if defined(__CFA_WITH_VERIFY__) 125 /* paranoid */ verify(this.lock); 126 /* paranoid */ verify(node->link.ts != 0); 127 /* paranoid */ verify(node->link.next == 0p); 128 /* paranoid */ verify(node->link.prev == 0p); 129 /* paranoid */ verify(tail(this)->link.next == 0p); 130 /* paranoid */ verify(head(this)->link.prev == 0p); 131 132 if(this.before.link.ts == 0l) { 133 /* paranoid */ verify(tail(this)->link.prev == head(this)); 134 /* paranoid */ verify(head(this)->link.next == tail(this)); 135 } else { 136 /* paranoid */ verify(tail(this)->link.prev != head(this)); 137 /* paranoid */ verify(head(this)->link.next != tail(this)); 138 } 139 #endif 140 141 // Get the relevant nodes locally 142 $thread * tail = tail(this); 143 $thread * prev = tail->link.prev; 144 145 // Do the push 146 node->link.next = tail; 147 node->link.prev = prev; 148 prev->link.next = node; 149 tail->link.prev = node; 150 151 // Update stats 152 #if !defined(__CFA_NO_SCHED_STATS__) 153 this.stat.diff++; 154 this.stat.push++; 155 #endif 156 157 verify(node->link.next == tail(this)); 158 159 // Check if the queue used to be empty 107 160 if(this.before.link.ts == 0l) { 108 /* paranoid */ verify(tail(this)->link.prev == head(this)); 109 /* paranoid */ verify(head(this)->link.next == tail(this)); 110 } else { 111 /* paranoid */ verify(tail(this)->link.prev != head(this)); 112 /* paranoid */ verify(head(this)->link.next != tail(this)); 113 } 114 #endif 115 116 // Get the relevant nodes locally 117 $thread * tail = tail(this); 118 $thread * prev = tail->link.prev; 119 120 // Do the push 121 node->link.next = tail; 122 node->link.prev = prev; 123 prev->link.next = node; 124 tail->link.prev = node; 125 126 // Update stats 127 #if !defined(__CFA_NO_SCHED_STATS__) 128 this.stat.diff++; 129 this.stat.push++; 130 #endif 131 132 verify(node->link.next == tail(this)); 133 134 // Check if the queue used to be empty 135 if(this.before.link.ts == 0l) { 136 this.before.link.ts = node->link.ts; 137 /* paranoid */ verify(node->link.prev == head(this)); 138 return true; 139 } 140 return false; 161 this.before.link.ts = node->link.ts; 162 /* paranoid */ verify(node->link.prev == head(this)); 163 return true; 164 } 165 return false; 166 #endif 141 167 } 142 168 … … 146 172 $thread * pop(__intrusive_lane_t & this) { 147 173 /* paranoid */ verify(this.lock); 148 /* paranoid */ verify(this.before.link.ts != 0ul); 149 150 // Get anchors locally 151 $thread * head = head(this); 152 $thread * tail = tail(this); 153 154 // Get the relevant nodes locally 155 $thread * node = head->link.next; 156 $thread * next = node->link.next; 157 158 /* paranoid */ verify(node != tail); 159 /* paranoid */ verify(node); 160 161 // Do the pop 162 head->link.next = next; 163 next->link.prev = head; 164 node->link.next = 0p; 165 node->link.prev = 0p; 166 167 // Update head time stamp 168 this.before.link.ts = next->link.ts; 169 170 // Update stats 171 #ifndef __CFA_NO_SCHED_STATS__ 172 this.stat.diff--; 173 this.stat.pop ++; 174 #endif 175 176 // Check if we emptied list and return accordingly 177 /* paranoid */ verify(tail(this)->link.next == 0p); 178 /* paranoid */ verify(head(this)->link.prev == 0p); 179 if(next == tail) { 180 /* paranoid */ verify(this.before.link.ts == 0); 181 /* paranoid */ verify(tail(this)->link.prev == head(this)); 182 /* paranoid */ verify(head(this)->link.next == tail(this)); 183 return node; 184 } 185 else { 186 /* paranoid */ verify(next->link.ts != 0); 187 /* paranoid */ verify(tail(this)->link.prev != head(this)); 188 /* paranoid */ verify(head(this)->link.next != tail(this)); 189 /* paranoid */ verify(this.before.link.ts != 0); 190 return node; 191 } 174 #if defined(USE_MPSC) 175 inline $thread * volatile & ?`next ( $thread * this ) __attribute__((const)) { 176 return this->link.next; 177 } 178 return pop(this.queue); 179 #else 180 /* paranoid */ verify(this.before.link.ts != 0ul); 181 182 // Get anchors locally 183 $thread * head = head(this); 184 $thread * tail = tail(this); 185 186 // Get the relevant nodes locally 187 $thread * node = head->link.next; 188 $thread * next = node->link.next; 189 190 /* paranoid */ verify(node != tail); 191 /* paranoid */ verify(node); 192 193 // Do the pop 194 head->link.next = next; 195 next->link.prev = head; 196 node->link.next = 0p; 197 node->link.prev = 0p; 198 199 // Update head time stamp 200 this.before.link.ts = next->link.ts; 201 202 // Update stats 203 #ifndef __CFA_NO_SCHED_STATS__ 204 this.stat.diff--; 205 this.stat.pop ++; 206 #endif 207 208 // Check if we emptied list and return accordingly 209 /* paranoid */ verify(tail(this)->link.next == 0p); 210 /* paranoid */ verify(head(this)->link.prev == 0p); 211 if(next == tail) { 212 /* paranoid */ verify(this.before.link.ts == 0); 213 /* paranoid */ verify(tail(this)->link.prev == head(this)); 214 /* paranoid */ verify(head(this)->link.next == tail(this)); 215 return node; 216 } 217 else { 218 /* paranoid */ verify(next->link.ts != 0); 219 /* paranoid */ verify(tail(this)->link.prev != head(this)); 220 /* paranoid */ verify(head(this)->link.next != tail(this)); 221 /* paranoid */ verify(this.before.link.ts != 0); 222 return node; 223 } 224 #endif 192 225 } 193 226 194 227 // Check whether or not list is empty 195 228 static inline bool is_empty(__intrusive_lane_t & this) { 196 // Cannot verify here since it may not be locked 197 return this.before.link.ts == 0; 229 #if defined(USE_MPSC) 230 return this.queue.head == 0p; 231 #else 232 // Cannot verify here since it may not be locked 233 return this.before.link.ts == 0; 234 #endif 198 235 } 199 236 200 237 // Return the timestamp 201 238 static inline unsigned long long ts(__intrusive_lane_t & this) { 202 // Cannot verify here since it may not be locked 203 return this.before.link.ts; 204 } 239 #if defined(USE_MPSC) 240 $thread * tl = this.queue.head; 241 if(!tl) return -1ull; 242 return tl->link.ts; 243 #else 244 // Cannot verify here since it may not be locked 245 return this.before.link.ts; 246 #endif 247 } 248 249 // Aligned timestamps which are used by the relaxed ready queue 250 struct __attribute__((aligned(128))) __timestamp_t { 251 volatile unsigned long long tv; 252 }; 253 254 void ?{}(__timestamp_t & this) { this.tv = 0; } 255 void ^?{}(__timestamp_t & this) {} -
libcfa/src/concurrency/stats.cfa
rfeacef9 r5407cdc 5 5 #include <inttypes.h> 6 6 #include "bits/debug.hfa" 7 #include "bits/locks.hfa" 7 8 #include "stats.hfa" 9 #include "strstream.hfa" 8 10 9 11 #if !defined(__CFA_NO_STATISTICS__) 10 12 void __init_stats( struct __stats_t * stats ) { 11 stats->ready.pick.push.attempt = 0; 12 stats->ready.pick.push.success = 0; 13 stats->ready.pick.push.local = 0; 14 stats->ready.pick.push.lsuccess = 0; 15 stats->ready.pick.pop .probe = 0; 16 stats->ready.pick.pop .attempt = 0; 17 stats->ready.pick.pop .success = 0; 18 stats->ready.pick.pop .local = 0; 19 stats->ready.pick.pop .lsuccess = 0; 13 stats->ready.push.local.attempt = 0; 14 stats->ready.push.local.success = 0; 15 stats->ready.push.share.attempt = 0; 16 stats->ready.push.share.success = 0; 17 stats->ready.push.extrn.attempt = 0; 18 stats->ready.push.extrn.success = 0; 19 stats->ready.pop.local .attempt = 0; 20 stats->ready.pop.local .success = 0; 21 stats->ready.pop.local .elock = 0; 22 stats->ready.pop.local .eempty = 0; 23 stats->ready.pop.local .espec = 0; 24 stats->ready.pop.help .attempt = 0; 25 stats->ready.pop.help .success = 0; 26 stats->ready.pop.help .elock = 0; 27 stats->ready.pop.help .eempty = 0; 28 stats->ready.pop.help .espec = 0; 29 stats->ready.pop.steal .attempt = 0; 30 stats->ready.pop.steal .success = 0; 31 stats->ready.pop.steal .elock = 0; 32 stats->ready.pop.steal .eempty = 0; 33 stats->ready.pop.steal .espec = 0; 34 stats->ready.pop.search.attempt = 0; 35 stats->ready.pop.search.success = 0; 36 stats->ready.pop.search.elock = 0; 37 stats->ready.pop.search.eempty = 0; 38 stats->ready.pop.search.espec = 0; 20 39 stats->ready.threads.migration = 0; 40 stats->ready.threads.extunpark = 0; 41 stats->ready.threads.threads = 0; 21 42 stats->ready.sleep.halts = 0; 22 43 stats->ready.sleep.cancels = 0; … … 25 46 26 47 #if defined(CFA_HAVE_LINUX_IO_URING_H) 27 stats->io.submit_q.submit_avg.rdy = 0; 28 stats->io.submit_q.submit_avg.csm = 0; 29 stats->io.submit_q.submit_avg.cnt = 0; 30 stats->io.submit_q.look_avg.val = 0; 31 stats->io.submit_q.look_avg.cnt = 0; 32 stats->io.submit_q.look_avg.block = 0; 33 stats->io.submit_q.alloc_avg.val = 0; 34 stats->io.submit_q.alloc_avg.cnt = 0; 35 stats->io.submit_q.alloc_avg.block = 0; 36 stats->io.submit_q.helped = 0; 37 stats->io.submit_q.leader = 0; 38 stats->io.submit_q.busy = 0; 39 stats->io.complete_q.completed_avg.val = 0; 40 stats->io.complete_q.completed_avg.cnt = 0; 41 stats->io.complete_q.blocks = 0; 48 stats->io.alloc.fast = 0; 49 stats->io.alloc.slow = 0; 50 stats->io.alloc.fail = 0; 51 stats->io.alloc.revoke = 0; 52 stats->io.alloc.block = 0; 53 stats->io.submit.fast = 0; 54 stats->io.submit.slow = 0; 55 stats->io.flush.external = 0; 56 stats->io.calls.flush = 0; 57 stats->io.calls.submitted = 0; 58 stats->io.calls.drain = 0; 59 stats->io.calls.completed = 0; 60 stats->io.calls.errors.busy = 0; 61 stats->io.poller.sleeps = 0; 62 #endif 63 64 #if defined(CFA_STATS_ARRAY) 65 stats->array.values = alloc(CFA_STATS_ARRAY); 66 stats->array.cnt = 0; 42 67 #endif 43 68 } 44 69 45 70 void __tally_stats( struct __stats_t * cltr, struct __stats_t * proc ) { 46 __atomic_fetch_add( &cltr->ready.pick.push.attempt , proc->ready.pick.push.attempt , __ATOMIC_SEQ_CST ); proc->ready.pick.push.attempt = 0; 47 __atomic_fetch_add( &cltr->ready.pick.push.success , proc->ready.pick.push.success , __ATOMIC_SEQ_CST ); proc->ready.pick.push.success = 0; 48 __atomic_fetch_add( &cltr->ready.pick.push.local , proc->ready.pick.push.local , __ATOMIC_SEQ_CST ); proc->ready.pick.push.local = 0; 49 __atomic_fetch_add( &cltr->ready.pick.push.lsuccess, proc->ready.pick.push.lsuccess, __ATOMIC_SEQ_CST ); proc->ready.pick.push.lsuccess = 0; 50 __atomic_fetch_add( &cltr->ready.pick.pop .probe , proc->ready.pick.pop .probe , __ATOMIC_SEQ_CST ); proc->ready.pick.pop .probe = 0; 51 __atomic_fetch_add( &cltr->ready.pick.pop .attempt , proc->ready.pick.pop .attempt , __ATOMIC_SEQ_CST ); proc->ready.pick.pop .attempt = 0; 52 __atomic_fetch_add( &cltr->ready.pick.pop .success , proc->ready.pick.pop .success , __ATOMIC_SEQ_CST ); proc->ready.pick.pop .success = 0; 53 __atomic_fetch_add( &cltr->ready.pick.pop .local , proc->ready.pick.pop .local , __ATOMIC_SEQ_CST ); proc->ready.pick.pop .local = 0; 54 __atomic_fetch_add( &cltr->ready.pick.pop .lsuccess, proc->ready.pick.pop .lsuccess, __ATOMIC_SEQ_CST ); proc->ready.pick.pop .lsuccess = 0; 71 __atomic_fetch_add( &cltr->ready.push.local.attempt, proc->ready.push.local.attempt, __ATOMIC_SEQ_CST ); proc->ready.push.local.attempt = 0; 72 __atomic_fetch_add( &cltr->ready.push.local.success, proc->ready.push.local.success, __ATOMIC_SEQ_CST ); proc->ready.push.local.success = 0; 73 __atomic_fetch_add( &cltr->ready.push.share.attempt, proc->ready.push.share.attempt, __ATOMIC_SEQ_CST ); proc->ready.push.share.attempt = 0; 74 __atomic_fetch_add( &cltr->ready.push.share.success, proc->ready.push.share.success, __ATOMIC_SEQ_CST ); proc->ready.push.share.success = 0; 75 __atomic_fetch_add( &cltr->ready.push.extrn.attempt, proc->ready.push.extrn.attempt, __ATOMIC_SEQ_CST ); proc->ready.push.extrn.attempt = 0; 76 __atomic_fetch_add( &cltr->ready.push.extrn.success, proc->ready.push.extrn.success, __ATOMIC_SEQ_CST ); proc->ready.push.extrn.success = 0; 77 __atomic_fetch_add( &cltr->ready.pop.local .attempt, proc->ready.pop.local .attempt, __ATOMIC_SEQ_CST ); proc->ready.pop.local .attempt = 0; 78 __atomic_fetch_add( &cltr->ready.pop.local .success, proc->ready.pop.local .success, __ATOMIC_SEQ_CST ); proc->ready.pop.local .success = 0; 79 __atomic_fetch_add( &cltr->ready.pop.local .elock , proc->ready.pop.local .elock , __ATOMIC_SEQ_CST ); proc->ready.pop.local .elock = 0; 80 __atomic_fetch_add( &cltr->ready.pop.local .eempty , proc->ready.pop.local .eempty , __ATOMIC_SEQ_CST ); proc->ready.pop.local .eempty = 0; 81 __atomic_fetch_add( &cltr->ready.pop.local .espec , proc->ready.pop.local .espec , __ATOMIC_SEQ_CST ); proc->ready.pop.local .espec = 0; 82 __atomic_fetch_add( &cltr->ready.pop.help .attempt, proc->ready.pop.help .attempt, __ATOMIC_SEQ_CST ); proc->ready.pop.help .attempt = 0; 83 __atomic_fetch_add( &cltr->ready.pop.help .success, proc->ready.pop.help .success, __ATOMIC_SEQ_CST ); proc->ready.pop.help .success = 0; 84 __atomic_fetch_add( &cltr->ready.pop.help .elock , proc->ready.pop.help .elock , __ATOMIC_SEQ_CST ); proc->ready.pop.help .elock = 0; 85 __atomic_fetch_add( &cltr->ready.pop.help .eempty , proc->ready.pop.help .eempty , __ATOMIC_SEQ_CST ); proc->ready.pop.help .eempty = 0; 86 __atomic_fetch_add( &cltr->ready.pop.help .espec , proc->ready.pop.help .espec , __ATOMIC_SEQ_CST ); proc->ready.pop.help .espec = 0; 87 __atomic_fetch_add( &cltr->ready.pop.steal .attempt, proc->ready.pop.steal .attempt, __ATOMIC_SEQ_CST ); proc->ready.pop.steal .attempt = 0; 88 __atomic_fetch_add( &cltr->ready.pop.steal .success, proc->ready.pop.steal .success, __ATOMIC_SEQ_CST ); proc->ready.pop.steal .success = 0; 89 __atomic_fetch_add( &cltr->ready.pop.steal .elock , proc->ready.pop.steal .elock , __ATOMIC_SEQ_CST ); proc->ready.pop.steal .elock = 0; 90 __atomic_fetch_add( &cltr->ready.pop.steal .eempty , proc->ready.pop.steal .eempty , __ATOMIC_SEQ_CST ); proc->ready.pop.steal .eempty = 0; 91 __atomic_fetch_add( &cltr->ready.pop.steal .espec , proc->ready.pop.steal .espec , __ATOMIC_SEQ_CST ); proc->ready.pop.steal .espec = 0; 92 __atomic_fetch_add( &cltr->ready.pop.search.attempt, proc->ready.pop.search.attempt, __ATOMIC_SEQ_CST ); proc->ready.pop.search.attempt = 0; 93 __atomic_fetch_add( &cltr->ready.pop.search.success, proc->ready.pop.search.success, __ATOMIC_SEQ_CST ); proc->ready.pop.search.success = 0; 94 __atomic_fetch_add( &cltr->ready.pop.search.elock , proc->ready.pop.search.elock , __ATOMIC_SEQ_CST ); proc->ready.pop.search.elock = 0; 95 __atomic_fetch_add( &cltr->ready.pop.search.eempty , proc->ready.pop.search.eempty , __ATOMIC_SEQ_CST ); proc->ready.pop.search.eempty = 0; 96 __atomic_fetch_add( &cltr->ready.pop.search.espec , proc->ready.pop.search.espec , __ATOMIC_SEQ_CST ); proc->ready.pop.search.espec = 0; 55 97 __atomic_fetch_add( &cltr->ready.threads.migration , proc->ready.threads.migration , __ATOMIC_SEQ_CST ); proc->ready.threads.migration = 0; 98 __atomic_fetch_add( &cltr->ready.threads.extunpark , proc->ready.threads.extunpark , __ATOMIC_SEQ_CST ); proc->ready.threads.extunpark = 0; 99 __atomic_fetch_add( &cltr->ready.threads.threads , proc->ready.threads.threads , __ATOMIC_SEQ_CST ); proc->ready.threads.threads = 0; 56 100 __atomic_fetch_add( &cltr->ready.sleep.halts , proc->ready.sleep.halts , __ATOMIC_SEQ_CST ); proc->ready.sleep.halts = 0; 57 101 __atomic_fetch_add( &cltr->ready.sleep.cancels , proc->ready.sleep.cancels , __ATOMIC_SEQ_CST ); proc->ready.sleep.cancels = 0; … … 60 104 61 105 #if defined(CFA_HAVE_LINUX_IO_URING_H) 62 __atomic_fetch_add( &cltr->io.submit_q.submit_avg.rdy , proc->io.submit_q.submit_avg.rdy , __ATOMIC_SEQ_CST ); proc->io.submit_q.submit_avg.rdy = 0; 63 __atomic_fetch_add( &cltr->io.submit_q.submit_avg.csm , proc->io.submit_q.submit_avg.csm , __ATOMIC_SEQ_CST ); proc->io.submit_q.submit_avg.csm = 0; 64 __atomic_fetch_add( &cltr->io.submit_q.submit_avg.avl , proc->io.submit_q.submit_avg.avl , __ATOMIC_SEQ_CST ); proc->io.submit_q.submit_avg.avl = 0; 65 __atomic_fetch_add( &cltr->io.submit_q.submit_avg.cnt , proc->io.submit_q.submit_avg.cnt , __ATOMIC_SEQ_CST ); proc->io.submit_q.submit_avg.cnt = 0; 66 __atomic_fetch_add( &cltr->io.submit_q.look_avg.val , proc->io.submit_q.look_avg.val , __ATOMIC_SEQ_CST ); proc->io.submit_q.look_avg.val = 0; 67 __atomic_fetch_add( &cltr->io.submit_q.look_avg.cnt , proc->io.submit_q.look_avg.cnt , __ATOMIC_SEQ_CST ); proc->io.submit_q.look_avg.cnt = 0; 68 __atomic_fetch_add( &cltr->io.submit_q.look_avg.block , proc->io.submit_q.look_avg.block , __ATOMIC_SEQ_CST ); proc->io.submit_q.look_avg.block = 0; 69 __atomic_fetch_add( &cltr->io.submit_q.alloc_avg.val , proc->io.submit_q.alloc_avg.val , __ATOMIC_SEQ_CST ); proc->io.submit_q.alloc_avg.val = 0; 70 __atomic_fetch_add( &cltr->io.submit_q.alloc_avg.cnt , proc->io.submit_q.alloc_avg.cnt , __ATOMIC_SEQ_CST ); proc->io.submit_q.alloc_avg.cnt = 0; 71 __atomic_fetch_add( &cltr->io.submit_q.alloc_avg.block , proc->io.submit_q.alloc_avg.block , __ATOMIC_SEQ_CST ); proc->io.submit_q.alloc_avg.block = 0; 72 __atomic_fetch_add( &cltr->io.submit_q.helped , proc->io.submit_q.helped , __ATOMIC_SEQ_CST ); proc->io.submit_q.helped = 0; 73 __atomic_fetch_add( &cltr->io.submit_q.leader , proc->io.submit_q.leader , __ATOMIC_SEQ_CST ); proc->io.submit_q.leader = 0; 74 __atomic_fetch_add( &cltr->io.submit_q.busy , proc->io.submit_q.busy , __ATOMIC_SEQ_CST ); proc->io.submit_q.busy = 0; 75 __atomic_fetch_add( &cltr->io.complete_q.completed_avg.val, proc->io.complete_q.completed_avg.val, __ATOMIC_SEQ_CST ); proc->io.complete_q.completed_avg.val = 0; 76 __atomic_fetch_add( &cltr->io.complete_q.completed_avg.cnt, proc->io.complete_q.completed_avg.cnt, __ATOMIC_SEQ_CST ); proc->io.complete_q.completed_avg.cnt = 0; 77 __atomic_fetch_add( &cltr->io.complete_q.blocks , proc->io.complete_q.blocks , __ATOMIC_SEQ_CST ); proc->io.complete_q.blocks = 0; 106 __atomic_fetch_add( &cltr->io.alloc.fast , proc->io.alloc.fast , __ATOMIC_SEQ_CST ); proc->io.alloc.fast = 0; 107 __atomic_fetch_add( &cltr->io.alloc.slow , proc->io.alloc.slow , __ATOMIC_SEQ_CST ); proc->io.alloc.slow = 0; 108 __atomic_fetch_add( &cltr->io.alloc.fail , proc->io.alloc.fail , __ATOMIC_SEQ_CST ); proc->io.alloc.fail = 0; 109 __atomic_fetch_add( &cltr->io.alloc.revoke , proc->io.alloc.revoke , __ATOMIC_SEQ_CST ); proc->io.alloc.revoke = 0; 110 __atomic_fetch_add( &cltr->io.alloc.block , proc->io.alloc.block , __ATOMIC_SEQ_CST ); proc->io.alloc.block = 0; 111 __atomic_fetch_add( &cltr->io.submit.fast , proc->io.submit.fast , __ATOMIC_SEQ_CST ); proc->io.submit.fast = 0; 112 __atomic_fetch_add( &cltr->io.submit.slow , proc->io.submit.slow , __ATOMIC_SEQ_CST ); proc->io.submit.slow = 0; 113 __atomic_fetch_add( &cltr->io.flush.external , proc->io.flush.external , __ATOMIC_SEQ_CST ); proc->io.flush.external = 0; 114 __atomic_fetch_add( &cltr->io.calls.flush , proc->io.calls.flush , __ATOMIC_SEQ_CST ); proc->io.calls.flush = 0; 115 __atomic_fetch_add( &cltr->io.calls.submitted , proc->io.calls.submitted , __ATOMIC_SEQ_CST ); proc->io.calls.submitted = 0; 116 __atomic_fetch_add( &cltr->io.calls.drain , proc->io.calls.drain , __ATOMIC_SEQ_CST ); proc->io.calls.drain = 0; 117 __atomic_fetch_add( &cltr->io.calls.completed , proc->io.calls.completed , __ATOMIC_SEQ_CST ); proc->io.calls.completed = 0; 118 __atomic_fetch_add( &cltr->io.calls.errors.busy, proc->io.calls.errors.busy, __ATOMIC_SEQ_CST ); proc->io.calls.errors.busy = 0; 119 __atomic_fetch_add( &cltr->io.poller.sleeps , proc->io.poller.sleeps , __ATOMIC_SEQ_CST ); proc->io.poller.sleeps = 0; 78 120 #endif 79 121 } 80 122 123 #define eng3(X) (ws(3, 3, unit(eng( X )))) 124 81 125 void __print_stats( struct __stats_t * stats, int flags, const char * type, const char * name, void * id ) with( *stats ) { 82 126 127 char buf[1024]; 128 ostrstream sstr = { buf, 1024 }; 129 83 130 if( flags & CFA_STATS_READY_Q ) { 84 double push_sur = (100.0 * ((double)ready.pick.push.success) / ready.pick.push.attempt); 85 double pop_sur = (100.0 * ((double)ready.pick.pop .success) / ready.pick.pop .attempt); 86 87 double push_len = ((double)ready.pick.push.attempt) / ready.pick.push.success; 88 double pop_len = ((double)ready.pick.pop .attempt) / ready.pick.pop .success; 89 90 double lpush_sur = (100.0 * ((double)ready.pick.push.lsuccess) / ready.pick.push.local); 91 double lpop_sur = (100.0 * ((double)ready.pick.pop .lsuccess) / ready.pick.pop .local); 92 93 double lpush_len = ((double)ready.pick.push.local) / ready.pick.push.lsuccess; 94 double lpop_len = ((double)ready.pick.pop .local) / ready.pick.pop .lsuccess; 95 96 __cfaabi_bits_print_safe( STDOUT_FILENO, 97 "----- %s \"%s\" (%p) - Ready Q Stats -----\n" 98 "- total threads run : %'15" PRIu64 "\n" 99 "- total threads scheduled: %'15" PRIu64 "\n" 100 "- push average probe len : %'18.2lf, %'18.2lf%% (%'15" PRIu64 " attempts)\n" 101 "- pop average probe len : %'18.2lf, %'18.2lf%% (%'15" PRIu64 " attempts)\n" 102 "- local push avg prb len : %'18.2lf, %'18.2lf%% (%'15" PRIu64 " attempts)\n" 103 "- local pop avg prb len : %'18.2lf, %'18.2lf%% (%'15" PRIu64 " attempts)\n" 104 "- thread migrations : %'15" PRIu64 "\n" 105 "- Idle Sleep -\n" 106 "-- halts : %'15" PRIu64 "\n" 107 "-- cancelled halts : %'15" PRIu64 "\n" 108 "-- schedule wake : %'15" PRIu64 "\n" 109 "-- wake on exit : %'15" PRIu64 "\n" 110 "\n" 111 , type, name, id 112 , ready.pick.pop.success 113 , ready.pick.push.success 114 , push_len, push_sur, ready.pick.push.attempt 115 , pop_len , pop_sur , ready.pick.pop .attempt 116 , lpush_len, lpush_sur, ready.pick.push.local 117 , lpop_len , lpop_sur , ready.pick.pop .local 118 , ready.threads.migration 119 , ready.sleep.halts, ready.sleep.cancels, ready.sleep.wakes, ready.sleep.exits 120 ); 131 132 sstr | "----- " | type | "\"" | name | "\" (" | "" | id | "" | ") - Ready Q Stats -----"; 133 134 uint64_t totalR = ready.pop.local.success + ready.pop.help.success + ready.pop.steal.success + ready.pop.search.success; 135 uint64_t totalS = ready.push.local.success + ready.push.share.success + ready.push.extrn.success; 136 sstr | "- totals : " | eng3(totalR) | "run," | eng3(totalS) | "schd (" | eng3(ready.push.extrn.success) | "ext," | eng3(ready.threads.migration) | "mig," | eng3(ready.threads.extunpark) | " eupk)"; 137 138 double push_len = ((double)ready.push.local.attempt + ready.push.share.attempt + ready.push.extrn.attempt) / totalS; 139 double sLcl_len = ready.push.local.success ? ((double)ready.push.local.attempt) / ready.push.local.success : 0; 140 double sOth_len = ready.push.share.success ? ((double)ready.push.share.attempt) / ready.push.share.success : 0; 141 double sExt_len = ready.push.extrn.success ? ((double)ready.push.extrn.attempt) / ready.push.extrn.success : 0; 142 sstr | "- push avg : " | ws(3, 3, push_len) 143 | "- l: " | eng3(ready.push.local.attempt) | " (" | ws(3, 3, sLcl_len) | ")" 144 | ", s: " | eng3(ready.push.share.attempt) | " (" | ws(3, 3, sOth_len) | ")" 145 | ", e: " | eng3(ready.push.extrn.attempt) | " (" | ws(3, 3, sExt_len) | ")"; 146 147 double rLcl_pc = (100.0 * (double)ready.pop.local .success) / totalR; 148 sstr | "- local : " | eng3(ready.pop.local .success) | "-"| ws(3, 3, rLcl_pc) | '%' 149 | " (" | eng3(ready.pop.local .attempt) | " try," | eng3(ready.pop.local .espec) | " spc," | eng3(ready.pop.local .elock) | " lck," | eng3(ready.pop.local .eempty) | " ept)"; 150 double rHlp_pc = (100.0 * (double)ready.pop.help .success) / totalR; 151 sstr | "- help : " | eng3(ready.pop.help .success) | "-"| ws(3, 3, rHlp_pc) | '%' 152 | " (" | eng3(ready.pop.help .attempt) | " try," | eng3(ready.pop.help .espec) | " spc," | eng3(ready.pop.help .elock) | " lck," | eng3(ready.pop.help .eempty) | " ept)"; 153 double rStl_pc = (100.0 * (double)ready.pop.steal .success) / totalR; 154 sstr | "- steal : " | eng3(ready.pop.steal .success) | "-"| ws(3, 3, rStl_pc) | '%' 155 | " (" | eng3(ready.pop.steal .attempt) | " try," | eng3(ready.pop.steal .espec) | " spc," | eng3(ready.pop.steal .elock) | " lck," | eng3(ready.pop.steal .eempty) | " ept)"; 156 double rSch_pc = (100.0 * (double)ready.pop.search.success) / totalR; 157 sstr | "- search : " | eng3(ready.pop.search.success) | "-"| ws(3, 3, rSch_pc) | '%' 158 | " (" | eng3(ready.pop.search.attempt) | " try," | eng3(ready.pop.search.espec) | " spc," | eng3(ready.pop.search.elock) | " lck," | eng3(ready.pop.search.eempty) | " ept)"; 159 160 sstr | "- Idle Slp : " | eng3(ready.sleep.halts) | "halt," | eng3(ready.sleep.cancels) | "cancel," | eng3(ready.sleep.wakes) | "wake," | eng3(ready.sleep.exits) | "exit"; 161 sstr | nl; 121 162 } 122 163 123 164 #if defined(CFA_HAVE_LINUX_IO_URING_H) 124 165 if( flags & CFA_STATS_IO ) { 125 double avgrdy = ((double)io.submit_q.submit_avg.rdy) / io.submit_q.submit_avg.cnt; 126 double avgcsm = ((double)io.submit_q.submit_avg.csm) / io.submit_q.submit_avg.cnt; 127 128 double lavgv = 0; 129 double lavgb = 0; 130 if(io.submit_q.look_avg.cnt != 0) { 131 lavgv = ((double)io.submit_q.look_avg.val ) / io.submit_q.look_avg.cnt; 132 lavgb = ((double)io.submit_q.look_avg.block) / io.submit_q.look_avg.cnt; 133 } 134 135 double aavgv = 0; 136 double aavgb = 0; 137 if(io.submit_q.alloc_avg.cnt != 0) { 138 aavgv = ((double)io.submit_q.alloc_avg.val ) / io.submit_q.alloc_avg.cnt; 139 aavgb = ((double)io.submit_q.alloc_avg.block) / io.submit_q.alloc_avg.cnt; 140 } 141 142 __cfaabi_bits_print_safe( STDOUT_FILENO, 143 "----- %s \"%s\" (%p) - I/O Stats -----\n" 144 "- total submit calls : %'15" PRIu64 "\n" 145 "- avg ready entries : %'18.2lf\n" 146 "- avg submitted entries : %'18.2lf\n" 147 "- total helped entries : %'15" PRIu64 "\n" 148 "- total leader entries : %'15" PRIu64 "\n" 149 "- total busy submit : %'15" PRIu64 "\n" 150 "- total ready search : %'15" PRIu64 "\n" 151 "- avg ready search len : %'18.2lf\n" 152 "- avg ready search block : %'18.2lf\n" 153 "- total alloc search : %'15" PRIu64 "\n" 154 "- avg alloc search len : %'18.2lf\n" 155 "- avg alloc search block : %'18.2lf\n" 156 "- total wait calls : %'15" PRIu64 "\n" 157 "- avg completion/wait : %'18.2lf\n" 158 "- total completion blocks: %'15" PRIu64 "\n" 159 "\n" 160 , type, name, id 161 , io.submit_q.submit_avg.cnt 162 , avgrdy, avgcsm 163 , io.submit_q.helped, io.submit_q.leader, io.submit_q.busy 164 , io.submit_q.look_avg.cnt 165 , lavgv, lavgb 166 , io.submit_q.alloc_avg.cnt 167 , aavgv, aavgb 168 , io.complete_q.completed_avg.cnt 169 , ((double)io.complete_q.completed_avg.val) / io.complete_q.completed_avg.cnt 170 , io.complete_q.blocks 171 ); 166 sstr | "----- " | type | "\"" | name | "\" (" | "" | id | "" | ") - I/O Stats -----"; 167 168 uint64_t total_allocs = io.alloc.fast + io.alloc.slow; 169 double avgfasta = (100.0 * (double)io.alloc.fast) / total_allocs; 170 sstr | "- total allocations : " | eng3(io.alloc.fast) | "fast," | eng3(io.alloc.slow) | "slow (" | ws(3, 3, avgfasta) | "%)"; 171 sstr | "- failures : " | eng3(io.alloc.fail) | "oom, " | eng3(io.alloc.revoke) | "rvk, " | eng3(io.alloc.block) | "blk"; 172 173 uint64_t total_submits = io.submit.fast + io.submit.slow; 174 double avgfasts = (100.0 * (double)io.submit.fast) / total_submits; 175 sstr | "- total submits : " | eng3(io.submit.fast) | "fast," | eng3(io.submit.slow) | "slow (" | ws(3, 3, avgfasts) | "%)"; 176 sstr | "- flush external : " | eng3(io.flush.external); 177 178 sstr | "- io_uring_enter : " | eng3(io.calls.flush) | " (" | eng3(io.calls.drain) | ", " | eng3(io.calls.errors.busy) | " EBUSY)"; 179 180 double avgsubs = ((double)io.calls.submitted) / io.calls.flush; 181 double avgcomp = ((double)io.calls.completed) / io.calls.drain; 182 sstr | "- submits : " | eng3(io.calls.submitted) | "(" | ws(3, 3, avgsubs) | "/flush)"; 183 sstr | "- completes : " | eng3(io.calls.completed) | "(" | ws(3, 3, avgcomp) | "/drain)"; 184 185 sstr | "- poller sleeping : " | eng3(io.poller.sleeps); 186 sstr | nl; 172 187 } 173 188 #endif 189 190 if(flags) write( sstr, stdout ); 174 191 } 192 193 #if defined(CFA_STATS_ARRAY) 194 extern "C" { 195 #include <stdio.h> 196 #include <errno.h> 197 #include <sys/stat.h> 198 #include <fcntl.h> 199 } 200 201 void __flush_stat( struct __stats_t * this, const char * name, void * handle) { 202 int ret = mkdir(".cfadata", 0755); 203 if(ret < 0 && errno != EEXIST) abort("Failed to create directory .cfadata: %d\n", errno); 204 205 char filename[100]; 206 snprintf(filename, 100, ".cfadata/%s%p.data", name, handle); 207 208 int fd = open(filename, O_WRONLY | O_APPEND | O_CREAT, 0644); 209 if(fd < 0) abort("Failed to create file %s: %d\n", filename, errno); 210 211 for(i; this->array.cnt) { 212 char line[100]; 213 size_t n = snprintf(line, 100, "%llu, %lld\n", this->array.values[i].ts, this->array.values[i].value); 214 write(fd, line, n); 215 } 216 217 this->array.cnt = 0; 218 close(fd); 219 } 220 221 static __spinlock_t stats_lock; 222 223 void __push_stat( struct __stats_t * this, int64_t value, bool external, const char * name, void * handle ) { 224 if(external) lock(stats_lock __cfaabi_dbg_ctx2); 225 226 if( this->array.cnt >= CFA_STATS_ARRAY ) __flush_stat( this, name, handle ); 227 228 size_t idx = this->array.cnt; 229 this->array.cnt++; 230 231 if(external) unlock(stats_lock); 232 233 this->array.values[idx].ts = rdtscl(); 234 this->array.values[idx].value = value; 235 } 236 #endif 175 237 #endif -
libcfa/src/concurrency/stats.hfa
rfeacef9 r5407cdc 1 1 #pragma once 2 3 // #define CFA_STATS_ARRAY 10000 2 4 3 5 #include <stdint.h> … … 14 16 static inline void __print_stats( struct __stats_t *, int, const char *, const char *, void * ) {} 15 17 #else 18 struct __stats_readyQ_pop_t { 19 // number of attemps at poping something 20 volatile uint64_t attempt; 16 21 17 struct __attribute__((aligned(64))) __stats_readQ_t { 22 // number of successes at poping 23 volatile uint64_t success; 24 25 // number of attempts failed due to the lock being held 26 volatile uint64_t elock; 27 28 // number of attempts failed due to the queue being empty (lock held) 29 volatile uint64_t eempty; 30 31 // number of attempts failed due to the queue looking empty (lock not held) 32 volatile uint64_t espec; 33 }; 34 35 struct __attribute__((aligned(64))) __stats_readyQ_t { 36 // Push statistic 18 37 struct { 19 // Push statistic20 38 struct { 21 // number of attemps at pushing something 39 // number of attemps at pushing something to preferred queues 22 40 volatile uint64_t attempt; 23 41 24 // number of successes at pushing 42 // number of successes at pushing to preferred queues 25 43 volatile uint64_t success; 44 } 45 // Stats for local queue within cluster 46 local, 26 47 27 // number of attemps at pushing something to preferred queues28 volatile uint64_t local;48 // Stats for non-local queues within cluster 49 share, 29 50 30 // number of successes at pushing to preferred queues31 volatile uint64_t lsuccess;32 } push;51 // Stats from outside cluster 52 extrn; 53 } push; 33 54 34 // Pop statistic 35 struct { 36 // number of reads of the mask 37 // picking an empty __cfa_readyQ_mask_t counts here 38 // but not as an attempt 39 volatile uint64_t probe; 55 // Pop statistic 56 struct { 57 // pop from local queue 58 __stats_readyQ_pop_t local; 40 59 41 // number of attemps at poping something42 volatile uint64_t attempt;60 // pop before looking at local queue 61 __stats_readyQ_pop_t help; 43 62 44 // number of successes at poping45 volatile uint64_t success;63 // pop from some other queue 64 __stats_readyQ_pop_t steal; 46 65 47 // number of attemps at poping something to preferred queues 48 volatile uint64_t local; 66 // pop when searching queues sequentially 67 __stats_readyQ_pop_t search; 68 } pop; 49 69 50 // number of successes at poping to preferred queues51 volatile uint64_t lsuccess;52 } pop;53 } pick;54 70 struct { 55 71 volatile uint64_t migration; 72 volatile uint64_t extunpark; 73 volatile int64_t threads; // number of threads in the system, includes only local change 56 74 } threads; 57 75 struct { … … 66 84 struct __attribute__((aligned(64))) __stats_io_t{ 67 85 struct { 86 volatile uint64_t fast; 87 volatile uint64_t slow; 88 volatile uint64_t fail; 89 volatile uint64_t revoke; 90 volatile uint64_t block; 91 } alloc; 92 struct { 93 volatile uint64_t fast; 94 volatile uint64_t slow; 95 } submit; 96 struct { 97 volatile uint64_t external; 98 } flush; 99 struct { 100 volatile uint64_t drain; 101 volatile uint64_t completed; 102 volatile uint64_t flush; 103 volatile uint64_t submitted; 68 104 struct { 69 volatile uint64_t rdy; 70 volatile uint64_t csm; 71 volatile uint64_t avl; 72 volatile uint64_t cnt; 73 } submit_avg; 74 struct { 75 volatile uint64_t val; 76 volatile uint64_t cnt; 77 volatile uint64_t block; 78 } look_avg; 79 struct { 80 volatile uint64_t val; 81 volatile uint64_t cnt; 82 volatile uint64_t block; 83 } alloc_avg; 84 volatile uint64_t helped; 85 volatile uint64_t leader; 86 volatile uint64_t busy; 87 } submit_q; 105 volatile uint64_t busy; 106 } errors; 107 } calls; 88 108 struct { 89 struct { 90 volatile uint64_t val; 91 volatile uint64_t cnt; 92 } completed_avg; 93 volatile uint64_t blocks; 94 } complete_q; 109 volatile uint64_t sleeps; 110 } poller; 111 }; 112 #endif 113 114 #if defined(CFA_STATS_ARRAY) 115 struct __stats_elem_t { 116 long long int ts; 117 int64_t value; 95 118 }; 96 119 #endif 97 120 98 121 struct __attribute__((aligned(128))) __stats_t { 99 __stats_read Q_t ready;122 __stats_readyQ_t ready; 100 123 #if defined(CFA_HAVE_LINUX_IO_URING_H) 101 124 __stats_io_t io; 102 125 #endif 126 127 #if defined(CFA_STATS_ARRAY) 128 struct { 129 __stats_elem_t * values; 130 volatile size_t cnt; 131 } array; 132 #endif 133 103 134 }; 104 135 … … 106 137 void __tally_stats( struct __stats_t *, struct __stats_t * ); 107 138 void __print_stats( struct __stats_t *, int, const char *, const char *, void * ); 139 #if defined(CFA_STATS_ARRAY) 140 void __push_stat ( struct __stats_t *, int64_t value, bool external, const char * name, void * handle); 141 void __flush_stat( struct __stats_t *, const char *, void * ); 142 #else 143 static inline void __push_stat ( struct __stats_t *, int64_t, bool, const char *, void * ) {} 144 static inline void __flush_stat( struct __stats_t *, const char *, void * ) {} 145 #endif 108 146 #endif 109 147 -
libcfa/src/concurrency/thread.cfa
rfeacef9 r5407cdc 39 39 link.next = 0p; 40 40 link.prev = 0p; 41 link.preferred = -1; 41 link.preferred = -1u; 42 last_proc = 0p; 42 43 #if defined( __CFA_WITH_VERIFY__ ) 43 44 canary = 0x0D15EA5E0D15EA5Ep; … … 62 63 } 63 64 64 FORALL_DATA_INSTANCE(ThreadCancelled, (thread_t &), (thread_t))65 66 65 forall(T &) 67 66 void copy(ThreadCancelled(T) * dst, ThreadCancelled(T) * src) { … … 73 72 forall(T &) 74 73 const char * msg(ThreadCancelled(T) *) { 75 return "ThreadCancelled ";74 return "ThreadCancelled(...)"; 76 75 } 77 76 78 77 forall(T &) 79 78 static void default_thread_cancel_handler(ThreadCancelled(T) & ) { 79 // Improve this error message, can I do formatting? 80 80 abort( "Unhandled thread cancellation.\n" ); 81 81 } 82 82 83 forall(T & | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T))) 83 forall(T & | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T)) 84 | { EHM_DEFAULT_VTABLE(ThreadCancelled, (T)); }) 84 85 void ?{}( thread_dtor_guard_t & this, 85 86 T & thrd, void(*cancelHandler)(ThreadCancelled(T) &)) { 86 $monitor * m = get_monitor(thrd);87 $monitor * m = get_monitor(thrd); 87 88 $thread * desc = get_thread(thrd); 88 89 … … 103 104 } 104 105 desc->state = Cancelled; 105 void(*defaultResumptionHandler)(ThreadCancelled(T) &) = 106 void(*defaultResumptionHandler)(ThreadCancelled(T) &) = 106 107 join ? cancelHandler : default_thread_cancel_handler; 107 108 109 // TODO: Remove explitate vtable set once trac#186 is fixed. 108 110 ThreadCancelled(T) except; 109 // TODO: Remove explitate vtable set once trac#186 is fixed. 110 except.virtual_table = &get_exception_vtable(&except); 111 except.virtual_table = &_default_vtable; 111 112 except.the_thread = &thrd; 112 113 except.the_exception = __cfaehm_cancellation_exception( cancellation ); 113 throwResume except; 114 // Why is this cast required? 115 throwResume (ThreadCancelled(T) &)except; 114 116 115 117 except.the_exception->virtual_table->free( except.the_exception ); … … 134 136 /* paranoid */ verify( this_thrd->context.SP ); 135 137 136 __schedule_thread( this_thrd );137 enable_interrupts( __cfaabi_dbg_ctx);138 schedule_thread$( this_thrd ); 139 enable_interrupts(); 138 140 } 139 141 … … 158 160 159 161 //----------------------------------------------------------------------------- 160 forall(T & | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T))) 162 forall(T & | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T)) 163 | { EHM_DEFAULT_VTABLE(ThreadCancelled, (T)); }) 161 164 T & join( T & this ) { 162 165 thread_dtor_guard_t guard = { this, defaultResumptionHandler }; … … 167 170 disable_interrupts(); 168 171 uint64_t ret = __tls_rand(); 169 enable_interrupts( __cfaabi_dbg_ctx);172 enable_interrupts(); 170 173 return ret; 171 174 } -
libcfa/src/concurrency/thread.hfa
rfeacef9 r5407cdc 32 32 }; 33 33 34 FORALL_DATA_EXCEPTION(ThreadCancelled, (thread_t &), (thread_t)) (34 EHM_FORALL_EXCEPTION(ThreadCancelled, (thread_t &), (thread_t)) ( 35 35 thread_t * the_thread; 36 36 exception_t * the_exception; … … 42 42 forall(T &) 43 43 const char * msg(ThreadCancelled(T) *); 44 45 // define that satisfies the trait without using the thread keyword46 #define DECL_THREAD(X) $thread* get_thread(X& this) __attribute__((const)) { return &this.__thrd; } void main(X& this)47 44 48 45 // Inline getters for threads/coroutines/monitors … … 82 79 }; 83 80 84 forall( T & | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T)) ) 81 forall( T & | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T)) 82 | { EHM_DEFAULT_VTABLE(ThreadCancelled, (T)); }) 85 83 void ?{}( thread_dtor_guard_t & this, T & thrd, void(*)(ThreadCancelled(T) &) ); 86 84 void ^?{}( thread_dtor_guard_t & this ); … … 128 126 //---------- 129 127 // join 130 forall( T & | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T)) ) 128 forall( T & | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T)) 129 | { EHM_DEFAULT_VTABLE(ThreadCancelled, (T)); }) 131 130 T & join( T & this ); 132 131 -
libcfa/src/containers/list.hfa
rfeacef9 r5407cdc 83 83 (this.is_terminator){ 1 }; 84 84 } 85 forall ( tInit | { void ?{}( $mgd_link(tE) &, tInit); } ) 86 static inline void ?=?( $mgd_link(tE) &this, tInit i ) { 87 ^?{}( this ); 88 ?{}( this, i ); 85 static inline void ?=?( $mgd_link(tE) &this, tE* elem ) { 86 this.elem = elem ; 87 this.terminator = 0p; 88 this.is_terminator = 0; 89 } 90 static inline void ?=?( $mgd_link(tE) &this, void * terminator ) { 91 this.elem = 0p; 92 this.terminator = terminator; 93 this.is_terminator = 1; 89 94 } 90 95 struct $dlinks { … … 181 186 182 187 static inline void insert_after(Tnode &list_pos, Telem &to_insert) { 183 assert(&list_pos != 0p);184 assert(&to_insert != 0p);188 verify (&list_pos != 0p); 189 verify (&to_insert != 0p); 185 190 Tnode &singleton_to_insert = $tempcv_e2n(to_insert); 186 assert($prev_link(singleton_to_insert).elem == 0p);187 assert($next_link(singleton_to_insert).elem == 0p);191 verify($prev_link(singleton_to_insert).elem == 0p); 192 verify($next_link(singleton_to_insert).elem == 0p); 188 193 $prev_link(singleton_to_insert) = & $tempcv_n2e(list_pos); 189 194 $next_link(singleton_to_insert) = $next_link(list_pos); … … 204 209 205 210 static inline void insert_before(Tnode &list_pos, Telem &to_insert) { 206 assert(&list_pos != 0p);207 assert(&to_insert != 0p);211 verify (&list_pos != 0p); 212 verify (&to_insert != 0p); 208 213 Tnode &singleton_to_insert = $tempcv_e2n(to_insert); 209 assert($prev_link(singleton_to_insert).elem == 0p);210 assert($next_link(singleton_to_insert).elem == 0p);214 verify($prev_link(singleton_to_insert).elem == 0p); 215 verify($next_link(singleton_to_insert).elem == 0p); 211 216 $next_link(singleton_to_insert) = & $tempcv_n2e(list_pos); 212 217 $prev_link(singleton_to_insert) = $prev_link(list_pos); … … 227 232 228 233 static inline void insert_first(dlist(Tnode, Telem) &list, Telem &to_insert) { 229 assert(&list != 0p);230 assert(&to_insert != 0p);234 verify (&list != 0p); 235 verify (&to_insert != 0p); 231 236 Tnode &singleton_to_insert = $tempcv_e2n(to_insert); 232 assert($prev_link(singleton_to_insert).elem == 0p);233 assert($next_link(singleton_to_insert).elem == 0p);237 verify($prev_link(singleton_to_insert).elem == 0p); 238 verify($next_link(singleton_to_insert).elem == 0p); 234 239 235 240 $prev_link(singleton_to_insert) = (void*) &list; … … 249 254 250 255 static inline void insert_last(dlist(Tnode, Telem) &list, Telem &to_insert) { 251 assert(&list != 0p);252 assert(&to_insert != 0p);256 verify (&list != 0p); 257 verify (&to_insert != 0p); 253 258 Tnode &singleton_to_insert = $tempcv_e2n(to_insert); 254 assert($next_link(singleton_to_insert).elem == 0p);255 assert($prev_link(singleton_to_insert).elem == 0p);259 verify($next_link(singleton_to_insert).elem == 0p); 260 verify($prev_link(singleton_to_insert).elem == 0p); 256 261 257 262 $next_link(singleton_to_insert) = (void*) &list; … … 271 276 272 277 static inline void remove(Tnode &list_pos) { 273 assert( &list_pos != 0p );278 verify( &list_pos != 0p ); 274 279 275 280 $mgd_link(Telem) &incoming_from_prev = *0p; … … 308 313 309 314 static inline bool ?`is_empty(dlist(Tnode, Telem) &list) { 310 assert( &list != 0p );315 verify( &list != 0p ); 311 316 $dlinks(Telem) *listLinks = & list.$links; 312 317 if (listLinks->next.is_terminator) { 313 assert(listLinks->prev.is_terminator);314 assert(listLinks->next.terminator);315 assert(listLinks->prev.terminator);318 verify(listLinks->prev.is_terminator); 319 verify(listLinks->next.terminator); 320 verify(listLinks->prev.terminator); 316 321 return true; 317 322 } else { 318 assert(!listLinks->prev.is_terminator);319 assert(listLinks->next.elem);320 assert(listLinks->prev.elem);323 verify(!listLinks->prev.is_terminator); 324 verify(listLinks->next.elem); 325 verify(listLinks->prev.elem); 321 326 return false; 322 327 } … … 324 329 325 330 static inline Telem & pop_first(dlist(Tnode, Telem) &list) { 326 assert( &list != 0p );327 assert( !list`is_empty );331 verify( &list != 0p ); 332 verify( !list`is_empty ); 328 333 $dlinks(Telem) *listLinks = & list.$links; 329 334 Telem & first = *listLinks->next.elem; … … 334 339 335 340 static inline Telem & pop_last(dlist(Tnode, Telem) &list) { 336 assert( &list != 0p );337 assert( !list`is_empty );341 verify( &list != 0p ); 342 verify( !list`is_empty ); 338 343 $dlinks(Telem) *listLinks = & list.$links; 339 344 Telem & last = *listLinks->prev.elem; -
libcfa/src/exception.c
rfeacef9 r5407cdc 10 10 // Created On : Mon Jun 26 15:13:00 2017 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Tue Oct 27 16:27:00 202013 // Update Count : 3 512 // Last Modified On : Wed Feb 24 13:40:00 2021 13 // Update Count : 36 14 14 // 15 15 … … 26 26 #include "concurrency/invoke.h" 27 27 #include "stdhdr/assert.h" 28 #include "virtual.h" 28 29 29 30 #if defined( __ARM_ARCH ) … … 46 47 const _Unwind_Exception_Class __cfaehm_exception_class = 0x4c50575500414643; 47 48 48 // Base exception vtable is abstract, you should not have base exceptions. 49 struct __cfaehm_base_exception_t_vtable 50 ___cfaehm_base_exception_t_vtable_instance = { 51 .parent = NULL, 52 .size = 0, 53 .copy = NULL, 54 .free = NULL, 55 .msg = NULL 49 // Base Exception type id: 50 struct __cfa__parent_vtable __cfatid_exception_t = { 51 NULL, 56 52 }; 57 53 -
libcfa/src/exception.h
rfeacef9 r5407cdc 10 10 // Created On : Mon Jun 26 15:11:00 2017 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : T ue Oct 27 14:45:00 202013 // Update Count : 1 112 // Last Modified On : Thr Apr 8 15:20:00 2021 13 // Update Count : 12 14 14 // 15 15 … … 29 29 struct __cfaehm_base_exception_t; 30 30 typedef struct __cfaehm_base_exception_t exception_t; 31 struct __cfa__parent_vtable; 31 32 struct __cfaehm_base_exception_t_vtable { 32 const struct __cfa ehm_base_exception_t_vtable * parent;33 const struct __cfa__parent_vtable * __cfavir_typeid; 33 34 size_t size; 34 35 void (*copy)(struct __cfaehm_base_exception_t *this, … … 40 41 struct __cfaehm_base_exception_t_vtable const * virtual_table; 41 42 }; 42 extern struct __cfaehm_base_exception_t_vtable 43 ___cfaehm_base_exception_t_vtable_instance; 43 extern struct __cfa__parent_vtable __cfatid_exception_t; 44 44 45 45 … … 104 104 /* The first field must be a pointer to a virtual table. 105 105 * That virtual table must be a decendent of the base exception virtual table. 106 * The virtual table must point at the prober type-id. 107 * None of these can be enforced in an assertion. 106 108 */ 107 virtualT const & get_exception_vtable(exceptT *);108 // Always returns the virtual table for this type (associated types hack).109 109 }; 110 110 -
libcfa/src/exception.hfa
rfeacef9 r5407cdc 10 10 // Created On : Thu Apr 7 10:25:00 2020 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : T ue Aug 4 16:22:00 202013 // Update Count : 312 // Last Modified On : Thr Apr 8 15:16:00 2021 13 // Update Count : 4 14 14 // 15 15 … … 18 18 // ----------------------------------------------------------------------------------------------- 19 19 20 // TRIVIAL_EXCEPTION_DECLARATION(exception_name); 21 // Declare a trivial exception, one that adds no fields or features. 22 // This will make the exception visible and may go in a .hfa or .cfa file. 23 #define TRIVIAL_EXCEPTION_DECLARATION(...) \ 24 _EXC_DISPATCH(_TRIVIAL_EXCEPTION_DECLARATION, __VA_ARGS__) 20 // EHM_EXCEPTION(exception_name)(fields...); 21 // Create an exception (a virtual structure that inherits from exception_t) 22 // with the given name and fields. 23 #define EHM_EXCEPTION(exception_name) \ 24 _EHM_TYPE_ID_STRUCT(exception_name, ); \ 25 _EHM_TYPE_ID_VALUE(exception_name, ); \ 26 _EHM_VIRTUAL_TABLE_STRUCT(exception_name, , ); \ 27 _EHM_EXCEPTION_STRUCT(exception_name, , ) 25 28 26 // TRIVIAL_EXCEPTION_INSTANCE(exception_name);27 // Create the trival exception. This must be used exactly once and should be used in a .cfa file,28 // as it creates the unique instance of the virtual table. 29 #define TRIVIAL_EXCEPTION_INSTANCE(...) _EXC_DISPATCH(_TRIVIAL_EXCEPTION_INSTANCE, __VA_ARGS__)29 // EHM_EXTERN_VTABLE(exception_name, table_name); 30 // Forward declare a virtual table called table_name for exception_name type. 31 #define EHM_EXTERN_VTABLE(exception_name, table_name) \ 32 _EHM_EXTERN_VTABLE(exception_name, , table_name) 30 33 31 // TRIVIAL_EXCEPTION(exception_name[, parent_name]); 32 // Does both of the above, a short hand if the exception is only used in one .cfa file. 33 // For legacy reasons this is the only one that official supports having a parent other than the 34 // base exception. This feature may be removed or changed. 35 #define TRIVIAL_EXCEPTION(...) \ 36 _EXC_DISPATCH(_TRIVIAL_EXCEPTION_DECLARATION, __VA_ARGS__); \ 37 _EXC_DISPATCH(_TRIVIAL_EXCEPTION_INSTANCE, __VA_ARGS__) 34 // EHM_VIRTUAL_TABLE(exception_name, table_name); 35 // Define a virtual table called table_name for exception_name type. 36 #define EHM_VIRTUAL_TABLE(exception_name, table_name) \ 37 _EHM_DEFINE_COPY(exception_name, ) \ 38 _EHM_DEFINE_MSG(exception_name, ) \ 39 _EHM_VIRTUAL_TABLE(exception_name, , table_name) 38 40 39 // FORALL_TRIVIAL_EXCEPTION(exception_name, (assertions...), (parameters...)); 40 // Forward declare a polymorphic but otherwise trivial exception type. You must provide the entire 41 // assertion list (exactly what would go in the forall clause) and parameters list (only the 42 // parameter names from the assertion list, same order and comma seperated). This should be 43 // visible where ever use the exception. This just generates the polymorphic framework, see 44 // POLY_VTABLE_DECLARATION to allow instantiations. 45 #define FORALL_TRIVIAL_EXCEPTION(exception_name, assertions, parameters) \ 46 _FORALL_TRIVIAL_EXCEPTION(exception_name, __cfaehm_base_exception_t, assertions, parameters, ) 41 // EHM_FORALL_EXCEPTION(exception_name, (assertions), (parameters))(fields...); 42 // As EHM_EXCEPTION but for polymorphic types instead of monomorphic ones. 43 // The assertions list should include all polymorphic parameters and 44 // assertions inside a parentisized list. Parameters should include all the 45 // polymorphic parameter names inside a parentisized list (same order). 46 #define EHM_FORALL_EXCEPTION(exception_name, assertions, parameters) \ 47 _EHM_TYPE_ID_STRUCT(exception_name, forall assertions); \ 48 _EHM_VIRTUAL_TABLE_STRUCT(exception_name, forall assertions, parameters); \ 49 _EHM_EXCEPTION_STRUCT(exception_name, forall assertions, parameters) 47 50 48 // FORALL_TRIVIAL_INSTANCE(exception_name, (assertions...), (parameters...)) 49 // Create the forall trivial exception. The assertion list and parameters must match. 50 // There must be exactly one use of this in a program for each exception type. This just 51 // generates the polymorphic framework, see POLY_VTABLE_INSTANCE to allow instantiations. 52 #define FORALL_TRIVIAL_INSTANCE(exception_name, assertions, parameters) \ 53 _FORALL_CTOR0_INSTANCE(exception_name, assertions, parameters) 51 // EHM_FORALL_EXTERN_VTABLE(exception_name, (arguments), table_name); 52 // As EHM_EXTERN_VTABLE but for polymorphic types instead of monomorphic ones. 53 // Arguments should be the parentisized list of polymorphic arguments. 54 #define EHM_FORALL_EXTERN_VTABLE(exception_name, arguments, table_name) \ 55 _EHM_EXTERN_VTABLE(exception_name, arguments, table_name) 54 56 55 // DATA_EXCEPTION(exception_name)(fields...); 56 // Forward declare an exception that adds fields but no features. The added fields go in the 57 // second argument list. The virtual table instance must be provided later (see VTABLE_INSTANCE). 58 #define DATA_EXCEPTION(...) _EXC_DISPATCH(_DATA_EXCEPTION, __VA_ARGS__) 57 // EHM_FORALL_VIRTUAL_TABLE(exception_name, (arguments), table_name); 58 // As EHM_VIRTUAL_TABLE but for polymorphic types instead of monomorphic ones. 59 // Arguments should be the parentisized list of polymorphic arguments. 60 #define EHM_FORALL_VIRTUAL_TABLE(exception_name, arguments, table_name) \ 61 _EHM_TYPE_ID_VALUE(exception_name, arguments); \ 62 _EHM_DEFINE_COPY(exception_name, arguments) \ 63 _EHM_DEFINE_MSG(exception_name, arguments) \ 64 _EHM_VIRTUAL_TABLE(exception_name, arguments, table_name) 59 65 60 // FORALL_DATA_EXCEPTION(exception_name, (assertions...), (parameters...))(fields...); 61 // Define a polymorphic exception that adds fields but no additional features. The assertion list 62 // and matching parameters must match. Then you can give the list of fields. This should be 63 // visible where ever you use the exception. This just generates the polymorphic framework, see 64 // POLY_VTABLE_DECLARATION to allow instantiations. 65 #define FORALL_DATA_EXCEPTION(exception_name, assertions, parameters) \ 66 _FORALL_DATA_EXCEPTION(exception_name, __cfaehm_base_exception_t, assertions, parameters, ) 67 68 // FORALL_DATA_INSTANCE(exception_name, (assertions...), (parameters...)) 69 // Create a polymorphic data exception. The assertion list and parameters must match. This should 70 // appear once in each program. This just generates the polymorphic framework, see 71 // POLY_VTABLE_INSTANCE to allow instantiations. 72 #define FORALL_DATA_INSTANCE(exception_name, assertions, parameters) \ 73 _FORALL_CTOR0_INSTANCE(exception_name, assertions, parameters) 74 75 // VTABLE_DECLARATION(exception_name)([new_features...]); 76 // Declare a virtual table type for an exception with exception_name. You may also add features 77 // (fields on the virtual table) by including them in the second list. 78 #define VTABLE_DECLARATION(...) _EXC_DISPATCH(_VTABLE_DECLARATION, __VA_ARGS__) 79 80 // VTABLE_INSTANCE(exception_name)(msg [, others...]); 81 // Create the instance of the virtual table. There must be exactly one instance of a virtual table 82 // for each exception type. This fills in most of the fields of the virtual table (uses ?=? and 83 // ^?{}) but you must provide the message function and any other fields added in the declaration. 84 #define VTABLE_INSTANCE(...) _EXC_DISPATCH(_VTABLE_INSTANCE, __VA_ARGS__) 85 86 // FORALL_VTABLE_DECLARATION(exception_name, (assertions...), (parameters...))([new_features...]); 87 // Declare a polymorphic virtual table type for an exception with exception_name, the given 88 // assertions and parameters. You may also add features (fields on the virtual table). This just 89 // generates the polymorphic framework, see POLY_VTABLE_DECLARATION to allow instantiations. 90 #define FORALL_VTABLE_DECLARATION(exception_name, assertions, parameters) \ 91 _FORALL_VTABLE_DECLARATION(exception_name, __cfaehm_base_exception_t, assertions, parameters, ) 92 93 // POLY_VTABLE_DECLARATION(exception_name, types...); 94 // Declares that an instantiation for this exception exists for the given types. This should be 95 // visible anywhere you use the instantiation of the exception is used. 96 #define POLY_VTABLE_DECLARATION(exception_name, ...) \ 97 VTABLE_TYPE(exception_name)(__VA_ARGS__) const & get_exception_vtable(exception_name(__VA_ARGS__) *); \ 98 extern VTABLE_TYPE(exception_name)(__VA_ARGS__) VTABLE_NAME(exception_name) 99 100 // POLY_VTABLE_INSTANCE(exception_name, types...)(msg [, others...]); 101 // Creates an instantiation for the given exception for the given types. This should occur only 102 // once in the entire program. You must fill in all features, message and any others given in the 103 // initial declaration. 104 #define POLY_VTABLE_INSTANCE(exception_name, ...) \ 105 _POLY_VTABLE_INSTANCE(exception_name, __cfaehm_base_exception_t, __VA_ARGS__) 106 107 // VTABLE_TYPE(exception_name) | VTABLE_NAME(exception_name) 108 // Get the name of the vtable type or the name of the vtable instance for an exception type. 109 #define VTABLE_TYPE(exception_name) struct _GLUE2(exception_name,_vtable) 110 #define VTABLE_NAME(exception_name) _GLUE3(_,exception_name,_vtable_instance) 111 112 // VTABLE_FIELD(exception_name); 113 // FORALL_VTABLE_FIELD(exception_name, (parameters-or-types)); 114 // The declaration of the virtual table field. Should be the first declaration in a virtual type. 115 #define VTABLE_FIELD(exception_name) VTABLE_TYPE(exception_name) const * virtual_table 116 #define FORALL_VTABLE_FIELD(exception_name, parameters) \ 117 VTABLE_TYPE(exception_name) parameters const * virtual_table 118 119 // VTABLE_INIT(object_reference, exception_name); 120 // Sets a virtual table field on an object to the virtual table instance for the type. 121 #define VTABLE_INIT(this, exception_name) (this).virtual_table = &VTABLE_NAME(exception_name) 122 123 // VTABLE_ASSERTION(exception_name, (parameters...)) 124 // The assertion that there is an instantiation of the vtable for the exception and types. 125 #define VTABLE_ASSERTION(exception_name, parameters) \ 126 { VTABLE_TYPE(exception_name) parameters VTABLE_NAME(exception_name); } 66 // EHM_DEFAULT_VTABLE(exception_name, (arguments)) 67 // Create a declaration for a (possibly polymorphic) default vtable. 68 #define EHM_DEFAULT_VTABLE(exception_name, arguments) \ 69 _EHM_VTABLE_TYPE(exception_name) arguments & const _default_vtable 127 70 128 71 // IS_EXCEPTION(exception_name [, (...parameters)]) … … 135 78 #define IS_TERMINATION_EXCEPTION(...) _IS_EXCEPTION(is_termination_exception, __VA_ARGS__, , ~) 136 79 137 // All internal helper macros begin with an underscore. 138 #define _CLOSE(...) __VA_ARGS__ } 139 #define _GLUE2(left, right) left##right 140 #define _GLUE3(left, middle, right) left##middle##right 141 #define _EXC_DISPATCH(to, ...) to(__VA_ARGS__,__cfaehm_base_exception_t,) 142 #define _UNPACK(...) __VA_ARGS__ 80 // Macros starting with a leading underscore are internal. 143 81 144 #define _TRIVIAL_EXCEPTION_DECLARATION(exception_name, parent_name, ...) \ 145 _VTABLE_DECLARATION(exception_name, parent_name)(); \ 146 struct exception_name { \ 147 VTABLE_FIELD(exception_name); \ 148 }; \ 149 void ?{}(exception_name & this); \ 150 const char * _GLUE2(exception_name,_msg)(exception_name * this) 82 // Create an exception type definition. must be tailing, can be polymorphic. 83 #define _EHM_EXCEPTION_STRUCT(exception_name, forall_clause, parameters) \ 84 forall_clause struct exception_name { \ 85 _EHM_VTABLE_TYPE(exception_name) parameters const * virtual_table; \ 86 _CLOSE 151 87 152 #define _TRIVIAL_EXCEPTION_INSTANCE(exception_name, parent_name, ...) \ 153 void ?{}(exception_name & this) { \ 154 VTABLE_INIT(this, exception_name); \ 155 } \ 156 const char * _GLUE2(exception_name,_msg)(exception_name * this) { \ 157 return #exception_name; \ 158 } \ 159 _VTABLE_INSTANCE(exception_name, parent_name,)(_GLUE2(exception_name,_msg)) 88 // Create a (possibly polymorphic) virtual table forward declaration. 89 #define _EHM_EXTERN_VTABLE(exception_name, arguments, table_name) \ 90 extern const _EHM_VTABLE_TYPE(exception_name) arguments table_name 160 91 161 #define _FORALL_TRIVIAL_EXCEPTION(exception_name, parent_name, assertions, \ 162 parameters, parent_parameters) \ 163 _FORALL_VTABLE_DECLARATION(exception_name, parent_name, assertions, \ 164 parameters, parent_parameters)(); \ 165 forall assertions struct exception_name { \ 166 FORALL_VTABLE_FIELD(exception_name, parameters); \ 167 }; \ 168 _FORALL_CTOR0_DECLARATION(exception_name, assertions, parameters) 169 170 #define _FORALL_CTOR0_DECLARATION(exception_name, assertions, parameters) \ 171 forall(_UNPACK assertions | \ 172 is_exception(exception_name parameters, VTABLE_TYPE(exception_name) parameters)) \ 173 void ?{}(exception_name parameters & this) 174 175 #define _FORALL_CTOR0_INSTANCE(exception_name, assertions, parameters) \ 176 _FORALL_CTOR0_DECLARATION(exception_name, assertions, parameters) { \ 177 (this).virtual_table = &get_exception_vtable(&this); \ 92 // Create a (possibly polymorphic) virtual table definition. 93 #define _EHM_VIRTUAL_TABLE(exception_type, arguments, table_name) \ 94 const _EHM_VTABLE_TYPE(exception_type) arguments table_name @= { \ 95 .__cfavir_typeid : &_EHM_TYPE_ID_NAME(exception_type), \ 96 .size : sizeof(struct exception_type arguments), \ 97 .copy : copy, \ 98 .^?{} : ^?{}, \ 99 .msg : msg, \ 178 100 } 179 101 180 #define _DATA_EXCEPTION(exception_name, parent_name, ...) \ 181 _VTABLE_DECLARATION(exception_name, parent_name)(); \ 182 struct exception_name { \ 183 VTABLE_FIELD(exception_name); \ 184 _CLOSE 102 // Create a (possibly polymorphic) copy function from an assignment operator. 103 #define _EHM_DEFINE_FORALL_COPY(exception_name, forall_clause, parameters) \ 104 forall_clause void copy(exception_name parameters * this, \ 105 exception_name parameters * that) { \ 106 *this = *that; \ 107 } 185 108 186 #define _FORALL_DATA_EXCEPTION(exception_name, parent_name, \ 187 assertions, parameters, parent_parameters) \ 188 _FORALL_VTABLE_DECLARATION(exception_name, parent_name, \ 189 assertions, parameters, parent_parameters)(); \ 190 _FORALL_CTOR0_DECLARATION(exception_name, assertions, parameters); \ 191 forall assertions struct exception_name { \ 192 FORALL_VTABLE_FIELD(exception_name, parameters); \ 193 _CLOSE 109 #define _EHM_DEFINE_COPY(exception_name, arguments) \ 110 void copy(exception_name arguments * this, exception_name arguments * that) { \ 111 *this = *that; \ 112 } 194 113 195 #define _VTABLE_DECLARATION(exception_name, parent_name, ...) \ 196 struct exception_name; \ 197 VTABLE_TYPE(exception_name); \ 198 VTABLE_TYPE(exception_name) const & get_exception_vtable(exception_name *); \ 199 extern VTABLE_TYPE(exception_name) VTABLE_NAME(exception_name); \ 200 VTABLE_TYPE(exception_name) { \ 201 VTABLE_TYPE(parent_name) const * parent; \ 202 size_t size; \ 203 void (*copy)(exception_name * this, exception_name * other); \ 204 void (*^?{})(exception_name & this); \ 205 const char * (*msg)(exception_name * this); \ 206 _CLOSE 114 // Create a (possibly polymorphic) msg function 115 #define _EHM_DEFINE_FORALL_MSG(exception_name, forall_clause, parameters) \ 116 forall_clause const char * msg(exception_name parameters * this) { \ 117 return #exception_name #parameters; \ 118 } 207 119 208 #define _VTABLE_INSTANCE(exception_name, parent_name, ...) \ 209 VTABLE_TYPE(exception_name) const & get_exception_vtable(exception_name *) { \ 210 return VTABLE_NAME(exception_name); \ 211 } \ 212 void _GLUE2(exception_name,_copy)(exception_name * this, exception_name * other) { \ 213 *this = *other; \ 214 } \ 215 VTABLE_TYPE(exception_name) VTABLE_NAME(exception_name) @= { \ 216 &VTABLE_NAME(parent_name), sizeof(exception_name), \ 217 _GLUE2(exception_name,_copy), ^?{}, \ 218 _CLOSE 120 #define _EHM_DEFINE_MSG(exception_name, arguments) \ 121 const char * msg(exception_name arguments * this) { \ 122 return #exception_name #arguments; \ 123 } 219 124 220 #define _FORALL_VTABLE_DECLARATION(exception_name, parent_name, assertions, \ 221 parameters, parent_parameters) \ 222 forall assertions struct exception_name; \ 223 forall assertions VTABLE_TYPE(exception_name) { \ 224 VTABLE_TYPE(parent_name) parent_parameters const * parent; \ 125 // Produces the C compatable name of the virtual table type for a virtual type. 126 #define _EHM_VTABLE_TYPE(type_name) struct _GLUE2(type_name,_vtable) 127 128 // Create the vtable type for exception name. 129 #define _EHM_VIRTUAL_TABLE_STRUCT(exception_name, forall_clause, parameters) \ 130 forall_clause struct exception_name; \ 131 forall_clause _EHM_VTABLE_TYPE(exception_name) { \ 132 _EHM_TYPE_ID_TYPE(exception_name) parameters const * __cfavir_typeid; \ 225 133 size_t size; \ 226 134 void (*copy)(exception_name parameters * this, exception_name parameters * other); \ 227 135 void (*^?{})(exception_name parameters & this); \ 228 136 const char * (*msg)(exception_name parameters * this); \ 229 _CLOSE137 } 230 138 231 #define _POLY_VTABLE_INSTANCE(exception_name, parent_name, ...) \ 232 extern VTABLE_TYPE(exception_name)(__VA_ARGS__) VTABLE_NAME(exception_name); \ 233 VTABLE_TYPE(exception_name)(__VA_ARGS__) const & get_exception_vtable( \ 234 exception_name(__VA_ARGS__) *) { \ 235 return VTABLE_NAME(exception_name); \ 236 } \ 237 void _GLUE2(exception_name,_copy)( \ 238 exception_name(__VA_ARGS__) * this, exception_name(__VA_ARGS__) * other) { \ 239 *this = *other; \ 240 } \ 241 VTABLE_TYPE(exception_name)(__VA_ARGS__) VTABLE_NAME(exception_name) @= { \ 242 &VTABLE_NAME(parent_name), sizeof(exception_name(__VA_ARGS__)), \ 243 _GLUE2(exception_name,_copy), ^?{}, \ 244 _CLOSE 139 // Define the function required to satify the trait for exceptions. 140 #define _EHM_TRAIT_FUNCTION(exception_name, forall_clause, parameters) \ 141 forall_clause inline void mark_exception( \ 142 exception_name parameters const &, \ 143 _EHM_VTABLE_TYPE(exception_name) parameters const &) {} \ 144 145 #define __EHM_TRAIT_FUNCTION(exception_name, forall_clause, parameters) \ 146 forall_clause inline _EHM_VTABLE_TYPE(exception_name) parameters const & \ 147 get_exception_vtable(exception_name parameters const & this) { \ 148 /* This comes before the structure definition, but we know the offset. */ \ 149 /* return (_EHM_VTABLE_TYPE(exception_name) parameters const &)this; */ \ 150 assert(false); \ 151 } 152 153 // Generates a new type-id structure. This is used to mangle the name of the 154 // type-id instance so it also includes polymorphic information. Must be the 155 // direct decendent of exception_t. 156 // The second field is used to recover type information about the exception. 157 #define _EHM_TYPE_ID_STRUCT(exception_name, forall_clause) \ 158 forall_clause _EHM_TYPE_ID_TYPE(exception_name) { \ 159 __cfa__parent_vtable const * parent; \ 160 } 161 162 // Generate a new type-id value. 163 #define _EHM_TYPE_ID_VALUE(exception_name, arguments) \ 164 __attribute__(( section(".gnu.linkonce." "__cfatid_" #exception_name) )) \ 165 _EHM_TYPE_ID_TYPE(exception_name) arguments const \ 166 _EHM_TYPE_ID_NAME(exception_name) = { \ 167 &__cfatid_exception_t, \ 168 } 169 170 // _EHM_TYPE_ID_STRUCT and _EHM_TYPE_ID_VALUE are the two that would need to 171 // be updated to extend the hierarchy if we are still using macros when that 172 // is added. 173 174 // Produce the C compatable name of the type-id type for an exception type. 175 #define _EHM_TYPE_ID_TYPE(exception_name) \ 176 struct _GLUE2(__cfatid_struct_, exception_name) 177 178 // Produce the name of the instance of the type-id for an exception type. 179 #define _EHM_TYPE_ID_NAME(exception_name) _GLUE2(__cfatid_,exception_name) 245 180 246 181 #define _IS_EXCEPTION(kind, exception_name, parameters, ...) \ 247 kind(exception_name parameters, VTABLE_TYPE(exception_name) parameters) 182 kind(exception_name parameters, _EHM_VTABLE_TYPE(exception_name) parameters) 183 184 // Internal helper macros: 185 #define _CLOSE(...) __VA_ARGS__ } 186 #define _GLUE2(left, right) left##right -
libcfa/src/fstream.cfa
rfeacef9 r5407cdc 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Jun 19 16:24:54 202013 // Update Count : 38414 // 15 16 #include "fstream.hfa" 12 // Last Modified On : Tue Apr 27 22:08:57 2021 13 // Update Count : 442 14 // 15 16 #include "fstream.hfa" // also includes iostream.hfa 17 17 18 18 #include <stdio.h> // vfprintf, vfscanf 19 19 #include <stdlib.h> // exit 20 20 #include <stdarg.h> // varargs 21 #include <string.h> // strlen 22 #include <float.h> // DBL_DIG, LDBL_DIG 23 #include <complex.h> // creal, cimag 21 #include <string.h> // strncpy, strerror 24 22 #include <assert.h> 25 23 #include <errno.h> // errno 26 24 27 28 25 // *********************************** ofstream *********************************** 29 26 … … 32 29 33 30 void ?{}( ofstream & os, void * file ) { 34 os.$file = file; 35 os.$sepDefault = true; 36 os.$sepOnOff = false; 37 os.$nlOnOff = true; 38 os.$prt = false; 39 os.$sawNL = false; 40 $sepSetCur( os, sepGet( os ) ); 31 os.file$ = file; 32 os.sepDefault$ = true; 33 os.sepOnOff$ = false; 34 os.nlOnOff$ = true; 35 os.prt$ = false; 36 os.sawNL$ = false; 37 os.acquired$ = false; 38 sepSetCur$( os, sepGet( os ) ); 41 39 sepSet( os, " " ); 42 40 sepSetTuple( os, ", " ); … … 44 42 45 43 // private 46 bool $sepPrt( ofstream & os ) { $setNL( os, false ); return os.$sepOnOff; }47 void $sepReset( ofstream & os ) { os.$sepOnOff = os.$sepDefault; }48 void $sepReset( ofstream & os, bool reset ) { os.$sepDefault = reset; os.$sepOnOff = os.$sepDefault; }49 const char * $sepGetCur( ofstream & os ) { return os.$sepCur; }50 void $sepSetCur( ofstream & os, const char sepCur[] ) { os.$sepCur= sepCur; }51 bool $getNL( ofstream & os ) { return os.$sawNL; }52 void $setNL( ofstream & os, bool state ) { os.$sawNL= state; }53 bool $getANL( ofstream & os ) { return os.$nlOnOff; }54 bool $getPrt( ofstream & os ) { return os.$prt; }55 void $setPrt( ofstream & os, bool state ) { os.$prt= state; }44 bool sepPrt$( ofstream & os ) { setNL$( os, false ); return os.sepOnOff$; } 45 void sepReset$( ofstream & os ) { os.sepOnOff$ = os.sepDefault$; } 46 void sepReset$( ofstream & os, bool reset ) { os.sepDefault$ = reset; os.sepOnOff$ = os.sepDefault$; } 47 const char * sepGetCur$( ofstream & os ) { return os.sepCur$; } 48 void sepSetCur$( ofstream & os, const char sepCur[] ) { os.sepCur$ = sepCur; } 49 bool getNL$( ofstream & os ) { return os.sawNL$; } 50 void setNL$( ofstream & os, bool state ) { os.sawNL$ = state; } 51 bool getANL$( ofstream & os ) { return os.nlOnOff$; } 52 bool getPrt$( ofstream & os ) { return os.prt$; } 53 void setPrt$( ofstream & os, bool state ) { os.prt$ = state; } 56 54 57 55 // public 58 void ?{}( ofstream & os ) { os. $file= 0p; }56 void ?{}( ofstream & os ) { os.file$ = 0p; } 59 57 60 58 void ?{}( ofstream & os, const char name[], const char mode[] ) { … … 70 68 } // ^?{} 71 69 72 void sepOn( ofstream & os ) { os. $sepOnOff = ! $getNL( os ); }73 void sepOff( ofstream & os ) { os. $sepOnOff= false; }70 void sepOn( ofstream & os ) { os.sepOnOff$ = ! getNL$( os ); } 71 void sepOff( ofstream & os ) { os.sepOnOff$ = false; } 74 72 75 73 bool sepDisable( ofstream & os ) { 76 bool temp = os. $sepDefault;77 os. $sepDefault= false;78 $sepReset( os );74 bool temp = os.sepDefault$; 75 os.sepDefault$ = false; 76 sepReset$( os ); 79 77 return temp; 80 78 } // sepDisable 81 79 82 80 bool sepEnable( ofstream & os ) { 83 bool temp = os. $sepDefault;84 os. $sepDefault= true;85 if ( os. $sepOnOff ) $sepReset( os ); // start of line ?81 bool temp = os.sepDefault$; 82 os.sepDefault$ = true; 83 if ( os.sepOnOff$ ) sepReset$( os ); // start of line ? 86 84 return temp; 87 85 } // sepEnable 88 86 89 void nlOn( ofstream & os ) { os. $nlOnOff= true; }90 void nlOff( ofstream & os ) { os. $nlOnOff= false; }91 92 const char * sepGet( ofstream & os ) { return os. $separator; }87 void nlOn( ofstream & os ) { os.nlOnOff$ = true; } 88 void nlOff( ofstream & os ) { os.nlOnOff$ = false; } 89 90 const char * sepGet( ofstream & os ) { return os.separator$; } 93 91 void sepSet( ofstream & os, const char s[] ) { 94 92 assert( s ); 95 strncpy( os. $separator, s,sepSize - 1 );96 os. $separator[sepSize - 1] = '\0';93 strncpy( os.separator$, s, ofstream_sepSize - 1 ); 94 os.separator$[ofstream_sepSize - 1] = '\0'; 97 95 } // sepSet 98 96 99 const char * sepGetTuple( ofstream & os ) { return os. $tupleSeparator; }97 const char * sepGetTuple( ofstream & os ) { return os.tupleSeparator$; } 100 98 void sepSetTuple( ofstream & os, const char s[] ) { 101 99 assert( s ); 102 strncpy( os. $tupleSeparator, s,sepSize - 1 );103 os. $tupleSeparator[sepSize - 1] = '\0';100 strncpy( os.tupleSeparator$, s, ofstream_sepSize - 1 ); 101 os.tupleSeparator$[ofstream_sepSize - 1] = '\0'; 104 102 } // sepSet 105 103 106 104 void ends( ofstream & os ) { 107 if ( $getANL( os ) ) nl( os );108 else $setPrt( os, false ); // turn off105 if ( getANL$( os ) ) nl( os ); 106 else setPrt$( os, false ); // turn off 109 107 if ( &os == &exit ) exit( EXIT_FAILURE ); 110 108 if ( &os == &abort ) abort(); 109 if ( os.acquired$ ) { os.acquired$ = false; release( os ); } 111 110 } // ends 112 111 113 intfail( ofstream & os ) {114 return os. $file == 0 || ferror( (FILE *)(os.$file) );112 bool fail( ofstream & os ) { 113 return os.file$ == 0 || ferror( (FILE *)(os.file$) ); 115 114 } // fail 116 115 117 116 int flush( ofstream & os ) { 118 return fflush( (FILE *)(os. $file) );117 return fflush( (FILE *)(os.file$) ); 119 118 } // flush 120 119 … … 135 134 136 135 void close( ofstream & os ) { 137 if ( (FILE *)(os. $file) == 0p ) return;138 if ( (FILE *)(os. $file) == (FILE *)stdout || (FILE *)(os.$file) == (FILE *)stderr ) return;139 140 if ( fclose( (FILE *)(os. $file) ) == EOF ) {136 if ( (FILE *)(os.file$) == 0p ) return; 137 if ( (FILE *)(os.file$) == (FILE *)stdout || (FILE *)(os.file$) == (FILE *)stderr ) return; 138 139 if ( fclose( (FILE *)(os.file$) ) == EOF ) { 141 140 abort | IO_MSG "close output" | nl | strerror( errno ); 142 141 } // if 143 os. $file= 0p;142 os.file$ = 0p; 144 143 } // close 145 144 … … 149 148 } // if 150 149 151 if ( fwrite( data, 1, size, (FILE *)(os. $file) ) != size ) {150 if ( fwrite( data, 1, size, (FILE *)(os.file$) ) != size ) { 152 151 abort | IO_MSG "write" | nl | strerror( errno ); 153 152 } // if … … 158 157 va_list args; 159 158 va_start( args, format ); 160 int len = vfprintf( (FILE *)(os. $file), format, args );159 int len = vfprintf( (FILE *)(os.file$), format, args ); 161 160 if ( len == EOF ) { 162 if ( ferror( (FILE *)(os. $file) ) ) {161 if ( ferror( (FILE *)(os.file$) ) ) { 163 162 abort | IO_MSG "invalid write"; 164 163 } // if … … 166 165 va_end( args ); 167 166 168 $setPrt( os, true ); // called in output cascade169 $sepReset( os ); // reset separator167 setPrt$( os, true ); // called in output cascade 168 sepReset$( os ); // reset separator 170 169 return len; 171 170 } // fmt 171 172 inline void acquire( ofstream & os ) { 173 lock( os.lock$ ); 174 if ( ! os.acquired$ ) os.acquired$ = true; 175 else unlock( os.lock$ ); 176 } // acquire 177 178 inline void release( ofstream & os ) { 179 unlock( os.lock$ ); 180 } // release 181 182 void ?{}( osacquire & acq, ofstream & os ) { &acq.os = &os; lock( os.lock$ ); } 183 void ^?{}( osacquire & acq ) { release( acq.os ); } 172 184 173 185 static ofstream soutFile = { (FILE *)stdout }; … … 176 188 ofstream & serr = serrFile, & stderr = serrFile; 177 189 190 static ofstream lsoutFile = { (FILE *)stdout }; 191 ofstream & lsout = lsoutFile; 192 178 193 static ofstream exitFile = { (FILE *)stdout }; 179 194 ofstream & exit = exitFile; … … 181 196 ofstream & abort = abortFile; 182 197 198 ofstream & nl( ofstream & os ) { 199 nl$( os ); // call basic_ostream nl 200 flush( os ); 201 return os; 202 // (ofstream &)(os | '\n'); 203 // setPrt$( os, false ); // turn off 204 // setNL$( os, true ); 205 // flush( os ); 206 // return sepOff( os ); // prepare for next line 207 } // nl 183 208 184 209 // *********************************** ifstream *********************************** … … 187 212 // private 188 213 void ?{}( ifstream & is, void * file ) { 189 is.$file = file; 190 is.$nlOnOff = false; 214 is.file$ = file; 215 is.nlOnOff$ = false; 216 is.acquired$ = false; 191 217 } // ?{} 192 218 193 219 // public 194 void ?{}( ifstream & is ) { is. $file= 0p; }220 void ?{}( ifstream & is ) { is.file$ = 0p; } 195 221 196 222 void ?{}( ifstream & is, const char name[], const char mode[] ) { … … 206 232 } // ^?{} 207 233 208 void nlOn( ifstream & os ) { os. $nlOnOff= true; }209 void nlOff( ifstream & os ) { os. $nlOnOff= false; }210 bool getANL( ifstream & os ) { return os. $nlOnOff; }211 212 intfail( ifstream & is ) {213 return is. $file == 0p || ferror( (FILE *)(is.$file) );234 void nlOn( ifstream & os ) { os.nlOnOff$ = true; } 235 void nlOff( ifstream & os ) { os.nlOnOff$ = false; } 236 bool getANL( ifstream & os ) { return os.nlOnOff$; } 237 238 bool fail( ifstream & is ) { 239 return is.file$ == 0p || ferror( (FILE *)(is.file$) ); 214 240 } // fail 215 241 242 void ends( ifstream & is ) { 243 if ( is.acquired$ ) { is.acquired$ = false; release( is ); } 244 } // ends 245 216 246 int eof( ifstream & is ) { 217 return feof( (FILE *)(is. $file) );247 return feof( (FILE *)(is.file$) ); 218 248 } // eof 219 249 … … 226 256 } // if 227 257 #endif // __CFA_DEBUG__ 228 is. $file= file;258 is.file$ = file; 229 259 } // open 230 260 … … 234 264 235 265 void close( ifstream & is ) { 236 if ( (FILE *)(is. $file) == 0p ) return;237 if ( (FILE *)(is. $file) == (FILE *)stdin ) return;238 239 if ( fclose( (FILE *)(is. $file) ) == EOF ) {266 if ( (FILE *)(is.file$) == 0p ) return; 267 if ( (FILE *)(is.file$) == (FILE *)stdin ) return; 268 269 if ( fclose( (FILE *)(is.file$) ) == EOF ) { 240 270 abort | IO_MSG "close input" | nl | strerror( errno ); 241 271 } // if 242 is. $file= 0p;272 is.file$ = 0p; 243 273 } // close 244 274 … … 248 278 } // if 249 279 250 if ( fread( data, size, 1, (FILE *)(is. $file) ) == 0 ) {280 if ( fread( data, size, 1, (FILE *)(is.file$) ) == 0 ) { 251 281 abort | IO_MSG "read" | nl | strerror( errno ); 252 282 } // if … … 259 289 } // if 260 290 261 if ( ungetc( c, (FILE *)(is. $file) ) == EOF ) {291 if ( ungetc( c, (FILE *)(is.file$) ) == EOF ) { 262 292 abort | IO_MSG "ungetc" | nl | strerror( errno ); 263 293 } // if … … 269 299 270 300 va_start( args, format ); 271 int len = vfscanf( (FILE *)(is. $file), format, args );301 int len = vfscanf( (FILE *)(is.file$), format, args ); 272 302 if ( len == EOF ) { 273 if ( ferror( (FILE *)(is. $file) ) ) {303 if ( ferror( (FILE *)(is.file$) ) ) { 274 304 abort | IO_MSG "invalid read"; 275 305 } // if … … 279 309 } // fmt 280 310 311 inline void acquire( ifstream & is ) { 312 lock( is.lock$ ); 313 if ( ! is.acquired$ ) is.acquired$ = true; 314 else unlock( is.lock$ ); 315 } // acquire 316 317 inline void release( ifstream & is ) { 318 unlock( is.lock$ ); 319 } // release 320 321 void ?{}( isacquire & acq, ifstream & is ) { &acq.is = &is; lock( is.lock$ ); } 322 void ^?{}( isacquire & acq ) { release( acq.is ); } 323 281 324 static ifstream sinFile = { (FILE *)stdin }; 282 325 ifstream & sin = sinFile, & stdin = sinFile; … … 286 329 287 330 331 EHM_VIRTUAL_TABLE(Open_Failure, Open_Failure_main_table); 288 332 void ?{}( Open_Failure & this, ofstream & ostream ) { 289 VTABLE_INIT(this, Open_Failure);333 this.virtual_table = &Open_Failure_main_table; 290 334 this.ostream = &ostream; 291 335 this.tag = 1; 292 336 } 293 337 void ?{}( Open_Failure & this, ifstream & istream ) { 294 VTABLE_INIT(this, Open_Failure);338 this.virtual_table = &Open_Failure_main_table; 295 339 this.istream = &istream; 296 340 this.tag = 0; 297 341 } 298 const char * Open_Failure_msg(Open_Failure * this) {299 return "Open_Failure";300 }301 VTABLE_INSTANCE(Open_Failure)(Open_Failure_msg);302 342 void throwOpen_Failure( ofstream & ostream ) { 303 343 Open_Failure exc = { ostream }; -
libcfa/src/fstream.hfa
rfeacef9 r5407cdc 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Jun 19 16:29:17 202013 // Update Count : 18912 // Last Modified On : Tue Apr 27 22:00:30 2021 13 // Update Count : 226 14 14 // 15 15 16 16 #pragma once 17 17 18 #include "bits/weakso_locks.hfa" 18 #include "bits/weakso_locks.hfa" // mutex_lock 19 19 #include "iostream.hfa" 20 20 #include <exception.hfa> … … 24 24 25 25 26 enum { sepSize = 16 };26 enum { ofstream_sepSize = 16 }; 27 27 struct ofstream { 28 void * $file; 29 bool $sepDefault; 30 bool $sepOnOff; 31 bool $nlOnOff; 32 bool $prt; // print text 33 bool $sawNL; 34 const char * $sepCur; 35 char $separator[sepSize]; 36 char $tupleSeparator[sepSize]; 37 // multiple_acquisition_lock lock; 28 void * file$; 29 bool sepDefault$; 30 bool sepOnOff$; 31 bool nlOnOff$; 32 bool prt$; // print text 33 bool sawNL$; 34 const char * sepCur$; 35 char separator$[ofstream_sepSize]; 36 char tupleSeparator$[ofstream_sepSize]; 37 multiple_acquisition_lock lock$; 38 bool acquired$; 38 39 }; // ofstream 39 40 41 // Satisfies ostream 42 40 43 // private 41 bool $sepPrt( ofstream & );42 void $sepReset( ofstream & );43 void $sepReset( ofstream &, bool );44 const char * $sepGetCur( ofstream & );45 void $sepSetCur( ofstream &, const char [] );46 bool $getNL( ofstream & );47 void $setNL( ofstream &, bool );48 bool $getANL( ofstream & );49 bool $getPrt( ofstream & );50 void $setPrt( ofstream &, bool );44 bool sepPrt$( ofstream & ); 45 void sepReset$( ofstream & ); 46 void sepReset$( ofstream &, bool ); 47 const char * sepGetCur$( ofstream & ); 48 void sepSetCur$( ofstream &, const char [] ); 49 bool getNL$( ofstream & ); 50 void setNL$( ofstream &, bool ); 51 bool getANL$( ofstream & ); 52 bool getPrt$( ofstream & ); 53 void setPrt$( ofstream &, bool ); 51 54 52 55 // public … … 63 66 void sepSetTuple( ofstream &, const char [] ); 64 67 65 void ends( ofstream & os ); 66 int fail( ofstream & ); 68 void ends( ofstream & ); 69 int fmt( ofstream &, const char format[], ... ) __attribute__(( format(printf, 2, 3) )); 70 71 bool fail( ofstream & ); 67 72 int flush( ofstream & ); 68 void open( ofstream &, const char name[], const char mode[] ); 73 void open( ofstream &, const char name[], const char mode[] ); // FIX ME: use default = "w" 69 74 void open( ofstream &, const char name[] ); 70 75 void close( ofstream & ); 71 76 ofstream & write( ofstream &, const char data[], size_t size ); 72 int fmt( ofstream &, const char format[], ... ) __attribute__(( format(printf, 2, 3) ));73 77 74 void ?{}( ofstream & os ); 75 void ?{}( ofstream & os, const char name[], const char mode[] ); 76 void ?{}( ofstream & os, const char name[] ); 77 void ^?{}( ofstream & os ); 78 void acquire( ofstream & ); 79 void release( ofstream & ); 80 81 struct osacquire { 82 ofstream & os; 83 }; 84 void ?{}( osacquire & acq, ofstream & ); 85 void ^?{}( osacquire & acq ); 86 87 void ?{}( ofstream & ); 88 void ?{}( ofstream &, const char name[], const char mode[] ); // FIX ME: use default = "w" 89 void ?{}( ofstream &, const char name[] ); 90 void ^?{}( ofstream & ); 91 92 // private 93 static inline ofstream & nl$( ofstream & os ) { return nl( os ); } // remember basic_ostream nl 94 // public 95 ofstream & nl( ofstream & os ); // override basic_ostream nl 78 96 79 97 extern ofstream & sout, & stdout, & serr, & stderr; // aliases … … 85 103 86 104 struct ifstream { 87 void * $file; 88 bool $nlOnOff; 105 void * file$; 106 bool nlOnOff$; 107 multiple_acquisition_lock lock$; 108 bool acquired$; 89 109 }; // ifstream 110 111 // Satisfies istream 90 112 91 113 // public … … 93 115 void nlOff( ifstream & ); 94 116 bool getANL( ifstream & ); 95 int fail( ifstream & is ); 117 void ends( ifstream & ); 118 int fmt( ifstream &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) )); 119 120 bool fail( ifstream & is ); 96 121 int eof( ifstream & is ); 97 void open( ifstream & is, const char name[], const char mode[] ); 122 void open( ifstream & is, const char name[], const char mode[] ); // FIX ME: use default = "r" 98 123 void open( ifstream & is, const char name[] ); 99 124 void close( ifstream & is ); 100 125 ifstream & read( ifstream & is, char * data, size_t size ); 101 126 ifstream & ungetc( ifstream & is, char c ); 102 int fmt( ifstream &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) )); 127 128 void acquire( ifstream & is ); 129 void release( ifstream & is ); 130 131 struct isacquire { 132 ifstream & is; 133 }; 134 void ?{}( isacquire & acq, ifstream & is ); 135 void ^?{}( isacquire & acq ); 103 136 104 137 void ?{}( ifstream & is ); 105 void ?{}( ifstream & is, const char name[], const char mode[] ); 138 void ?{}( ifstream & is, const char name[], const char mode[] ); // FIX ME: use default = "r" 106 139 void ?{}( ifstream & is, const char name[] ); 107 140 void ^?{}( ifstream & is ); … … 113 146 114 147 115 DATA_EXCEPTION(Open_Failure)(148 EHM_EXCEPTION(Open_Failure)( 116 149 union { 117 150 ofstream * ostream; … … 122 155 ); 123 156 124 void ?{}( Open_Failure & this, ofstream & ostream);125 void ?{}( Open_Failure & this, ifstream & istream);157 void ?{}( Open_Failure & this, ofstream & ); 158 void ?{}( Open_Failure & this, ifstream & ); 126 159 127 160 // Local Variables: // -
libcfa/src/gmp.hfa
rfeacef9 r5407cdc 10 10 // Created On : Tue Apr 19 08:43:43 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Feb 9 09:56:54 202013 // Update Count : 3 112 // Last Modified On : Tue Apr 20 20:59:21 2021 13 // Update Count : 32 14 14 // 15 15 … … 263 263 forall( ostype & | ostream( ostype ) ) { 264 264 ostype & ?|?( ostype & os, Int mp ) { 265 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );265 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 266 266 gmp_printf( "%Zd", mp.mpz ); 267 267 sepOn( os ); -
libcfa/src/heap.cfa
rfeacef9 r5407cdc 10 10 // Created On : Tue Dec 19 21:58:35 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Jan 10 11:20:49202113 // Update Count : 103 112 // Last Modified On : Tue Apr 20 21:20:48 2021 13 // Update Count : 1033 14 14 // 15 15 … … 52 52 static bool prtFree = false; 53 53 54 inlinebool prtFree() {54 bool prtFree() { 55 55 return prtFree; 56 56 } // prtFree … … 1128 1128 1129 1129 // Set the alignment for an the allocation and return previous alignment or 0 if no alignment. 1130 size_t $malloc_alignment_set( void * addr, size_t alignment ) {1130 size_t malloc_alignment_set$( void * addr, size_t alignment ) { 1131 1131 if ( unlikely( addr == 0p ) ) return libAlign(); // minimum alignment 1132 1132 size_t ret; … … 1139 1139 } // if 1140 1140 return ret; 1141 } // $malloc_alignment_set1141 } // malloc_alignment_set$ 1142 1142 1143 1143 … … 1153 1153 1154 1154 // Set allocation is zero filled and return previous zero filled. 1155 bool $malloc_zero_fill_set( void * addr ) {1155 bool malloc_zero_fill_set$( void * addr ) { 1156 1156 if ( unlikely( addr == 0p ) ) return false; // null allocation is not zero fill 1157 1157 HeapManager.Storage.Header * header = headerAddr( addr ); … … 1162 1162 header->kind.real.blockSize |= 2; // mark as zero filled 1163 1163 return ret; 1164 } // $malloc_zero_fill_set1164 } // malloc_zero_fill_set$ 1165 1165 1166 1166 … … 1176 1176 1177 1177 // Set allocation size and return previous size. 1178 size_t $malloc_size_set( void * addr, size_t size ) {1178 size_t malloc_size_set$( void * addr, size_t size ) { 1179 1179 if ( unlikely( addr == 0p ) ) return 0; // null allocation has 0 size 1180 1180 HeapManager.Storage.Header * header = headerAddr( addr ); … … 1185 1185 header->kind.real.size = size; 1186 1186 return ret; 1187 } // $malloc_size_set1187 } // malloc_size_set$ 1188 1188 1189 1189 -
libcfa/src/iostream.cfa
rfeacef9 r5407cdc 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Aug 24 08:31:35 202013 // Update Count : 1 13012 // Last Modified On : Tue Apr 27 18:01:03 2021 13 // Update Count : 1330 14 14 // 15 15 … … 36 36 37 37 38 forall( ostype & | ostream( ostype ) ) {38 forall( ostype & | basic_ostream( ostype ) ) { 39 39 ostype & ?|?( ostype & os, bool b ) { 40 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );40 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 41 41 fmt( os, "%s", b ? "true" : "false" ); 42 42 return os; … … 48 48 ostype & ?|?( ostype & os, char c ) { 49 49 fmt( os, "%c", c ); 50 if ( c == '\n' ) $setNL( os, true );50 if ( c == '\n' ) setNL$( os, true ); 51 51 return sepOff( os ); 52 52 } // ?|? … … 56 56 57 57 ostype & ?|?( ostype & os, signed char sc ) { 58 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );58 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 59 59 fmt( os, "%hhd", sc ); 60 60 return os; … … 65 65 66 66 ostype & ?|?( ostype & os, unsigned char usc ) { 67 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );67 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 68 68 fmt( os, "%hhu", usc ); 69 69 return os; … … 74 74 75 75 ostype & ?|?( ostype & os, short int si ) { 76 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );76 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 77 77 fmt( os, "%hd", si ); 78 78 return os; … … 83 83 84 84 ostype & ?|?( ostype & os, unsigned short int usi ) { 85 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );85 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 86 86 fmt( os, "%hu", usi ); 87 87 return os; … … 92 92 93 93 ostype & ?|?( ostype & os, int i ) { 94 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );94 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 95 95 fmt( os, "%d", i ); 96 96 return os; … … 101 101 102 102 ostype & ?|?( ostype & os, unsigned int ui ) { 103 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );103 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 104 104 fmt( os, "%u", ui ); 105 105 return os; … … 110 110 111 111 ostype & ?|?( ostype & os, long int li ) { 112 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );112 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 113 113 fmt( os, "%ld", li ); 114 114 return os; … … 119 119 120 120 ostype & ?|?( ostype & os, unsigned long int uli ) { 121 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );121 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 122 122 fmt( os, "%lu", uli ); 123 123 return os; … … 128 128 129 129 ostype & ?|?( ostype & os, long long int lli ) { 130 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );130 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 131 131 fmt( os, "%lld", lli ); 132 132 return os; … … 137 137 138 138 ostype & ?|?( ostype & os, unsigned long long int ulli ) { 139 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );139 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 140 140 fmt( os, "%llu", ulli ); 141 141 return os; … … 145 145 } // ?|? 146 146 147 #if defined( __SIZEOF_INT128__ )147 #if defined( __SIZEOF_INT128__ ) 148 148 // UINT64_MAX 18_446_744_073_709_551_615_ULL 149 149 #define P10_UINT64 10_000_000_000_000_000_000_ULL // 19 zeroes 150 150 151 151 static inline void base10_128( ostype & os, unsigned int128 val ) { 152 #if defined(__GNUC__) && __GNUC_PREREQ(7,0)// gcc version >= 7152 #if defined(__GNUC__) && __GNUC_PREREQ(7,0) // gcc version >= 7 153 153 if ( val > P10_UINT64 ) { 154 #else154 #else 155 155 if ( (uint64_t)(val >> 64) != 0 || (uint64_t)val > P10_UINT64 ) { // patch gcc 5 & 6 -O3 bug 156 #endif // __GNUC_PREREQ(7,0)156 #endif // __GNUC_PREREQ(7,0) 157 157 base10_128( os, val / P10_UINT64 ); // recursive 158 158 fmt( os, "%.19lu", (uint64_t)(val % P10_UINT64) ); … … 171 171 172 172 ostype & ?|?( ostype & os, int128 llli ) { 173 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );173 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 174 174 base10_128( os, llli ); 175 175 return os; … … 180 180 181 181 ostype & ?|?( ostype & os, unsigned int128 ullli ) { 182 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );182 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 183 183 base10_128( os, ullli ); 184 184 return os; … … 187 187 (ostype &)(os | ullli); ends( os ); 188 188 } // ?|? 189 #endif // __SIZEOF_INT128__189 #endif // __SIZEOF_INT128__ 190 190 191 191 #define PrintWithDP( os, format, val, ... ) \ … … 195 195 int len = snprintf( buf, size, format, ##__VA_ARGS__, val ); \ 196 196 fmt( os, "%s", buf ); \ 197 if ( isfinite( val ) ) { /* if number, always print decimal point */ \197 if ( isfinite( val ) ) { /* if number, print decimal point when no fraction or exponent */ \ 198 198 for ( int i = 0;; i += 1 ) { \ 199 199 if ( i == len ) { fmt( os, "." ); break; } \ 200 if ( buf[i] == '.' ) break;\200 if ( buf[i] == '.' || buf[i] == 'e' || buf[i] == 'E' ) break; /* decimal point or scientific ? */ \ 201 201 } /* for */ \ 202 202 } /* if */ \ … … 204 204 205 205 ostype & ?|?( ostype & os, float f ) { 206 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );206 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 207 207 PrintWithDP( os, "%g", f ); 208 208 return os; … … 213 213 214 214 ostype & ?|?( ostype & os, double d ) { 215 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );215 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 216 216 PrintWithDP( os, "%.*lg", d, DBL_DIG ); 217 217 return os; … … 222 222 223 223 ostype & ?|?( ostype & os, long double ld ) { 224 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );224 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 225 225 PrintWithDP( os, "%.*Lg", ld, LDBL_DIG ); 226 226 return os; … … 231 231 232 232 ostype & ?|?( ostype & os, float _Complex fc ) { 233 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );233 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 234 234 // os | crealf( fc ) | nonl; 235 235 PrintWithDP( os, "%g", crealf( fc ) ); … … 243 243 244 244 ostype & ?|?( ostype & os, double _Complex dc ) { 245 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );245 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 246 246 // os | creal( dc ) | nonl; 247 247 PrintWithDP( os, "%.*lg", creal( dc ), DBL_DIG ); … … 255 255 256 256 ostype & ?|?( ostype & os, long double _Complex ldc ) { 257 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );257 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 258 258 // os | creall( ldc ) || nonl; 259 259 PrintWithDP( os, "%.*Lg", creall( ldc ), LDBL_DIG ); … … 266 266 } // ?|? 267 267 268 ostype & ?|?( ostype & os, const char s tr[] ) {268 ostype & ?|?( ostype & os, const char s[] ) { 269 269 enum { Open = 1, Close, OpenClose }; 270 270 static const unsigned char mask[256] @= { … … 282 282 }; // mask 283 283 284 if ( s tr[0] == '\0' ) { sepOff( os ); return os; } // null string => no separator284 if ( s[0] == '\0' ) { sepOff( os ); return os; } // null string => no separator 285 285 286 286 // first character IS NOT spacing or closing punctuation => add left separator 287 unsigned char ch = s tr[0]; // must make unsigned288 if ( $sepPrt( os ) && mask[ ch ] != Close && mask[ ch ] != OpenClose ) {289 fmt( os, "%s", $sepGetCur( os ) );287 unsigned char ch = s[0]; // must make unsigned 288 if ( sepPrt$( os ) && mask[ ch ] != Close && mask[ ch ] != OpenClose ) { 289 fmt( os, "%s", sepGetCur$( os ) ); 290 290 } // if 291 291 292 292 // if string starts line, must reset to determine open state because separator is off 293 $sepReset( os ); // reset separator293 sepReset$( os ); // reset separator 294 294 295 295 // last character IS spacing or opening punctuation => turn off separator for next item 296 size_t len = strlen( str ); 297 ch = str[len - 1]; // must make unsigned 298 if ( $sepPrt( os ) && mask[ ch ] != Open && mask[ ch ] != OpenClose ) { 296 int len = strlen( s ); 297 ch = s[len - 1]; // must make unsigned 298 fmt( os, "%s", s ); // fmt resets seperator, but reset it again 299 if ( sepPrt$( os ) && mask[ ch ] != Open && mask[ ch ] != OpenClose ) { 299 300 sepOn( os ); 300 301 } else { 301 302 sepOff( os ); 302 303 } // if 303 if ( ch == '\n' ) $setNL( os, true ); // check *AFTER* $sepPrtcall above as it resets NL flag304 return write( os, str, len );305 } // ?|? 306 307 void ?|?( ostype & os, const char s tr[] ) {308 (ostype &)(os | s tr); ends( os );309 } // ?|? 310 311 // ostype & ?|?( ostype & os, const char16_t * s tr) {312 // if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );313 // fmt( os, "%ls", s tr);304 if ( ch == '\n' ) setNL$( os, true ); // check *AFTER* sepPrt$ call above as it resets NL flag 305 return os; 306 // return write( os, s, len ); 307 } // ?|? 308 void ?|?( ostype & os, const char s[] ) { 309 (ostype &)(os | s); ends( os ); 310 } // ?|? 311 312 // ostype & ?|?( ostype & os, const char16_t * s ) { 313 // if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 314 // fmt( os, "%ls", s ); 314 315 // return os; 315 316 // } // ?|? 316 317 317 318 // #if ! ( __ARM_ARCH_ISA_ARM == 1 && __ARM_32BIT_STATE == 1 ) // char32_t == wchar_t => ambiguous 318 // ostype & ?|?( ostype & os, const char32_t * s tr) {319 // if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );320 // fmt( os, "%ls", s tr);319 // ostype & ?|?( ostype & os, const char32_t * s ) { 320 // if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 321 // fmt( os, "%ls", s ); 321 322 // return os; 322 323 // } // ?|? 323 324 // #endif // ! ( __ARM_ARCH_ISA_ARM == 1 && __ARM_32BIT_STATE == 1 ) 324 325 325 // ostype & ?|?( ostype & os, const wchar_t * s tr) {326 // if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );327 // fmt( os, "%ls", s tr);326 // ostype & ?|?( ostype & os, const wchar_t * s ) { 327 // if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 328 // fmt( os, "%ls", s ); 328 329 // return os; 329 330 // } // ?|? 330 331 331 332 ostype & ?|?( ostype & os, const void * p ) { 332 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );333 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 333 334 fmt( os, "%p", p ); 334 335 return os; … … 340 341 // manipulators 341 342 ostype & ?|?( ostype & os, ostype & (* manip)( ostype & ) ) { 342 (ostype &)(manip( os )); 343 return os; 343 return manip( os ); 344 344 } // ?|? 345 345 void ?|?( ostype & os, ostype & (* manip)( ostype & ) ) { 346 (ostype &)(manip( os ));347 if ( $getPrt( os ) ) ends( os ); // something printed ?348 $setPrt( os, false ); // turn off346 manip( os ); 347 if ( getPrt$( os ) ) ends( os ); // something printed ? 348 setPrt$( os, false ); // turn off 349 349 } // ?|? 350 350 … … 359 359 ostype & nl( ostype & os ) { 360 360 (ostype &)(os | '\n'); 361 $setPrt( os, false ); // turn off 362 $setNL( os, true ); 363 flush( os ); 361 setPrt$( os, false ); // turn off 362 setNL$( os, true ); 364 363 return sepOff( os ); // prepare for next line 365 364 } // nl 366 365 367 366 ostype & nonl( ostype & os ) { 368 $setPrt( os, false ); // turn off367 setPrt$( os, false ); // turn off 369 368 return os; 370 369 } // nonl … … 399 398 return os; 400 399 } // nlOff 400 } // distribution 401 402 forall( ostype & | ostream( ostype ) ) { 403 ostype & acquire( ostype & os ) { 404 acquire( os ); // call void returning 405 return os; 406 } // acquire 401 407 } // distribution 402 408 … … 405 411 ostype & ?|?( ostype & os, T arg, Params rest ) { 406 412 (ostype &)(os | arg); // print first argument 407 $sepSetCur( os, sepGetTuple( os ) ); // switch to tuple separator413 sepSetCur$( os, sepGetTuple( os ) ); // switch to tuple separator 408 414 (ostype &)(os | rest); // print remaining arguments 409 $sepSetCur( os, sepGet( os ) ); // switch to regular separator415 sepSetCur$( os, sepGet( os ) ); // switch to regular separator 410 416 return os; 411 417 } // ?|? … … 413 419 // (ostype &)(?|?( os, arg, rest )); ends( os ); 414 420 (ostype &)(os | arg); // print first argument 415 $sepSetCur( os, sepGetTuple( os ) ); // switch to tuple separator421 sepSetCur$( os, sepGetTuple( os ) ); // switch to tuple separator 416 422 (ostype &)(os | rest); // print remaining arguments 417 $sepSetCur( os, sepGet( os ) ); // switch to regular separator423 sepSetCur$( os, sepGet( os ) ); // switch to regular separator 418 424 ends( os ); 419 425 } // ?|? … … 442 448 // Default prefix for non-decimal prints is 0b, 0, 0x. 443 449 #define IntegralFMTImpl( T, IFMTNP, IFMTP ) \ 444 forall( ostype & | ostream( ostype ) ) { \450 forall( ostype & | basic_ostream( ostype ) ) { \ 445 451 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 446 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); \452 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); \ 447 453 \ 448 454 if ( f.base == 'b' || f.base == 'B' ) { /* bespoke binary format */ \ … … 517 523 return os; \ 518 524 } /* ?|? */ \ 519 void ?|?( ostype & os, _Ostream_Manip(T) f ) { (ostype &)(os | f); ends( os ); } \ 520 } // distribution 521 522 IntegralFMTImpl( signed char, "% *hh ", "% *.*hh " ) 523 IntegralFMTImpl( unsigned char, "% *hh ", "% *.*hh " ) 524 IntegralFMTImpl( signed short int, "% *h ", "% *.*h " ) 525 IntegralFMTImpl( unsigned short int, "% *h ", "% *.*h " ) 526 IntegralFMTImpl( signed int, "% * ", "% *.* " ) 527 IntegralFMTImpl( unsigned int, "% * ", "% *.* " ) 528 IntegralFMTImpl( signed long int, "% *l ", "% *.*l " ) 529 IntegralFMTImpl( unsigned long int, "% *l ", "% *.*l " ) 530 IntegralFMTImpl( signed long long int, "% *ll ", "% *.*ll " ) 531 IntegralFMTImpl( unsigned long long int, "% *ll ", "% *.*ll " ) 532 533 #if 0 525 void ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 526 (ostype &)(os | f); ends( os ); \ 527 } /* ?|? */ \ 528 } // distribution 529 530 IntegralFMTImpl( signed char, " *hh ", " *.*hh " ) 531 IntegralFMTImpl( unsigned char, " *hh ", " *.*hh " ) 532 IntegralFMTImpl( signed short int, " *h ", " *.*h " ) 533 IntegralFMTImpl( unsigned short int, " *h ", " *.*h " ) 534 IntegralFMTImpl( signed int, " * ", " *.* " ) 535 IntegralFMTImpl( unsigned int, " * ", " *.* " ) 536 IntegralFMTImpl( signed long int, " *l ", " *.*l " ) 537 IntegralFMTImpl( unsigned long int, " *l ", " *.*l " ) 538 IntegralFMTImpl( signed long long int, " *ll ", " *.*ll " ) 539 IntegralFMTImpl( unsigned long long int, " *ll ", " *.*ll " ) 540 541 534 542 #if defined( __SIZEOF_INT128__ ) 535 543 // Default prefix for non-decimal prints is 0b, 0, 0x. 536 #define IntegralFMTImpl128( T, SIGNED, CODE, IFMTNP, IFMTP ) \ 537 forall( ostype & | ostream( ostype ) ) \ 538 static void base10_128( ostype & os, _Ostream_Manip(T) f ) { \ 539 if ( f.val > UINT64_MAX ) { \ 540 unsigned long long int lsig = f.val % P10_UINT64; \ 541 f.val /= P10_UINT64; /* msig */ \ 542 base10_128( os, f ); /* recursion */ \ 543 _Ostream_Manip(unsigned long long int) fmt @= { lsig, 0, 19, 'u', { .all : 0 } }; \ 544 fmt.flags.nobsdp = true; \ 545 /* printf( "fmt1 %c %lld %d\n", fmt.base, fmt.val, fmt.all ); */ \ 546 sepOff( os ); \ 547 (ostype &)(os | fmt); \ 548 } else { \ 549 /* printf( "fmt2 %c %lld %d\n", f.base, (unsigned long long int)f.val, f.all ); */ \ 550 _Ostream_Manip(SIGNED long long int) fmt @= { (SIGNED long long int)f.val, f.wd, f.pc, f.base, { .all : f.all } }; \ 551 (ostype &)(os | fmt); \ 552 } /* if */ \ 553 } /* base10_128 */ \ 554 forall( ostype & | ostream( ostype ) ) { \ 555 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 556 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); \ 557 \ 558 if ( f.base == 'b' | f.base == 'B' | f.base == 'o' | f.base == 'x' | f.base == 'X' ) { \ 559 unsigned long long int msig = (unsigned long long int)(f.val >> 64); \ 560 unsigned long long int lsig = (unsigned long long int)(f.val); \ 561 _Ostream_Manip(SIGNED long long int) fmt @= { msig, f.wd, f.pc, f.base, { .all : f.all } }; \ 562 _Ostream_Manip(unsigned long long int) fmt2 @= { lsig, 0, 0, f.base, { .all : 0 } }; \ 563 if ( msig == 0 ) { \ 564 fmt.val = lsig; \ 565 (ostype &)(os | fmt); \ 566 } else { \ 567 fmt2.flags.pad0 = fmt2.flags.nobsdp = true; \ 568 if ( f.base == 'b' | f.base == 'B' ) { \ 569 if ( fmt.flags.pc && fmt.pc > 64 ) fmt.pc -= 64; else { fmt.flags.pc = false; fmt.pc = 0; } \ 570 if ( fmt.flags.left ) { \ 571 fmt.flags.left = false; \ 572 fmt.wd = 0; \ 573 /* printf( "L %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \ 574 fmt2.flags.left = true; \ 575 int msigd = high1( msig ); \ 576 fmt2.wd = f.wd - (fmt.pc > msigd ? fmt.pc : msigd); \ 577 if ( ! fmt.flags.nobsdp ) fmt2.wd -= 2; /* compensate for 0b base specifier */ \ 578 if ( (int)fmt2.wd < 64 ) fmt2.wd = 64; /* cast deals with negative value */ \ 579 fmt2.flags.pc = true; fmt2.pc = 64; \ 580 } else { \ 581 if ( fmt.wd > 64 ) fmt.wd -= 64; \ 582 else fmt.wd = 1; \ 583 /* printf( "R %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \ 584 fmt2.wd = 64; \ 585 } /* if */ \ 586 /* printf( "C %llo %d %d '%c' %x\n", fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 587 (ostype &)(os | fmt | "" | fmt2); \ 588 } else if ( f.base == 'o' ) { \ 589 if ( fmt.flags.pc && fmt.pc > 22 ) fmt.pc -= 22; else { fmt.flags.pc = false; fmt.pc = 0; } \ 590 fmt.val = (unsigned long long int)fmt.val >> 2; \ 591 fmt2.val = ((msig & 0x3) << 1) + ((lsig & 0x8000000000000000U) != 0); \ 592 if ( fmt.flags.left ) { \ 593 fmt.flags.left = false; \ 594 fmt.wd = 0; \ 595 /* printf( "L %llo %llo %llo %d %d '%c' %x %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all, fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 596 (ostype &)(os | fmt | "" | fmt2); \ 597 sepOff( os ); \ 598 fmt2.flags.left = true; \ 599 int msigd = ceiling_div( high1( fmt.val ), 3 ); \ 600 fmt2.wd = f.wd - (fmt.pc > msigd ? fmt.pc : msigd); \ 601 if ( ! fmt.flags.nobsdp ) fmt2.wd -= 1; /* compensate for 0 base specifier */ \ 602 if ( (int)fmt2.wd < 21 ) fmt2.wd = 21; /* cast deals with negative value */ \ 603 fmt2.flags.pc = true; fmt2.pc = 21; \ 604 } else { \ 605 if ( fmt.wd > 22 ) fmt.wd -= 22; \ 606 else fmt.wd = 1; \ 607 /* printf( "R %llo %llo %llo %d %d '%c' %x %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all, fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 608 (ostype &)(os | fmt | "" | fmt2); \ 609 sepOff( os ); \ 610 fmt2.wd = 21; \ 611 } /* if */ \ 612 fmt2.val = lsig & 0x7fffffffffffffffU; \ 613 /* printf( "\nC %llo %d %d '%c' %x\n", fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 614 (ostype &)(os | fmt2); \ 615 } else { /* f.base == 'x' | f.base == 'X' */ \ 616 if ( fmt.flags.pc && fmt.pc > 16 ) fmt.pc -= 16; else { fmt.flags.pc = false; fmt.pc = 0; } \ 617 if ( fmt.flags.left ) { \ 618 fmt.flags.left = false; \ 619 fmt.wd = 0; \ 620 /* printf( "L %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \ 621 fmt2.flags.left = true; \ 622 int msigd = high1( msig ); \ 623 fmt2.wd = f.wd - (fmt.pc > msigd ? fmt.pc : msigd); \ 624 if ( ! fmt.flags.nobsdp ) fmt2.wd -= 2; /* compensate for 0x base specifier */ \ 625 if ( (int)fmt2.wd < 16 ) fmt2.wd = 16; /* cast deals with negative value */ \ 626 fmt2.flags.pc = true; fmt2.pc = 16; \ 627 } else { \ 628 if ( fmt.wd > 16 ) fmt.wd -= 16; \ 629 else fmt.wd = 1; \ 630 /* printf( "R %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \ 631 fmt2.wd = 16; \ 632 } /* if */ \ 633 /* printf( "C %llo %d %d '%c' %x\n", fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 634 (ostype &)(os | fmt | "" | fmt2); \ 635 } /* if */ \ 636 } /* if */ \ 637 } else { \ 638 if ( CODE == 'd' ) { \ 639 if ( f.val < 0 ) { fmt( os, "-" ); sepOff( os ); f.val = -f.val; f.flags.sign = false; } \ 640 } /* if */ \ 641 base10_128( os, f ); \ 642 } /* if */ \ 643 return os; \ 644 } /* ?|? */ \ 645 void ?|?( ostype & os, _Ostream_Manip(T) f ) { (ostype &)(os | f); ends( os ); } \ 646 } // distribution 647 648 IntegralFMTImpl128( int128, signed, 'd', "% *ll ", "% *.*ll " ) 649 IntegralFMTImpl128( unsigned int128, unsigned, 'u', "% *ll ", "% *.*ll " ) 650 #endif // __SIZEOF_INT128__ 651 #endif // 0 652 653 #if 1 654 #if defined( __SIZEOF_INT128__ ) 655 // Default prefix for non-decimal prints is 0b, 0, 0x. 656 forall( ostype & | ostream( ostype ) ) 544 forall( ostype & | basic_ostream( ostype ) ) 657 545 static inline void base_128( ostype & os, unsigned int128 val, unsigned int128 power, _Ostream_Manip(uint64_t) & f, unsigned int maxdig, unsigned int bits, unsigned int cnt = 0 ) { 658 546 int wd = 1; // f.wd is never 0 because 0 implies left-pad … … 719 607 720 608 #define IntegralFMTImpl128( T ) \ 721 forall( ostype & | ostream( ostype ) ) { \609 forall( ostype & | basic_ostream( ostype ) ) { \ 722 610 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 723 611 _Ostream_Manip(uint64_t) fmt; \ … … 741 629 IntegralFMTImpl128( unsigned int128 ) 742 630 #endif // __SIZEOF_INT128__ 743 #endif // 0744 631 745 632 // *********************************** floating point *********************************** 746 633 747 #define PrintWithDP2( os, format, val, ... ) \ 634 static const char *suffixes[] = { 635 "y", "z", "a", "f", "p", "n", "u", "m", "", 636 "K", "M", "G", "T", "P", "E", "Z", "Y" 637 }; 638 #define SUFFIXES_START (-24) /* Smallest power for which there is a suffix defined. */ 639 #define SUFFIXES_END (SUFFIXES_START + (int)((sizeof(suffixes) / sizeof(char *) - 1) * 3)) 640 641 #define PrintWithDP2( os, format, ... ) \ 748 642 { \ 749 enum { size = 48 };\750 char buf[size]; \751 int bufbeg = 0, i, len = snprintf( buf, size, format, ##__VA_ARGS__, val );\752 if ( isfinite( val ) && (f.base != 'g' || f.pc != 0) ) { /* if number, print decimal point*/ \753 for ( i = 0; i < len && buf[i] != '.' && buf[i] != 'e' && buf[i] != 'E'; i += 1 ); /* decimal point or scientific ? */\754 if ( i == len && ! f.flags.nobsdp) { \755 if ( ! f.flags.left ) {\756 buf[i] = '.'; buf[i + 1] = '\0';\757 if ( buf[0] == ' ' ) bufbeg = 1; /* decimal point within width */\758 } else {\759 for ( i = 0; i < len && buf[i] != ' '; i += 1 ); /* trailing blank ? */\760 buf[i] = '.'; \761 if ( i == len ) buf[i + 1] = '\0';\643 if ( ! f.flags.eng ) { \ 644 len = snprintf( buf, size, format, ##__VA_ARGS__ ); \ 645 if ( isfinite( f.val ) && ( f.pc != 0 || ! f.flags.nobsdp ) ) { /* if number, print decimal point when no fraction or exponent */ \ 646 for ( i = 0; i < len && buf[i] != '.' && buf[i] != 'e' && buf[i] != 'E'; i += 1 ); /* decimal point or scientific ? */ \ 647 if ( i == len ) { \ 648 if ( ! f.flags.left ) { \ 649 buf[i] = '.'; buf[i + 1] = '\0'; \ 650 if ( buf[0] == ' ' ) bufbeg = 1; /* decimal point within width */ \ 651 } else { \ 652 for ( i = 0; i < len && buf[i] != ' '; i += 1 ); /* trailing blank ? */ \ 653 buf[i] = '.'; \ 654 if ( i == len ) buf[i + 1] = '\0'; \ 655 } /* if */ \ 762 656 } /* if */ \ 763 657 } /* if */ \ 658 } else { \ 659 int exp10, len2; \ 660 eng( f.val, f.pc, exp10 ); /* changes arguments */ \ 661 if ( ! f.flags.left && f.wd > 1 ) { \ 662 /* Exponent size (number of digits, 'e', optional minus sign) */ \ 663 f.wd -= lrint( floor( log10( abs( exp10 ) ) ) ) + 1 + 1 + (exp10 < 0 ? 1 : 0); \ 664 if ( f.wd < 1 ) f.wd = 1; \ 665 } /* if */ \ 666 len = snprintf( buf, size, format, ##__VA_ARGS__ ); \ 667 if ( f.flags.left ) { \ 668 for ( len -= 1; len > 0 && buf[len] == ' '; len -= 1 ); \ 669 len += 1; \ 670 } /* if */ \ 671 if ( ! f.flags.nobsdp || (exp10 < SUFFIXES_START) || (exp10 > SUFFIXES_END) ) { \ 672 len2 = snprintf( &buf[len], size - len, "e%d", exp10 ); \ 673 } else { \ 674 len2 = snprintf( &buf[len], size - len, "%s", suffixes[(exp10 - SUFFIXES_START) / 3] ); \ 675 } /* if */ \ 676 if ( f.flags.left && len + len2 < f.wd ) buf[len + len2] = ' '; \ 764 677 } /* if */ \ 765 678 fmt( os, "%s", &buf[bufbeg] ); \ … … 767 680 768 681 #define FloatingPointFMTImpl( T, DFMTNP, DFMTP ) \ 769 forall( ostype & | ostream( ostype ) ) { \ 682 forall( ostype & | basic_ostream( ostype ) ) { \ 683 static void eng( T &value, int & pc, int & exp10 ) { \ 684 exp10 = lrint( floor( log10( abs( value ) ) ) ); /* round to desired precision */ \ 685 if ( exp10 < 0 ) exp10 -= 2; \ 686 exp10 = floor( exp10, 3 ); \ 687 value *= pow( 10.0, -exp10 ); \ 688 if ( pc <= 3 ) pc = 3; \ 689 } /* eng */ \ 690 \ 770 691 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 771 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); \ 772 char fmtstr[sizeof(DFMTP)]; /* sizeof includes '\0' */ \ 692 enum { size = 48 }; \ 693 char buf[size]; \ 694 int bufbeg = 0, i, len; \ 695 \ 696 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); \ 697 char fmtstr[sizeof(DFMTP) + 8]; /* sizeof includes '\0' */ \ 773 698 if ( ! f.flags.pc ) memcpy( &fmtstr, DFMTNP, sizeof(DFMTNP) ); \ 774 699 else memcpy( &fmtstr, DFMTP, sizeof(DFMTP) ); \ … … 784 709 fmtstr[sizeof(DFMTNP)-2] = f.base; /* sizeof includes '\0' */ \ 785 710 /* printf( "%g %d %s\n", f.val, f.wd, &fmtstr[star]); */ \ 786 PrintWithDP2( os, &fmtstr[star], f. val, f.wd) \711 PrintWithDP2( os, &fmtstr[star], f.wd, f.val ) \ 787 712 } else { /* precision */ \ 788 713 fmtstr[sizeof(DFMTP)-2] = f.base; /* sizeof includes '\0' */ \ 789 714 /* printf( "%g %d %d %s\n", f.val, f.wd, f.pc, &fmtstr[star] ); */ \ 790 PrintWithDP2( os, &fmtstr[star], f. val, f.wd, f.pc) \715 PrintWithDP2( os, &fmtstr[star], f.wd, f.pc, f.val ) \ 791 716 } /* if */ \ 792 717 return os; \ … … 796 721 } // distribution 797 722 798 FloatingPointFMTImpl( double, " % * ", "%*.* " )799 FloatingPointFMTImpl( long double, " % *L ", "%*.*L " )723 FloatingPointFMTImpl( double, " * ", " *.* " ) 724 FloatingPointFMTImpl( long double, " *L ", " *.*L " ) 800 725 801 726 // *********************************** character *********************************** 802 727 803 forall( ostype & | ostream( ostype ) ) {728 forall( ostype & | basic_ostream( ostype ) ) { 804 729 ostype & ?|?( ostype & os, _Ostream_Manip(char) f ) { 805 730 if ( f.base != 'c' ) { // bespoke binary/octal/hex format … … 812 737 } // if 813 738 814 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );739 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 815 740 816 741 #define CFMTNP "% * " … … 834 759 // *********************************** C string *********************************** 835 760 836 forall( ostype & | ostream( ostype ) ) {761 forall( ostype & | basic_ostream( ostype ) ) { 837 762 ostype & ?|?( ostype & os, _Ostream_Manip(const char *) f ) { 838 763 if ( ! f.val ) return os; // null pointer ? … … 850 775 } // if 851 776 852 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );777 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); 853 778 854 779 #define SFMTNP "% * " … … 882 807 883 808 884 forall( istype & | istream( istype ) ) {809 forall( istype & | basic_istream( istype ) ) { 885 810 istype & ?|?( istype & is, bool & b ) { 886 811 char val[6]; … … 894 819 return is; 895 820 } // ?|? 821 void ?|?( istype & is, bool & b ) { 822 (istype &)(is | b); ends( is ); 823 } // ?|? 896 824 897 825 istype & ?|?( istype & is, char & c ) { … … 905 833 return is; 906 834 } // ?|? 835 void ?|?( istype & is, char & c ) { 836 (istype &)(is | c); ends( is ); 837 } // ?|? 907 838 908 839 istype & ?|?( istype & is, signed char & sc ) { … … 910 841 return is; 911 842 } // ?|? 843 void ?|?( istype & is, signed char & sc ) { 844 (istype &)(is | sc); ends( is ); 845 } // ?|? 912 846 913 847 istype & ?|?( istype & is, unsigned char & usc ) { … … 915 849 return is; 916 850 } // ?|? 851 void ?|?( istype & is, unsigned char & usc ) { 852 (istype &)(is | usc); ends( is ); 853 } // ?|? 917 854 918 855 istype & ?|?( istype & is, short int & si ) { … … 920 857 return is; 921 858 } // ?|? 859 void ?|?( istype & is, short int & si ) { 860 (istype &)(is | si); ends( is ); 861 } // ?|? 922 862 923 863 istype & ?|?( istype & is, unsigned short int & usi ) { … … 925 865 return is; 926 866 } // ?|? 867 void ?|?( istype & is, unsigned short int & usi ) { 868 (istype &)(is | usi); ends( is ); 869 } // ?|? 927 870 928 871 istype & ?|?( istype & is, int & i ) { … … 930 873 return is; 931 874 } // ?|? 875 void ?|?( istype & is, int & i ) { 876 (istype &)(is | i); ends( is ); 877 } // ?|? 932 878 933 879 istype & ?|?( istype & is, unsigned int & ui ) { … … 935 881 return is; 936 882 } // ?|? 883 void ?|?( istype & is, unsigned int & ui ) { 884 (istype &)(is | ui); ends( is ); 885 } // ?|? 937 886 938 887 istype & ?|?( istype & is, long int & li ) { … … 940 889 return is; 941 890 } // ?|? 891 void ?|?( istype & is, long int & li ) { 892 (istype &)(is | li); ends( is ); 893 } // ?|? 942 894 943 895 istype & ?|?( istype & is, unsigned long int & ulli ) { … … 945 897 return is; 946 898 } // ?|? 899 void ?|?( istype & is, unsigned long int & ulli ) { 900 (istype &)(is | ulli); ends( is ); 901 } // ?|? 947 902 948 903 istype & ?|?( istype & is, long long int & lli ) { … … 950 905 return is; 951 906 } // ?|? 907 void ?|?( istype & is, long long int & lli ) { 908 (istype &)(is | lli); ends( is ); 909 } // ?|? 952 910 953 911 istype & ?|?( istype & is, unsigned long long int & ulli ) { … … 955 913 return is; 956 914 } // ?|? 957 958 #if defined( __SIZEOF_INT128__ ) 959 istype & ?|?( istype & is, int128 & i128 ) { 960 return (istype &)(is | (unsigned int128 &)i128); 961 } // ?|? 962 963 istype & ?|?( istype & is, unsigned int128 & ui128 ) { 915 void & ?|?( istype & is, unsigned long long int & ulli ) { 916 (istype &)(is | ulli); ends( is ); 917 } // ?|? 918 919 #if defined( __SIZEOF_INT128__ ) 920 istype & ?|?( istype & is, int128 & llli ) { 921 return (istype &)(is | (unsigned int128 &)llli); 922 } // ?|? 923 void ?|?( istype & is, int128 & llli ) { 924 (istype &)(is | llli); ends( is ); 925 } // ?|? 926 927 istype & ?|?( istype & is, unsigned int128 & ullli ) { 964 928 char s[40]; 965 929 bool sign = false; … … 968 932 // If the input is too large, the value returned is undefined. If there is no input, no value is returned 969 933 if ( fmt( is, "%39[0-9]%*[0-9]", s ) == 1 ) { // take first 39 characters, ignore remaining 970 u i128= 0;934 ullli = 0; 971 935 for ( unsigned int i = 0; s[i] != '\0'; i += 1 ) { 972 u i128 = ui128* 10 + s[i] - '0';936 ullli = ullli * 10 + s[i] - '0'; 973 937 } // for 974 if ( sign ) u i128 = -ui128;938 if ( sign ) ullli = -ullli; 975 939 } else if ( sign ) ungetc( is, '-' ); // return minus when no digits 976 940 return is; 977 941 } // ?|? 978 #endif // __SIZEOF_INT128__ 942 void ?|?( istype & is, unsigned int128 & ullli ) { 943 (istype &)(is | ullli); ends( is ); 944 } // ?|? 945 #endif // __SIZEOF_INT128__ 979 946 980 947 istype & ?|?( istype & is, float & f ) { … … 982 949 return is; 983 950 } // ?|? 951 void ?|?( istype & is, float & f ) { 952 (istype &)(is | f); ends( is ); 953 } // ?|? 984 954 985 955 istype & ?|?( istype & is, double & d ) { … … 987 957 return is; 988 958 } // ?|? 959 void ?|?( istype & is, double & d ) { 960 (istype &)(is | d); ends( is ); 961 } // ?|? 989 962 990 963 istype & ?|?( istype & is, long double & ld ) { … … 992 965 return is; 993 966 } // ?|? 994 967 void ?|?( istype & is, long double & ld ) { 968 (istype &)(is | ld); ends( is ); 969 } // ?|? 995 970 996 971 istype & ?|?( istype & is, float _Complex & fc ) { … … 1000 975 return is; 1001 976 } // ?|? 977 void ?|?( istype & is, float _Complex & fc ) { 978 (istype &)(is | fc); ends( is ); 979 } // ?|? 1002 980 1003 981 istype & ?|?( istype & is, double _Complex & dc ) { … … 1007 985 return is; 1008 986 } // ?|? 987 void ?|?( istype & is, double _Complex & dc ) { 988 (istype &)(is | dc); ends( is ); 989 } // ?|? 1009 990 1010 991 istype & ?|?( istype & is, long double _Complex & ldc ) { … … 1014 995 return is; 1015 996 } // ?|? 997 void ?|?( istype & is, long double _Complex & ldc ) { 998 (istype &)(is | ldc); ends( is ); 999 } // ?|? 1016 1000 1017 1001 // istype & ?|?( istype & is, const char fmt[] ) { … … 1020 1004 // } // ?|? 1021 1005 1022 istype & ?|?( istype & is, char * s) {1006 istype & ?|?( istype & is, char s[] ) { 1023 1007 fmt( is, "%s", s ); 1024 1008 return is; 1009 } // ?|? 1010 void ?|?( istype & is, char s[] ) { 1011 (istype &)(is | s); ends( is ); 1025 1012 } // ?|? 1026 1013 … … 1029 1016 return manip( is ); 1030 1017 } // ?|? 1018 void ?|?( istype & is, istype & (* manip)( istype & ) ) { 1019 manip( is ); ends( is ); 1020 } // ?|? 1031 1021 1032 1022 istype & nl( istype & is ) { … … 1046 1036 } // distribution 1047 1037 1038 forall( istype & | istream( istype ) ) { 1039 istype & acquire( istype & is ) { 1040 acquire( is ); // call void returning 1041 return is; 1042 } // acquire 1043 } // distribution 1044 1048 1045 // *********************************** manipulators *********************************** 1049 1046 1050 forall( istype & | istream( istype ) ) 1051 istype & ?|?( istype & is, _Istream_Cstr f ) { 1052 // skip xxx 1053 if ( ! f.s ) { 1054 // printf( "skip %s %d\n", f.scanset, f.wd ); 1055 if ( f.wd == -1 ) fmt( is, f.scanset, "" ); // no input arguments 1056 else for ( f.wd ) fmt( is, "%*c" ); 1057 return is; 1058 } // if 1059 size_t len = 0; 1060 if ( f.scanset ) len = strlen( f.scanset ); 1061 char fmtstr[len + 16]; 1062 int start = 1; 1063 fmtstr[0] = '%'; 1064 if ( f.flags.ignore ) { fmtstr[1] = '*'; start += 1; } 1065 if ( f.wd != -1 ) { start += sprintf( &fmtstr[start], "%d", f.wd ); } 1066 // cstr %s, %*s, %ws, %*ws 1067 if ( ! f.scanset ) { 1068 fmtstr[start] = 's'; fmtstr[start + 1] = '\0'; 1069 // printf( "cstr %s\n", fmtstr ); 1047 forall( istype & | basic_istream( istype ) ) { 1048 istype & ?|?( istype & is, _Istream_Cstr f ) { 1049 // skip xxx 1050 if ( ! f.s ) { 1051 // printf( "skip %s %d\n", f.scanset, f.wd ); 1052 if ( f.wd == -1 ) fmt( is, f.scanset, "" ); // no input arguments 1053 else for ( f.wd ) fmt( is, "%*c" ); 1054 return is; 1055 } // if 1056 size_t len = 0; 1057 if ( f.scanset ) len = strlen( f.scanset ); 1058 char fmtstr[len + 16]; 1059 int start = 1; 1060 fmtstr[0] = '%'; 1061 if ( f.flags.ignore ) { fmtstr[1] = '*'; start += 1; } 1062 if ( f.wd != -1 ) { start += sprintf( &fmtstr[start], "%d", f.wd ); } 1063 // cstr %s, %*s, %ws, %*ws 1064 if ( ! f.scanset ) { 1065 fmtstr[start] = 's'; fmtstr[start + 1] = '\0'; 1066 // printf( "cstr %s\n", fmtstr ); 1067 fmt( is, fmtstr, f.s ); 1068 return is; 1069 } // if 1070 // incl %[xxx], %*[xxx], %w[xxx], %*w[xxx] 1071 // excl %[^xxx], %*[^xxx], %w[^xxx], %*w[^xxx] 1072 fmtstr[start] = '['; start += 1; 1073 if ( f.flags.inex ) { fmtstr[start] = '^'; start += 1; } 1074 strcpy( &fmtstr[start], f.scanset ); // copy includes '\0' 1075 len += start; 1076 fmtstr[len] = ']'; fmtstr[len + 1] = '\0'; 1077 // printf( "incl/excl %s\n", fmtstr ); 1070 1078 fmt( is, fmtstr, f.s ); 1071 1079 return is; 1072 } // if 1073 // incl %[xxx], %*[xxx], %w[xxx], %*w[xxx] 1074 // excl %[^xxx], %*[^xxx], %w[^xxx], %*w[^xxx] 1075 fmtstr[start] = '['; start += 1; 1076 if ( f.flags.inex ) { fmtstr[start] = '^'; start += 1; } 1077 strcpy( &fmtstr[start], f.scanset ); // copy includes '\0' 1078 len += start; 1079 fmtstr[len] = ']'; fmtstr[len + 1] = '\0'; 1080 // printf( "incl/excl %s\n", fmtstr ); 1081 fmt( is, fmtstr, f.s ); 1082 return is; 1083 } // ?|? 1084 1085 forall( istype & | istream( istype ) ) 1086 istype & ?|?( istype & is, _Istream_Char f ) { 1087 fmt( is, "%*c" ); // argument variable unused 1088 return is; 1089 } // ?|? 1080 } // ?|? 1081 void ?|?( istype & is, _Istream_Cstr f ) { 1082 (istype &)(is | f); ends( is ); 1083 } // ?|? 1084 1085 istype & ?|?( istype & is, _Istream_Char f ) { 1086 fmt( is, "%*c" ); // argument variable unused 1087 return is; 1088 } // ?|? 1089 void ?|?( istype & is, _Istream_Char f ) { 1090 (istype &)(is | f); ends( is ); 1091 } // ?|? 1092 } // distribution 1090 1093 1091 1094 #define InputFMTImpl( T, CODE ) \ 1092 forall( istype & | istream( istype ) ) \ 1093 istype & ?|?( istype & is, _Istream_Manip(T) f ) { \ 1094 enum { size = 16 }; \ 1095 char fmtstr[size]; \ 1096 if ( f.wd == -1 ) { \ 1097 snprintf( fmtstr, size, "%%%s%s", f.ignore ? "*" : "", CODE ); \ 1098 } else { \ 1099 snprintf( fmtstr, size, "%%%s%d%s", f.ignore ? "*" : "", f.wd, CODE ); \ 1100 } /* if */ \ 1101 /* printf( "%d %s %p\n", f.wd, fmtstr, &f.val ); */ \ 1102 fmt( is, fmtstr, &f.val ); \ 1103 return is; \ 1104 } // ?|? 1095 forall( istype & | basic_istream( istype ) ) { \ 1096 istype & ?|?( istype & is, _Istream_Manip(T) f ) { \ 1097 enum { size = 16 }; \ 1098 char fmtstr[size]; \ 1099 if ( f.wd == -1 ) { \ 1100 snprintf( fmtstr, size, "%%%s%s", f.ignore ? "*" : "", CODE ); \ 1101 } else { \ 1102 snprintf( fmtstr, size, "%%%s%d%s", f.ignore ? "*" : "", f.wd, CODE ); \ 1103 } /* if */ \ 1104 /* printf( "%d %s %p\n", f.wd, fmtstr, &f.val ); */ \ 1105 fmt( is, fmtstr, &f.val ); \ 1106 return is; \ 1107 } /* ?|? */ \ 1108 void ?|?( istype & is, _Istream_Manip(T) f ) { \ 1109 (istype &)(is | f); ends( is ); \ 1110 } /* ?|? */ \ 1111 } // distribution 1105 1112 1106 1113 InputFMTImpl( signed char, "hhi" ) … … 1119 1126 InputFMTImpl( long double, "Lf" ) 1120 1127 1121 forall( istype & | istream( istype ) ) 1122 istype & ?|?( istype & is, _Istream_Manip(float _Complex) fc ) { 1123 float re, im; 1124 _Istream_Manip(float) fmtuc @= { re, fc.wd, fc.ignore }; 1125 is | fmtuc; 1126 &fmtuc.val = &im; 1127 is | fmtuc; 1128 if ( ! fc.ignore ) fc.val = re + im * _Complex_I; // re/im are uninitialized for ignore 1129 return is; 1130 } // ?|? 1131 1132 forall( istype & | istream( istype ) ) 1133 istype & ?|?( istype & is, _Istream_Manip(double _Complex) dc ) { 1134 double re, im; 1135 _Istream_Manip(double) fmtuc @= { re, dc.wd, dc.ignore }; 1136 is | fmtuc; 1137 &fmtuc.val = &im; 1138 is | fmtuc; 1139 if ( ! dc.ignore ) dc.val = re + im * _Complex_I; // re/im are uninitialized for ignore 1140 return is; 1141 } // ?|? 1142 1143 forall( istype & | istream( istype ) ) 1144 istype & ?|?( istype & is, _Istream_Manip(long double _Complex) ldc ) { 1145 long double re, im; 1146 _Istream_Manip(long double) fmtuc @= { re, ldc.wd, ldc.ignore }; 1147 is | fmtuc; 1148 &fmtuc.val = &im; 1149 is | fmtuc; 1150 if ( ! ldc.ignore ) ldc.val = re + im * _Complex_I; // re/im are uninitialized for ignore 1151 return is; 1152 } // ?|? 1128 forall( istype & | basic_istream( istype ) ) { 1129 istype & ?|?( istype & is, _Istream_Manip(float _Complex) fc ) { 1130 float re, im; 1131 _Istream_Manip(float) fmtuc @= { re, fc.wd, fc.ignore }; 1132 is | fmtuc; 1133 &fmtuc.val = &im; 1134 is | fmtuc; 1135 if ( ! fc.ignore ) fc.val = re + im * _Complex_I; // re/im are uninitialized for ignore 1136 return is; 1137 } // ?|? 1138 void ?|?( istype & is, _Istream_Manip(float _Complex) fc ) { 1139 (istype &)(is | fc); ends( is ); 1140 } // ?|? 1141 1142 istype & ?|?( istype & is, _Istream_Manip(double _Complex) dc ) { 1143 double re, im; 1144 _Istream_Manip(double) fmtuc @= { re, dc.wd, dc.ignore }; 1145 is | fmtuc; 1146 &fmtuc.val = &im; 1147 is | fmtuc; 1148 if ( ! dc.ignore ) dc.val = re + im * _Complex_I; // re/im are uninitialized for ignore 1149 return is; 1150 } // ?|? 1151 void ?|?( istype & is, _Istream_Manip(double _Complex) dc ) { 1152 (istype &)(is | dc); ends( is ); 1153 } // ?|? 1154 1155 istype & ?|?( istype & is, _Istream_Manip(long double _Complex) ldc ) { 1156 long double re, im; 1157 _Istream_Manip(long double) fmtuc @= { re, ldc.wd, ldc.ignore }; 1158 is | fmtuc; 1159 &fmtuc.val = &im; 1160 is | fmtuc; 1161 if ( ! ldc.ignore ) ldc.val = re + im * _Complex_I; // re/im are uninitialized for ignore 1162 return is; 1163 } // ?|? 1164 void ?|?( istype & is, _Istream_Manip(long double _Complex) ldc ) { 1165 (istype &)(is | ldc); ends( is ); 1166 } // ?|? 1167 } // distribution 1153 1168 1154 1169 // Local Variables: // -
libcfa/src/iostream.hfa
rfeacef9 r5407cdc 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue A ug 11 22:16:14 202013 // Update Count : 3 5012 // Last Modified On : Tue Apr 27 17:59:21 2021 13 // Update Count : 398 14 14 // 15 15 … … 22 22 23 23 24 trait ostream( ostype & ) {24 trait basic_ostream( ostype & ) { 25 25 // private 26 bool $sepPrt( ostype & ); // get separator state (on/off)27 void $sepReset( ostype & ); // set separator state to default state28 void $sepReset( ostype &, bool ); // set separator and default state29 const char * $sepGetCur( ostype & ); // get current separator string30 void $sepSetCur( ostype &, const char [] ); // set current separator string31 bool $getNL( ostype & ); // check newline32 void $setNL( ostype &, bool ); // saw newline33 bool $getANL( ostype & ); // get auto newline (on/off)34 bool $getPrt( ostype & ); // get fmt called in output cascade35 void $setPrt( ostype &, bool ); // set fmt called in output cascade26 bool sepPrt$( ostype & ); // get separator state (on/off) 27 void sepReset$( ostype & ); // set separator state to default state 28 void sepReset$( ostype &, bool ); // set separator and default state 29 const char * sepGetCur$( ostype & ); // get current separator string 30 void sepSetCur$( ostype &, const char [] ); // set current separator string 31 bool getNL$( ostype & ); // check newline 32 void setNL$( ostype &, bool ); // saw newline 33 bool getANL$( ostype & ); // get auto newline (on/off) 34 bool getPrt$( ostype & ); // get fmt called in output cascade 35 void setPrt$( ostype &, bool ); // set fmt called in output cascade 36 36 // public 37 37 void sepOn( ostype & ); // turn separator state on … … 47 47 void sepSetTuple( ostype &, const char [] ); // set tuple separator to string (15 character maximum) 48 48 49 void ends( ostype & os ); // end of output statement 50 int fail( ostype & ); 49 void ends( ostype & ); // end of output statement 50 int fmt( ostype &, const char format[], ... ) __attribute__(( format(printf, 2, 3) )); 51 }; // basic_ostream 52 53 trait ostream( ostype & | basic_ostream( ostype ) ) { 51 54 int flush( ostype & ); 52 void open( ostype & os, const char name[], const char mode[] ); 53 void close( ostype & os ); 55 bool fail( ostype & ); // operation failed? 56 void open( ostype &, const char name[], const char mode[] ); 57 void close( ostype & ); 54 58 ostype & write( ostype &, const char [], size_t ); 55 int fmt( ostype &, const char format[], ... ) __attribute__(( format(printf, 2, 3) ));59 void acquire( ostype & ); // concurrent access 56 60 }; // ostream 57 61 … … 66 70 // implement writable for intrinsic types 67 71 68 forall( ostype & | ostream( ostype ) ) {72 forall( ostype & | basic_ostream( ostype ) ) { 69 73 ostype & ?|?( ostype &, bool ); 70 74 void ?|?( ostype &, bool ); … … 93 97 ostype & ?|?( ostype &, unsigned long long int ); 94 98 void ?|?( ostype &, unsigned long long int ); 95 #if defined( __SIZEOF_INT128__ )99 #if defined( __SIZEOF_INT128__ ) 96 100 ostype & ?|?( ostype &, int128 ); 97 101 void ?|?( ostype &, int128 ); 98 102 ostype & ?|?( ostype &, unsigned int128 ); 99 103 void ?|?( ostype &, unsigned int128 ); 100 #endif // __SIZEOF_INT128__104 #endif // __SIZEOF_INT128__ 101 105 102 106 ostype & ?|?( ostype &, float ); … … 117 121 void ?|?( ostype &, const char [] ); 118 122 // ostype & ?|?( ostype &, const char16_t * ); 119 #if ! ( __ARM_ARCH_ISA_ARM == 1 && __ARM_32BIT_STATE == 1 ) // char32_t == wchar_t => ambiguous123 #if ! ( __ARM_ARCH_ISA_ARM == 1 && __ARM_32BIT_STATE == 1 ) // char32_t == wchar_t => ambiguous 120 124 // ostype & ?|?( ostype &, const char32_t * ); 121 #endif // ! ( __ARM_ARCH_ISA_ARM == 1 && __ARM_32BIT_STATE == 1 )125 #endif // ! ( __ARM_ARCH_ISA_ARM == 1 && __ARM_32BIT_STATE == 1 ) 122 126 // ostype & ?|?( ostype &, const wchar_t * ); 123 127 ostype & ?|?( ostype &, const void * ); … … 139 143 } // distribution 140 144 145 forall( ostype & | ostream( ostype ) ) { 146 ostype & acquire( ostype & ); 147 } // distribution 148 141 149 // tuples 142 150 forall( ostype &, T, Params... | writeable( T, ostype ) | { ostype & ?|?( ostype &, Params ); } ) { … … 156 164 struct _Ostream_Manip { 157 165 T val; // polymorphic base-type 158 unsigned int wd, pc; // width, precision166 int wd, pc; // width, precision: signed for computations 159 167 char base; // numeric base / floating-point style 160 168 union { 161 169 unsigned char all; 162 170 struct { 171 unsigned char eng:1; // engineering notation 163 172 unsigned char neg:1; // val is negative 164 173 unsigned char pc:1; // precision specified … … 183 192 _Ostream_Manip(T) hex( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'x', { .all : 0 } }; } \ 184 193 _Ostream_Manip(T) wd( unsigned int w, T val ) { return (_Ostream_Manip(T))@{ val, w, 0, CODE, { .all : 0 } }; } \ 185 _Ostream_Manip(T) wd( unsigned int w, unsigned charpc, T val ) { return (_Ostream_Manip(T))@{ val, w, pc, CODE, { .flags.pc : true } }; } \194 _Ostream_Manip(T) wd( unsigned int w, unsigned int pc, T val ) { return (_Ostream_Manip(T))@{ val, w, pc, CODE, { .flags.pc : true } }; } \ 186 195 _Ostream_Manip(T) & wd( unsigned int w, _Ostream_Manip(T) & fmt ) { fmt.wd = w; return fmt; } \ 187 _Ostream_Manip(T) & wd( unsigned int w, unsigned charpc, _Ostream_Manip(T) & fmt ) { fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \196 _Ostream_Manip(T) & wd( unsigned int w, unsigned int pc, _Ostream_Manip(T) & fmt ) { fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \ 188 197 _Ostream_Manip(T) & left( _Ostream_Manip(T) & fmt ) { fmt.flags.left = true; return fmt; } \ 189 198 _Ostream_Manip(T) & upcase( _Ostream_Manip(T) & fmt ) { if ( fmt.base == 'x' || fmt.base == 'b' ) fmt.base -= 32; /* upper case */ return fmt; } \ … … 193 202 _Ostream_Manip(T) & sign( _Ostream_Manip(T) & fmt ) { fmt.flags.sign = true; return fmt; } \ 194 203 } /* distribution */ \ 195 forall( ostype & | ostream( ostype ) ) { \204 forall( ostype & | basic_ostream( ostype ) ) { \ 196 205 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ); \ 197 206 void ?|?( ostype & os, _Ostream_Manip(T) f ); \ … … 220 229 _Ostream_Manip(T) hex( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'a', { .all : 0 } }; } \ 221 230 _Ostream_Manip(T) sci( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'e', { .all : 0 } }; } \ 222 _Ostream_Manip(T) wd( unsigned int w, T val ) { return (_Ostream_Manip(T))@{ val, w, 0, 'f', { .all : 0 } }; } \ 223 _Ostream_Manip(T) wd( unsigned int w, unsigned char pc, T val ) { return (_Ostream_Manip(T))@{ val, w, pc, 'f', { .flags.pc : true } }; } \ 224 _Ostream_Manip(T) ws( unsigned int w, unsigned char pc, T val ) { return (_Ostream_Manip(T))@{ val, w, pc, 'g', { .flags.pc : true } }; } \ 225 _Ostream_Manip(T) & wd( unsigned int w, _Ostream_Manip(T) & fmt ) { fmt.wd = w; return fmt; } \ 226 _Ostream_Manip(T) & wd( unsigned int w, unsigned char pc, _Ostream_Manip(T) & fmt ) { fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \ 231 _Ostream_Manip(T) eng( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'g', { .flags.eng : true } }; } \ 232 _Ostream_Manip(T) wd( unsigned int w, T val ) { return (_Ostream_Manip(T))@{ val, w, 0, 'g', { .all : 0 } }; } \ 233 _Ostream_Manip(T) wd( unsigned int w, unsigned int pc, T val ) { return (_Ostream_Manip(T))@{ val, w, pc, 'f', { .flags.pc : true } }; } \ 234 _Ostream_Manip(T) ws( unsigned int w, unsigned int pc, T val ) { return (_Ostream_Manip(T))@{ val, w, pc, 'g', { .flags.pc : true } }; } \ 235 _Ostream_Manip(T) & wd( unsigned int w, _Ostream_Manip(T) & fmt ) { if ( fmt.flags.eng ) fmt.base = 'f'; fmt.wd = w; return fmt; } \ 236 _Ostream_Manip(T) & wd( unsigned int w, unsigned int pc, _Ostream_Manip(T) & fmt ) { if ( fmt.flags.eng ) fmt.base = 'f'; fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \ 237 _Ostream_Manip(T) & ws( unsigned int w, unsigned int pc, _Ostream_Manip(T) & fmt ) { fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \ 227 238 _Ostream_Manip(T) & left( _Ostream_Manip(T) & fmt ) { fmt.flags.left = true; return fmt; } \ 228 239 _Ostream_Manip(T) upcase( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'G', { .all : 0 } }; } \ … … 233 244 _Ostream_Manip(T) nodp( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'g', { .flags.nobsdp : true } }; } \ 234 245 _Ostream_Manip(T) & nodp( _Ostream_Manip(T) & fmt ) { fmt.flags.nobsdp = true; return fmt; } \ 246 _Ostream_Manip(T) unit( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'g', { .flags.nobsdp : true } }; } \ 247 _Ostream_Manip(T) & unit( _Ostream_Manip(T) & fmt ) { fmt.flags.nobsdp = true; return fmt; } \ 235 248 } /* distribution */ \ 236 forall( ostype & | ostream( ostype ) ) { \249 forall( ostype & | basic_ostream( ostype ) ) { \ 237 250 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ); \ 238 251 void ?|?( ostype & os, _Ostream_Manip(T) f ); \ … … 254 267 _Ostream_Manip(char) & nobase( _Ostream_Manip(char) & fmt ) { fmt.flags.nobsdp = true; return fmt; } 255 268 } // distribution 256 forall( ostype & | ostream( ostype ) ) {269 forall( ostype & | basic_ostream( ostype ) ) { 257 270 ostype & ?|?( ostype & os, _Ostream_Manip(char) f ); 258 271 void ?|?( ostype & os, _Ostream_Manip(char) f ); … … 266 279 _Ostream_Manip(const char *) hex( const char s[] ) { return (_Ostream_Manip(const char *))@{ s, 1, 0, 'x', { .all : 0 } }; } 267 280 _Ostream_Manip(const char *) wd( unsigned int w, const char s[] ) { return (_Ostream_Manip(const char *))@{ s, w, 0, 's', { .all : 0 } }; } 268 _Ostream_Manip(const char *) wd( unsigned int w, unsigned charpc, const char s[] ) { return (_Ostream_Manip(const char *))@{ s, w, pc, 's', { .flags.pc : true } }; }281 _Ostream_Manip(const char *) wd( unsigned int w, unsigned int pc, const char s[] ) { return (_Ostream_Manip(const char *))@{ s, w, pc, 's', { .flags.pc : true } }; } 269 282 _Ostream_Manip(const char *) & wd( unsigned int w, _Ostream_Manip(const char *) & fmt ) { fmt.wd = w; return fmt; } 270 _Ostream_Manip(const char *) & wd( unsigned int w, unsigned charpc, _Ostream_Manip(const char *) & fmt ) { fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; }283 _Ostream_Manip(const char *) & wd( unsigned int w, unsigned int pc, _Ostream_Manip(const char *) & fmt ) { fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; } 271 284 _Ostream_Manip(const char *) & left( _Ostream_Manip(const char *) & fmt ) { fmt.flags.left = true; return fmt; } 272 285 _Ostream_Manip(const char *) & nobase( _Ostream_Manip(const char *) & fmt ) { fmt.flags.nobsdp = true; return fmt; } 273 286 } // distribution 274 forall( ostype & | ostream( ostype ) ) {287 forall( ostype & | basic_ostream( ostype ) ) { 275 288 ostype & ?|?( ostype & os, _Ostream_Manip(const char *) f ); 276 289 void ?|?( ostype & os, _Ostream_Manip(const char *) f ); … … 281 294 282 295 283 trait istream( istype & ) { 296 trait basic_istream( istype & ) { 297 bool getANL( istype & ); // get scan newline (on/off) 284 298 void nlOn( istype & ); // read newline 285 299 void nlOff( istype & ); // scan newline 286 bool getANL( istype & ); // get scan newline (on/off) 287 int fail( istype & ); 300 301 void ends( istype & os ); // end of output statement 302 int fmt( istype &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) )); 303 istype & ungetc( istype &, char ); 288 304 int eof( istype & ); 305 }; // basic_istream 306 307 trait istream( istype & | basic_istream( istype ) ) { 308 bool fail( istype & ); 289 309 void open( istype & is, const char name[] ); 290 310 void close( istype & is ); 291 311 istype & read( istype &, char *, size_t ); 292 istype & ungetc( istype &, char ); 293 int fmt( istype &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) )); 312 void acquire( istype & ); // concurrent access 294 313 }; // istream 295 314 … … 298 317 }; // readable 299 318 300 forall( istype & | istream( istype ) ) {319 forall( istype & | basic_istream( istype ) ) { 301 320 istype & ?|?( istype &, bool & ); 321 void ?|?( istype &, bool & ); 302 322 303 323 istype & ?|?( istype &, char & ); 324 void ?|?( istype &, char & ); 304 325 istype & ?|?( istype &, signed char & ); 326 void ?|?( istype &, signed char & ); 305 327 istype & ?|?( istype &, unsigned char & ); 328 void ?|?( istype &, unsigned char & ); 306 329 307 330 istype & ?|?( istype &, short int & ); 331 void ?|?( istype &, short int & ); 308 332 istype & ?|?( istype &, unsigned short int & ); 333 void ?|?( istype &, unsigned short int & ); 309 334 istype & ?|?( istype &, int & ); 335 void ?|?( istype &, int & ); 310 336 istype & ?|?( istype &, unsigned int & ); 337 void ?|?( istype &, unsigned int & ); 311 338 istype & ?|?( istype &, long int & ); 339 void ?|?( istype &, long int & ); 312 340 istype & ?|?( istype &, unsigned long int & ); 341 void ?|?( istype &, unsigned long int & ); 313 342 istype & ?|?( istype &, long long int & ); 343 void ?|?( istype &, long long int & ); 314 344 istype & ?|?( istype &, unsigned long long int & ); 315 #if defined( __SIZEOF_INT128__ ) 345 void ?|?( istype &, unsigned long long int & ); 346 #if defined( __SIZEOF_INT128__ ) 316 347 istype & ?|?( istype &, int128 & ); 348 void ?|?( istype &, int128 & ); 317 349 istype & ?|?( istype &, unsigned int128 & ); 318 #endif // __SIZEOF_INT128__ 350 void ?|?( istype &, unsigned int128 & ); 351 #endif // __SIZEOF_INT128__ 319 352 320 353 istype & ?|?( istype &, float & ); 354 void ?|?( istype &, float & ); 321 355 istype & ?|?( istype &, double & ); 356 void ?|?( istype &, double & ); 322 357 istype & ?|?( istype &, long double & ); 358 void ?|?( istype &, long double & ); 323 359 324 360 istype & ?|?( istype &, float _Complex & ); 361 void ?|?( istype &, float _Complex & ); 325 362 istype & ?|?( istype &, double _Complex & ); 363 void ?|?( istype &, double _Complex & ); 326 364 istype & ?|?( istype &, long double _Complex & ); 365 void ?|?( istype &, long double _Complex & ); 327 366 328 367 // istype & ?|?( istype &, const char [] ); 329 istype & ?|?( istype &, char * ); 368 istype & ?|?( istype &, char [] ); 369 void ?|?( istype &, char [] ); 330 370 331 371 // manipulators 332 372 istype & ?|?( istype &, istype & (*)( istype & ) ); 373 void ?|?( istype &, istype & (*)( istype & ) ); 333 374 istype & nl( istype & is ); 334 375 istype & nlOn( istype & ); 335 376 istype & nlOff( istype & ); 377 } // distribution 378 379 forall( istype & | istream( istype ) ) { 380 istype & acquire( istype & ); 336 381 } // distribution 337 382 … … 352 397 353 398 static inline { 399 _Istream_Cstr skip( const char scanset[] ) { return (_Istream_Cstr){ 0p, scanset, -1, { .all : 0 } }; } 354 400 _Istream_Cstr skip( unsigned int n ) { return (_Istream_Cstr){ 0p, 0p, n, { .all : 0 } }; } 355 _Istream_Cstr skip( const char scanset[] ) { return (_Istream_Cstr){ 0p, scanset, -1, { .all : 0 } }; }356 401 _Istream_Cstr incl( const char scanset[], char * s ) { return (_Istream_Cstr){ s, scanset, -1, { .flags.inex : false } }; } 357 402 _Istream_Cstr & incl( const char scanset[], _Istream_Cstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = false; return fmt; } … … 363 408 _Istream_Cstr & wdi( unsigned int w, _Istream_Cstr & fmt ) { fmt.wd = w; return fmt; } 364 409 } // distribution 365 forall( istype & | istream( istype ) ) istype & ?|?( istype & is, _Istream_Cstr f ); 410 forall( istype & | basic_istream( istype ) ) { 411 istype & ?|?( istype & is, _Istream_Cstr f ); 412 void ?|?( istype & is, _Istream_Cstr f ); 413 } 366 414 367 415 struct _Istream_Char { … … 373 421 _Istream_Char & ignore( _Istream_Char & fmt ) { fmt.ignore = true; return fmt; } 374 422 } // distribution 375 forall( istype & | istream( istype ) ) istype & ?|?( istype & is, _Istream_Char f ); 423 forall( istype & | basic_istream( istype ) ) { 424 istype & ?|?( istype & is, _Istream_Char f ); 425 void ?|?( istype & is, _Istream_Char f ); 426 } 376 427 377 428 forall( T & | sized( T ) ) … … 389 440 _Istream_Manip(T) & wdi( unsigned int w, _Istream_Manip(T) & fmt ) { fmt.wd = w; return fmt; } \ 390 441 } /* distribution */ \ 391 forall( istype & | istream( istype ) ) { \442 forall( istype & | basic_istream( istype ) ) { \ 392 443 istype & ?|?( istype & is, _Istream_Manip(T) f ); \ 444 void ?|?( istype & is, _Istream_Manip(T) f ); \ 393 445 } // ?|? 394 446 -
libcfa/src/math.hfa
rfeacef9 r5407cdc 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // math --7 // math.hfa -- 8 8 // 9 9 // Author : Peter A. Buhr 10 10 // Created On : Mon Apr 18 23:37:04 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Aug 24 08:56:20 202013 // Update Count : 1 2612 // Last Modified On : Thu Apr 15 11:47:56 2021 13 // Update Count : 132 14 14 // 15 15 … … 100 100 long double _Complex log( long double _Complex x ) { return clogl( x ); } 101 101 102 // O(1) polymorphic integer log2, using clz, which returns the number of leading 0-bits, starting at the most 103 // significant bit (single instruction on x86) 104 int log2( unsigned int n ) { return n == 0 ? -1 : sizeof(n) * __CHAR_BIT__ - 1 - __builtin_clz( n ); } 105 long int log2( unsigned long int n ) { return n == 0 ? -1 : sizeof(n) * __CHAR_BIT__ - 1 - __builtin_clzl( n ); } 106 long long int log2( unsigned long long int n ) { return n == 0 ? -1 : sizeof(n) * __CHAR_BIT__ - 1 - __builtin_clzll( n ); } 102 107 float log2( float x ) { return log2f( x ); } 103 108 // extern "C" { double log2( double ); } -
libcfa/src/startup.cfa
rfeacef9 r5407cdc 39 39 40 40 void disable_interrupts() __attribute__(( weak )) {} 41 void enable_interrupts _noPoll() __attribute__(( weak )) {}41 void enable_interrupts() __attribute__(( weak )) {} 42 42 } // extern "C" 43 43 -
libcfa/src/stdlib.hfa
rfeacef9 r5407cdc 10 10 // Created On : Thu Jan 28 17:12:35 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : T hu Jan 21 22:02:13 202113 // Update Count : 57 412 // Last Modified On : Tue Apr 20 21:20:03 2021 13 // Update Count : 575 14 14 // 15 15 … … 44 44 45 45 // Macro because of returns 46 #define $ARRAY_ALLOC( allocation, alignment, dim ) \46 #define ARRAY_ALLOC$( allocation, alignment, dim ) \ 47 47 if ( _Alignof(T) <= libAlign() ) return (T *)(void *)allocation( dim, (size_t)sizeof(T) ); /* C allocation */ \ 48 48 else return (T *)alignment( _Alignof(T), dim, sizeof(T) ) … … 57 57 58 58 T * aalloc( size_t dim ) { 59 $ARRAY_ALLOC( aalloc, amemalign, dim );59 ARRAY_ALLOC$( aalloc, amemalign, dim ); 60 60 } // aalloc 61 61 62 62 T * calloc( size_t dim ) { 63 $ARRAY_ALLOC( calloc, cmemalign, dim );63 ARRAY_ALLOC$( calloc, cmemalign, dim ); 64 64 } // calloc 65 65 … … 119 119 S_fill(T) ?`fill( T a[], size_t nmemb ) { S_fill(T) ret = {'a', nmemb}; ret.fill.a = a; return ret; } 120 120 121 3. Replace the $alloc_internalfunction which is outside ttype forall-block with following function:122 T * $alloc_internal( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill) {121 3. Replace the alloc_internal$ function which is outside ttype forall-block with following function: 122 T * alloc_internal$( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill) { 123 123 T * ptr = NULL; 124 124 size_t size = sizeof(T); … … 145 145 146 146 return ptr; 147 } // $alloc_internal147 } // alloc_internal$ 148 148 */ 149 149 … … 175 175 S_realloc(T) ?`realloc ( T * a ) { return (S_realloc(T)){a}; } 176 176 177 T * $alloc_internal( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill ) {177 T * alloc_internal$( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill ) { 178 178 T * ptr = NULL; 179 179 size_t size = sizeof(T); … … 206 206 207 207 return ptr; 208 } // $alloc_internal209 210 forall( TT... | { T * $alloc_internal( void *, T *, size_t, size_t, S_fill(T), TT ); } ) {211 212 T * $alloc_internal( void * , T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill, T_resize Resize, TT rest) {213 return $alloc_internal( Resize, (T*)0p, Align, Dim, Fill, rest);214 } 215 216 T * $alloc_internal( void * Resize, T * , size_t Align, size_t Dim, S_fill(T) Fill, S_realloc(T) Realloc, TT rest) {217 return $alloc_internal( (void*)0p, Realloc, Align, Dim, Fill, rest);218 } 219 220 T * $alloc_internal( void * Resize, T * Realloc, size_t , size_t Dim, S_fill(T) Fill, T_align Align, TT rest) {221 return $alloc_internal( Resize, Realloc, Align, Dim, Fill, rest);222 } 223 224 T * $alloc_internal( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T) , S_fill(T) Fill, TT rest) {225 return $alloc_internal( Resize, Realloc, Align, Dim, Fill, rest);208 } // alloc_internal$ 209 210 forall( TT... | { T * alloc_internal$( void *, T *, size_t, size_t, S_fill(T), TT ); } ) { 211 212 T * alloc_internal$( void * , T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill, T_resize Resize, TT rest) { 213 return alloc_internal$( Resize, (T*)0p, Align, Dim, Fill, rest); 214 } 215 216 T * alloc_internal$( void * Resize, T * , size_t Align, size_t Dim, S_fill(T) Fill, S_realloc(T) Realloc, TT rest) { 217 return alloc_internal$( (void*)0p, Realloc, Align, Dim, Fill, rest); 218 } 219 220 T * alloc_internal$( void * Resize, T * Realloc, size_t , size_t Dim, S_fill(T) Fill, T_align Align, TT rest) { 221 return alloc_internal$( Resize, Realloc, Align, Dim, Fill, rest); 222 } 223 224 T * alloc_internal$( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T) , S_fill(T) Fill, TT rest) { 225 return alloc_internal$( Resize, Realloc, Align, Dim, Fill, rest); 226 226 } 227 227 228 228 T * alloc( TT all ) { 229 return $alloc_internal( (void*)0p, (T*)0p, (_Alignof(T) > libAlign() ? _Alignof(T) : libAlign()), (size_t)1, (S_fill(T)){'0'}, all);229 return alloc_internal$( (void*)0p, (T*)0p, (_Alignof(T) > libAlign() ? _Alignof(T) : libAlign()), (size_t)1, (S_fill(T)){'0'}, all); 230 230 } 231 231 232 232 T * alloc( size_t dim, TT all ) { 233 return $alloc_internal( (void*)0p, (T*)0p, (_Alignof(T) > libAlign() ? _Alignof(T) : libAlign()), dim, (S_fill(T)){'0'}, all);233 return alloc_internal$( (void*)0p, (T*)0p, (_Alignof(T) > libAlign() ? _Alignof(T) : libAlign()), dim, (S_fill(T)){'0'}, all); 234 234 } 235 235 -
libcfa/src/time.hfa
rfeacef9 r5407cdc 10 10 // Created On : Wed Mar 14 23:18:57 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Jun 17 16:13:00 202013 // Update Count : 66 312 // Last Modified On : Wed Apr 21 06:32:31 2021 13 // Update Count : 667 14 14 // 15 15 … … 28 28 29 29 static inline { 30 void ?{}( Duration & dur, timeval t ) with( dur ) { tn = (int64_t)t.tv_sec * TIMEGRAN + t.tv_usec * 1000; } 31 void ?{}( Duration & dur, timespec t ) with( dur ) { tn = (int64_t)t.tv_sec * TIMEGRAN + t.tv_nsec; } 32 30 33 Duration ?=?( Duration & dur, __attribute__((unused)) zero_t ) { return dur{ 0 }; } 34 Duration ?=?( Duration & dur, timeval t ) with( dur ) { 35 tn = (int64_t)t.tv_sec * TIMEGRAN + t.tv_usec * (TIMEGRAN / 1_000_000LL); 36 return dur; 37 } // ?=? 38 Duration ?=?( Duration & dur, timespec t ) with( dur ) { 39 tn = (int64_t)t.tv_sec * TIMEGRAN + t.tv_nsec; 40 return dur; 41 } // ?=? 31 42 32 43 Duration +?( Duration rhs ) with( rhs ) { return (Duration)@{ +tn }; } … … 49 60 Duration ?%?( Duration lhs, Duration rhs ) { return (Duration)@{ lhs.tn % rhs.tn }; } 50 61 Duration ?%=?( Duration & lhs, Duration rhs ) { lhs = lhs % rhs; return lhs; } 62 63 bool ?==?( Duration lhs, __attribute__((unused)) zero_t ) { return lhs.tn == 0; } 64 bool ?!=?( Duration lhs, __attribute__((unused)) zero_t ) { return lhs.tn != 0; } 65 bool ?<? ( Duration lhs, __attribute__((unused)) zero_t ) { return lhs.tn < 0; } 66 bool ?<=?( Duration lhs, __attribute__((unused)) zero_t ) { return lhs.tn <= 0; } 67 bool ?>? ( Duration lhs, __attribute__((unused)) zero_t ) { return lhs.tn > 0; } 68 bool ?>=?( Duration lhs, __attribute__((unused)) zero_t ) { return lhs.tn >= 0; } 51 69 52 70 bool ?==?( Duration lhs, Duration rhs ) { return lhs.tn == rhs.tn; } … … 56 74 bool ?>? ( Duration lhs, Duration rhs ) { return lhs.tn > rhs.tn; } 57 75 bool ?>=?( Duration lhs, Duration rhs ) { return lhs.tn >= rhs.tn; } 58 59 bool ?==?( Duration lhs, __attribute__((unused)) zero_t ) { return lhs.tn == 0; }60 bool ?!=?( Duration lhs, __attribute__((unused)) zero_t ) { return lhs.tn != 0; }61 bool ?<? ( Duration lhs, __attribute__((unused)) zero_t ) { return lhs.tn < 0; }62 bool ?<=?( Duration lhs, __attribute__((unused)) zero_t ) { return lhs.tn <= 0; }63 bool ?>? ( Duration lhs, __attribute__((unused)) zero_t ) { return lhs.tn > 0; }64 bool ?>=?( Duration lhs, __attribute__((unused)) zero_t ) { return lhs.tn >= 0; }65 76 66 77 Duration abs( Duration rhs ) { return rhs.tn >= 0 ? rhs : -rhs; } … … 152 163 void ?{}( Time & time, int year, int month = 1, int day = 1, int hour = 0, int min = 0, int sec = 0, int64_t nsec = 0 ); 153 164 static inline { 165 void ?{}( Time & time, timeval t ) with( time ) { tn = (int64_t)t.tv_sec * TIMEGRAN + t.tv_usec * 1000; } 166 void ?{}( Time & time, timespec t ) with( time ) { tn = (int64_t)t.tv_sec * TIMEGRAN + t.tv_nsec; } 167 154 168 Time ?=?( Time & time, __attribute__((unused)) zero_t ) { return time{ 0 }; } 155 156 void ?{}( Time & time, timeval t ) with( time ) { tn = (int64_t)t.tv_sec * TIMEGRAN + t.tv_usec * 1000; }157 169 Time ?=?( Time & time, timeval t ) with( time ) { 158 170 tn = (int64_t)t.tv_sec * TIMEGRAN + t.tv_usec * (TIMEGRAN / 1_000_000LL); 159 171 return time; 160 172 } // ?=? 161 162 void ?{}( Time & time, timespec t ) with( time ) { tn = (int64_t)t.tv_sec * TIMEGRAN + t.tv_nsec; }163 173 Time ?=?( Time & time, timespec t ) with( time ) { 164 174 tn = (int64_t)t.tv_sec * TIMEGRAN + t.tv_nsec; -
libcfa/src/virtual.c
rfeacef9 r5407cdc 15 15 16 16 #include "virtual.h" 17 #include "assert.h" 17 18 18 19 int __cfa__is_parent( struct __cfa__parent_vtable const * parent, 19 20 struct __cfa__parent_vtable const * child ) { 21 assert( child ); 20 22 do { 21 23 if ( parent == child ) … … 28 30 void * __cfa__virtual_cast( struct __cfa__parent_vtable const * parent, 29 31 struct __cfa__parent_vtable const * const * child ) { 32 assert( child ); 30 33 return (__cfa__is_parent(parent, *child)) ? (void *)child : (void *)0; 31 34 }
Note:
See TracChangeset
for help on using the changeset viewer.