Changeset 5407cdc for libcfa


Ignore:
Timestamp:
Apr 28, 2021, 4:56:50 PM (5 years ago)
Author:
Thierry Delisle <tdelisle@…>
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.
Message:

Merge branch 'master' of plg.uwaterloo.ca:software/cfa/cfa-cc

Location:
libcfa
Files:
6 added
3 deleted
54 edited

Legend:

Unmodified
Added
Removed
  • libcfa/configure.ac

    rfeacef9 r5407cdc  
    169169AH_TEMPLATE([CFA_HAVE_IOSQE_FIXED_FILE],[Defined if io_uring support is present when compiling libcfathread and supports the flag FIXED_FILE.])
    170170AH_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.])
    172171AH_TEMPLATE([CFA_HAVE_IOSQE_IO_LINK],[Defined if io_uring support is present when compiling libcfathread and supports the flag IO_LINK.])
    173172AH_TEMPLATE([CFA_HAVE_IOSQE_IO_HARDLINK],[Defined if io_uring support is present when compiling libcfathread and supports the flag IO_HARDLINK.])
     173AH_TEMPLATE([CFA_HAVE_IOSQE_ASYNC],[Defined if io_uring support is present when compiling libcfathread and supports the flag ASYNC.])
     174AH_TEMPLATE([CFA_HAVE_IOSQE_BUFFER_SELECT],[Defined if io_uring support is present when compiling libcfathread and supports the flag BUFFER_SELEC.])
    174175AH_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.])
    175176AH_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.])
     
    182183
    183184define(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])
     185define(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])
    185186
    186187define(ioring_from_decls, [
  • libcfa/prelude/builtins.c

    rfeacef9 r5407cdc  
    99// Author           : Peter A. Buhr
    1010// Created On       : Fri Jul 21 16:21:03 2017
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Tue Oct 27 14:42:00 2020
    13 // Update Count     : 111
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Tue Apr 13 17:26:32 2021
     13// Update Count     : 117
    1414//
    1515
     
    125125} // distribution
    126126
    127 #define __CFA_BASE_COMP_1__() if ( ep == 1 ) return 1
    128 #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)
    129129#define __CFA_EXP_OVERFLOW__() if ( y >= sizeof(y) * CHAR_BIT ) return 0
    130130
     
    134134        __CFA_BASE_COMP_2__();                                                          /* special case, positive shifting for integral types */ \
    135135        __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 */ \
    137137        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; \
    140140        } \
    141         return ep * op
     141        return x * op
    142142
    143143static 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__(); }
    146147        // 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__(); }
    149151} // distribution
    150152
     
    157159
    158160static 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__(); }
    161164} // distribution
    162165
     
    166169
    167170static inline {
     171        long int ?\=?( int & x, unsigned int y ) { x = x \ y; return x; }
    168172        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; }
    169175        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; }
    172177} // distribution
    173178
  • libcfa/prelude/defines.hfa.in

    rfeacef9 r5407cdc  
    149149
    150150/* 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
    151155   supports the flag FIXED_FILE. */
    152156#undef CFA_HAVE_IOSQE_FIXED_FILE
  • libcfa/src/Makefile.am

    rfeacef9 r5407cdc  
    1111## Created On       : Sun May 31 08:54:01 2015
    1212## Last Modified By : Peter A. Buhr
    13 ## Last Modified On : Wed Dec  9 22:46:14 2020
    14 ## Update Count     : 250
     13## Last Modified On : Sat Apr 24 09:09:56 2021
     14## Update Count     : 254
    1515###############################################################################
    1616
     
    5656        bits/queue.hfa \
    5757        bits/sequence.hfa \
     58        containers/array.hfa \
    5859        concurrency/iofwd.hfa \
    5960        containers/list.hfa \
     61        containers/queueLockFree.hfa \
    6062        containers/stackLockFree.hfa \
    6163        vec/vec.hfa \
     
    6769        common.hfa \
    6870        fstream.hfa \
     71        strstream.hfa \
    6972        heap.hfa \
    7073        iostream.hfa \
  • libcfa/src/bits/debug.hfa

    rfeacef9 r5407cdc  
    101101        __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_print_buffer(STDERR_FILENO, __VA_ARGS__))
    102102#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 ))
    104104#define __cfadbg_print_buffer_local(group, ...) \
    105105        __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  
    7474        #error unsupported architecture
    7575#endif
     76
     77#define CFA_IO_LAZY (1_l64u << 32_l64u)
  • libcfa/src/bits/locks.hfa

    rfeacef9 r5407cdc  
    3737        extern "C" {
    3838                extern void disable_interrupts() OPTIONAL_THREAD;
    39                 extern void enable_interrupts_noPoll() OPTIONAL_THREAD;
     39                extern void enable_interrupts( bool poll = true ) OPTIONAL_THREAD;
    4040
    4141                #ifdef __CFA_DEBUG__
     
    5757                        __cfaabi_dbg_record_lock( this, caller );
    5858                } else {
    59                         enable_interrupts_noPoll();
     59                        enable_interrupts( false );
    6060                }
    6161                return result;
     
    9090        static inline void unlock( __spinlock_t & this ) {
    9191                __atomic_clear( &this.lock, __ATOMIC_RELEASE );
    92                 enable_interrupts_noPoll();
     92                enable_interrupts( false );
    9393        }
    9494#endif
  • libcfa/src/bits/queue.hfa

    rfeacef9 r5407cdc  
    1515        };
    1616
    17         inline {
     17        static inline {
    1818                // wrappers to make Collection have T
    1919                T & head( Queue(T) & q ) with( q ) {
     
    154154        struct QueueIter {
    155155                inline ColIter;                                                                 // Plan 9 inheritance
    156         };     
     156        };
    157157
    158         inline {
     158        static inline {
    159159                void ?{}( QueueIter(T) & qi ) with( qi ) {
    160160                        ((ColIter &)qi){};
  • libcfa/src/bits/weakso_locks.cfa

    rfeacef9 r5407cdc  
    1818#include "bits/weakso_locks.hfa"
    1919
    20 void  ?{}( blocking_lock & this, bool multi_acquisition, bool strict_owner ) {}
    21 void ^?{}( blocking_lock & this ) {}
     20void  ?{}( blocking_lock &, bool, bool ) {}
     21void ^?{}( blocking_lock & ) {}
    2222
    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; }
     23void lock( blocking_lock & ) {}
     24bool try_lock( blocking_lock & ) { return false; }
     25void unlock( blocking_lock & ) {}
     26void on_notify( blocking_lock &, struct $thread * ) {}
     27size_t on_wait( blocking_lock & ) { return 0; }
     28void on_wakeup( blocking_lock &, size_t ) {}
     29size_t wait_count( blocking_lock & ) { return 0; }
  • libcfa/src/bits/weakso_locks.hfa

    rfeacef9 r5407cdc  
    5656void unlock( blocking_lock & this ) OPTIONAL_THREAD;
    5757void on_notify( blocking_lock & this, struct $thread * t ) OPTIONAL_THREAD;
    58 void on_wait( blocking_lock & this ) OPTIONAL_THREAD;
     58size_t on_wait( blocking_lock & this ) OPTIONAL_THREAD;
     59void on_wakeup( blocking_lock & this, size_t ) OPTIONAL_THREAD;
    5960size_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;
    6261
    6362//----------
     
    6968static inline void  ?{}( multiple_acquisition_lock & this ) {((blocking_lock &)this){ true, false };}
    7069static 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 ); }
     70static inline void   lock     ( multiple_acquisition_lock & this ) { lock    ( (blocking_lock &)this ); }
     71static inline bool   try_lock ( multiple_acquisition_lock & this ) { return try_lock( (blocking_lock &)this ); }
     72static inline void   unlock   ( multiple_acquisition_lock & this ) { unlock  ( (blocking_lock &)this ); }
     73static inline size_t on_wait  ( multiple_acquisition_lock & this ) { return on_wait ( (blocking_lock &)this ); }
     74static inline void   on_wakeup( multiple_acquisition_lock & this, size_t v ) { on_wakeup ( (blocking_lock &)this, v ); }
    7475static 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  
    1010// Created On       : Thu Apr 12 14:36:06 2018
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Mon Jan  6 12:49:58 2020
    13 // Update Count     : 9
     12// Last Modified On : Sun Apr 18 08:12:16 2021
     13// Update Count     : 28
    1414//
    1515
     
    2727//######################### Clock #########################
    2828
    29 struct Clock {                                                                                  // private
    30         Duration offset;                                                                        // for virtual clock: contains offset from real-time
     29struct Clock {                                                                                  // virtual clock
     30        // private
     31        Duration offset;                                                                        // offset from computer real-time
    3132};
    3233
    3334static inline {
    34         void resetClock( Clock & clk, Duration adj ) with( clk ) {
     35        void reset( Clock & clk, Duration adj ) with( clk ) { // change offset
    3536                offset = adj + __timezone`s;                                    // timezone (global) is (UTC - local time) in seconds
    36         } // resetClock
     37        } // reset
    3738
    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
    3941
    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)
    4146                struct timespec res;
    4247                clock_getres( CLOCK_REALTIME, &res );
    4348                return ((int64_t)res.tv_sec * TIMEGRAN + res.tv_nsec)`ns;
    44         } // getRes
     49        } // resolutionHi
    4550
    46         Duration getRes() {
     51        Duration resolution() {                                                         // clock resolution without nanoseconds (coarse)
    4752                struct timespec res;
    4853                clock_getres( CLOCK_REALTIME_COARSE, &res );
    4954                return ((int64_t)res.tv_sec * TIMEGRAN + res.tv_nsec)`ns;
    50         } // getRes
     55        } // resolution
    5156
    52         Time getTimeNsec() {                                                            // with nanoseconds
     57        Time timeHiRes() {                                                                      // real time with nanoseconds
    5358                timespec curr;
    5459                clock_gettime( CLOCK_REALTIME, &curr );
    5560                return (Time){ curr };
    56         } // getTimeNsec
     61        } // timeHiRes
    5762
    58         Time getTime() {                                                                        // without nanoseconds
     63        Time time() {                                                                           // real time without nanoseconds
    5964                timespec curr;
    6065                clock_gettime( CLOCK_REALTIME_COARSE, &curr );
    6166                curr.tv_nsec = 0;
    6267                return (Time){ curr };
    63         } // getTime
     68        } // time
    6469
    65         Time getTime( Clock & clk ) with( clk ) {
    66                 return getTime() + offset;
    67         } // getTime
     70        Time time( Clock & clk ) with( clk ) {                          // real time for given clock
     71                return time() + offset;
     72        } // time
    6873
    6974        Time ?()( Clock & clk ) with( clk ) {                           // alternative syntax
    70                 return getTime() + offset;
    71         } // getTime
     75                return time() + offset;
     76        } // ?()
    7277
    73         timeval getTime( Clock & clk ) {
     78        timeval time( Clock & clk ) {                                           // convert to C time format
    7479                return (timeval){ clk() };
    75         } // getTime
     80        } // time
    7681
    77         tm getTime( Clock & clk ) with( clk ) {
     82        tm time( Clock & clk ) with( clk ) {
    7883                tm ret;
    79                 localtime_r( getTime( clk ).tv_sec, &ret );
     84                localtime_r( time( clk ).tv_sec, &ret );
    8085                return ret;
    81         } // getTime
     86        } // time
    8287
    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
    8492                timespec ts;
    8593                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
    88115} // distribution
    89116
  • libcfa/src/concurrency/alarm.cfa

    rfeacef9 r5407cdc  
    1515
    1616#define __cforall_thread__
     17// #define __CFA_DEBUG_PRINT_PREEMPTION__
    1718
    1819#include <errno.h>
     
    107108                bool first = ! & alarms`first;
    108109
     110                __cfadbg_print_safe( preemption, " KERNEL: alarm inserting %p (%lu).\n", this, this->alarm.tn );
    109111                insert( &alarms, this );
    110112                if( first ) {
     
    114116        unlock( event_kernel->lock );
    115117        this->set = true;
    116         enable_interrupts( __cfaabi_dbg_ctx );
     118        enable_interrupts();
    117119}
    118120
     
    125127        }
    126128        unlock( event_kernel->lock );
    127         enable_interrupts( __cfaabi_dbg_ctx );
     129        enable_interrupts();
    128130        this->set = false;
    129131}
  • libcfa/src/concurrency/clib/cfathread.cfa

    rfeacef9 r5407cdc  
    1414//
    1515
     16#include "fstream.hfa"
     17#include "locks.hfa"
    1618#include "kernel.hfa"
     19#include "stats.hfa"
    1720#include "thread.hfa"
    18 
    19 thread CRunner {
    20         void (*themain)( CRunner * );
     21#include "time.hfa"
     22
     23#include "cfathread.h"
     24
     25extern void ?{}(processor &, const char[], cluster &, $thread *);
     26extern "C" {
     27      extern void __cfactx_invoke_thread(void (*main)(void *), void * this);
     28}
     29
     30//================================================================================
     31// Thread run y the C Interface
     32
     33struct cfathread_object {
     34        $thread self;
     35        void * (*themain)( void * );
     36        void * arg;
     37        void * ret;
    2138};
    22 
    23 static void ?{}( CRunner & this, void (*themain)( CRunner * ) ) {
     39void main(cfathread_object & this);
     40void ^?{}(cfathread_object & mutex this);
     41
     42static inline $thread * get_thread( cfathread_object & this ) { return &this.self; }
     43
     44typedef ThreadCancelled(cfathread_object) cfathread_exception;
     45typedef ThreadCancelled_vtable(cfathread_object) cfathread_vtable;
     46
     47void defaultResumptionHandler(ThreadCancelled(cfathread_object) & except) {
     48        abort | "A thread was cancelled";
     49}
     50
     51cfathread_vtable _cfathread_vtable_instance;
     52
     53cfathread_vtable & const _default_vtable = _cfathread_vtable_instance;
     54
     55cfathread_vtable const & get_exception_vtable(cfathread_exception *) {
     56        return _cfathread_vtable_instance;
     57}
     58
     59static void ?{}( cfathread_object & this, cluster & cl, void *(*themain)( void * ), void * arg ) {
    2460        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
     66void ^?{}(cfathread_object & mutex this) {
     67        ^(this.self){};
     68}
     69
     70void 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
     80struct __cfainit {
     81        $thread self;
     82        void (*init)( void * );
     83        void * arg;
     84};
     85void main(__cfainit & this);
     86void ^?{}(__cfainit & mutex this);
     87
     88static inline $thread * get_thread( __cfainit & this ) { return &this.self; }
     89
     90typedef ThreadCancelled(__cfainit) __cfainit_exception;
     91typedef ThreadCancelled_vtable(__cfainit) __cfainit_vtable;
     92
     93void 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
     103static 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
     122void ^?{}(__cfainit & mutex this) {
     123        ^(this.self){};
     124}
     125
     126void 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
    34136extern "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;
    43226        }
    44227
     
    47230        }
    48231
    49         void cfathread_unpark( CRunner * thrd ) {
     232        void cfathread_unpark( cfathread_t thrd ) {
    50233                unpark( *thrd );
    51234        }
     
    55238        }
    56239
    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
     277extern "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  
    1414//
    1515
    16 #include "stddef.h"
    17 #include "invoke.h"
    18 
    1916#if defined(__cforall) || defined(__cplusplus)
    2017extern "C" {
    2118#endif
     19        #include <asm/types.h>
     20        #include <errno.h>
     21        #include <unistd.h>
     22
     23
    2224        //--------------------
    2325        // 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);
    2635
    2736        //--------------------
    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);
    3163
    3264        void cfathread_park( void );
     
    3567
    3668        //--------------------
    37         // Basic kernel features
    38         void cfathread_setproccnt( int );
     69        // mutex and condition
     70        struct timespec;
    3971
     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);
    40104
    41105#if defined(__cforall) || defined(__cplusplus)
  • libcfa/src/concurrency/coroutine.cfa

    rfeacef9 r5407cdc  
    4646
    4747//-----------------------------------------------------------------------------
    48 FORALL_DATA_INSTANCE(CoroutineCancelled, (coroutine_t &), (coroutine_t))
    49 
    50 forall(T &)
    51 void mark_exception(CoroutineCancelled(T) *) {}
    52 
    5348forall(T &)
    5449void copy(CoroutineCancelled(T) * dst, CoroutineCancelled(T) * src) {
     
    6560// This code should not be inlined. It is the error path on resume.
    6661forall(T & | is_coroutine(T))
    67 void __cfaehm_cancelled_coroutine( T & cor, $coroutine * desc ) {
     62void __cfaehm_cancelled_coroutine(
     63                T & cor, $coroutine * desc, EHM_DEFAULT_VTABLE(CoroutineCancelled, (T)) ) {
    6864        verify( desc->cancellation );
    6965        desc->state = Cancelled;
     
    7268        // TODO: Remove explitate vtable set once trac#186 is fixed.
    7369        CoroutineCancelled(T) except;
    74         except.virtual_table = &get_exception_vtable(&except);
     70        except.virtual_table = &_default_vtable;
    7571        except.the_coroutine = &cor;
    7672        except.the_exception = except;
    77         throwResume except;
     73        // Why does this need a cast?
     74        throwResume (CoroutineCancelled(T) &)except;
    7875
    7976        except->virtual_table->free( except );
     
    148145// Part of the Public API
    149146// Not inline since only ever called once per coroutine
    150 forall(T & | is_coroutine(T))
     147forall(T & | is_coroutine(T) | { EHM_DEFAULT_VTABLE(CoroutineCancelled, (T)); })
    151148void prime(T& cor) {
    152149        $coroutine* this = get_coroutine(cor);
     
    196193
    197194void __stack_clean  ( __stack_info_t * this ) {
    198         size_t size = ((intptr_t)this->storage->base) - ((intptr_t)this->storage->limit) + sizeof(__stack_t);
    199195        void * storage = this->storage->limit;
    200196
    201197        #if CFA_COROUTINE_USE_MMAP
     198                size_t size = ((intptr_t)this->storage->base) - ((intptr_t)this->storage->limit) + sizeof(__stack_t);
    202199                storage = (void *)(((intptr_t)storage) - __page_size);
    203200                if(munmap(storage, size + __page_size) == -1) {
  • libcfa/src/concurrency/coroutine.hfa

    rfeacef9 r5407cdc  
    2222//-----------------------------------------------------------------------------
    2323// Exception thrown from resume when a coroutine stack is cancelled.
    24 FORALL_DATA_EXCEPTION(CoroutineCancelled, (coroutine_t &), (coroutine_t)) (
     24EHM_FORALL_EXCEPTION(CoroutineCancelled, (coroutine_t &), (coroutine_t)) (
    2525        coroutine_t * the_coroutine;
    2626        exception_t * the_exception;
     
    6060//-----------------------------------------------------------------------------
    6161// Public coroutine API
    62 forall(T & | is_coroutine(T))
     62forall(T & | is_coroutine(T) | { EHM_DEFAULT_VTABLE(CoroutineCancelled, (T)); })
    6363void prime(T & cor);
    6464
     
    130130
    131131forall(T & | is_coroutine(T))
    132 void __cfaehm_cancelled_coroutine( T & cor, $coroutine * desc );
     132void __cfaehm_cancelled_coroutine(
     133        T & cor, $coroutine * desc, EHM_DEFAULT_VTABLE(CoroutineCancelled, (T)) );
    133134
    134135// Resume implementation inlined for performance
    135 forall(T & | is_coroutine(T))
     136forall(T & | is_coroutine(T) | { EHM_DEFAULT_VTABLE(CoroutineCancelled, (T)); })
    136137static inline T & resume(T & cor) {
    137138        // optimization : read TLS once and reuse it
     
    163164        $ctx_switch( src, dst );
    164165        if ( unlikely(dst->cancellation) ) {
    165                 __cfaehm_cancelled_coroutine( cor, dst );
     166                __cfaehm_cancelled_coroutine( cor, dst, _default_vtable );
    166167        }
    167168
  • libcfa/src/concurrency/future.hfa

    rfeacef9 r5407cdc  
    3737
    3838                // Fulfil the future, returns whether or not someone was unblocked
    39                 bool fulfil( future(T) & this, T result ) {
     39                $thread * fulfil( future(T) & this, T result ) {
    4040                        this.result = result;
    4141                        return fulfil( (future_t&)this );
     
    9696                bool fulfil( multi_future(T) & this, T result ) {
    9797                        this.result = result;
    98                         return fulfil( (future_t&)this );
     98                        return fulfil( (future_t&)this ) != 0p;
    9999                }
    100100
  • libcfa/src/concurrency/invoke.c

    rfeacef9 r5407cdc  
    3434
    3535extern void disable_interrupts() OPTIONAL_THREAD;
    36 extern void enable_interrupts( __cfaabi_dbg_ctx_param );
     36extern void enable_interrupts( _Bool poll );
    3737
    3838void __cfactx_invoke_coroutine(
     
    8282) {
    8383        // Officially start the thread by enabling preemption
    84         enable_interrupts( __cfaabi_dbg_ctx );
     84        enable_interrupts( true );
    8585
    8686        // Call the main of the thread
  • libcfa/src/concurrency/invoke.h

    rfeacef9 r5407cdc  
    148148                struct $thread * prev;
    149149                volatile unsigned long long ts;
    150                 int preferred;
     150                unsigned preferred;
    151151        };
    152152
     
    200200                } node;
    201201
     202                struct processor * last_proc;
     203
    202204                #if defined( __CFA_WITH_VERIFY__ )
    203205                        void * canary;
     
    224226                }
    225227
     228                static inline $thread * volatile & ?`next ( $thread * this )  __attribute__((const)) {
     229                        return this->seqable.next;
     230                }
     231
    226232                static inline $thread *& Back( $thread * this ) __attribute__((const)) {
    227233                        return this->seqable.back;
  • libcfa/src/concurrency/io.cfa

    rfeacef9 r5407cdc  
    3232        extern "C" {
    3333                #include <sys/syscall.h>
     34                #include <sys/eventfd.h>
    3435
    3536                #include <linux/io_uring.h>
     
    3940        #include "kernel.hfa"
    4041        #include "kernel/fwd.hfa"
     42        #include "kernel_private.hfa"
    4143        #include "io/types.hfa"
    4244
     
    7981        };
    8082
    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 ) {
    11595                /* 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 );
    24099
    241100                // 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;
    250105
    251106                __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
    253111                for(i; count) {
    254112                        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];
    256114
    257115                        /* paranoid */ verify(&cqe);
    258116
    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);
    261124
    262125                // Mark to the kernel that the cqe has been seen
    263126                // 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:
    287151                                // 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) );
    298156                        }
    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;
    325174        }
    326175
     
    344193//         head and tail must be fully filled and shouldn't ever be touched again.
    345194//
     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        }
    346229
    347230        // Allocate an submit queue entry.
     
    350233        // for convenience, return both the index and the pointer to the sqe
    351234        // 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?
    446315                {
    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
    705337        // Go through the ring's submit queue and release everything that has already been consumed
    706338        // 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
    712343                __attribute__((unused))
    713                 __u32 ctail = *ring.submit_q.tail;        // get the current tail of the queue
    714                 __u32 chead = *ring.submit_q.head;              // get the current head of the queue
    715                 __u32 phead = ring.submit_q.prev_head;  // get the head the last time we were here
    716                 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
    718349
    719350                // the 3 fields are organized like this diagram
     
    734365                __u32 count = chead - phead;
    735366
     367                if(count == 0) {
     368                        return 0;
     369                }
     370
    736371                // We acquired an previous-head/current-head range
    737372                // go through the range and release the sqes
    738373                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
    744384                return count;
    745385        }
    746386
    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                }
    760498        }
    761499#endif
  • libcfa/src/concurrency/io/call.cfa.in

    rfeacef9 r5407cdc  
    5454                        | IOSQE_IO_DRAIN
    5555                #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
    5662                #if defined(CFA_HAVE_IOSQE_ASYNC)
    5763                        | IOSQE_ASYNC
    5864                #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
    6767                #endif
    6868        ;
     
    7474        ;
    7575
    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)));
    8878#endif
    8979
     
    9888
    9989extern "C" {
    100         #include <sys/types.h>
     90        #include <asm/types.h>
    10191        #include <sys/socket.h>
    10292        #include <sys/syscall.h>
     
    142132        extern int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    143133
    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);
    145135        extern ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags);
    146136}
     
    195185                return ', '.join(args_a)
    196186
    197 AsyncTemplate = """inline void async_{name}(io_future_t & future, {params}, int submit_flags, io_cancellation * cancellation, io_context * context) {{
     187AsyncTemplate = """inline void async_{name}(io_future_t & future, {params}, __u64 submit_flags) {{
    198188        #if !defined(CFA_HAVE_LINUX_IO_URING_H) || !defined(CFA_HAVE_IORING_OP_{op})
    199189                ssize_t res = {name}({args});
     
    205195                }}
    206196        #else
    207                 // we don't support LINK yet
    208                 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 
    219197                __u8 sflags = REGULAR_FLAGS & submit_flags;
    220                 struct __io_data & ring = *context->thrd.ring;
    221 
    222198                __u32 idx;
    223199                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 );
    225201
    226202                sqe->opcode = IORING_OP_{op};
     203                sqe->user_data = (uintptr_t)&future;
    227204                sqe->flags = sflags;
    228205                sqe->ioprio = 0;
     
    238215                asm volatile("": : :"memory");
    239216
    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) );
    242219        #endif
    243220}}"""
    244221
    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         }}
     222SyncTemplate = """{ret} cfa_{name}({params}, __u64 submit_flags) {{
    250223        io_future_t future;
    251224
    252         async_{name}( future, {args}, submit_flags, cancellation, context );
     225        async_{name}( future, {args}, submit_flags );
    253226
    254227        wait( future );
     
    265238                'fd'  : 'fd',
    266239                'off' : 'offset',
    267                 'addr': '(__u64)iov',
     240                'addr': '(uintptr_t)iov',
    268241                'len' : 'iovcnt',
    269242        }, define = 'CFA_HAVE_PREADV2'),
     
    272245                'fd'  : 'fd',
    273246                'off' : 'offset',
    274                 'addr': '(__u64)iov',
     247                'addr': '(uintptr_t)iov',
    275248                'len' : 'iovcnt'
    276249        }, define = 'CFA_HAVE_PWRITEV2'),
     
    284257                'addr': 'fd',
    285258                'len': 'op',
    286                 'off': '(__u64)event'
     259                'off': '(uintptr_t)event'
    287260        }),
    288261        # CFA_HAVE_IORING_OP_SYNC_FILE_RANGE
     
    296269        Call('SENDMSG', 'ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)', {
    297270                'fd': 'sockfd',
    298                 'addr': '(__u64)(struct msghdr *)msg',
     271                'addr': '(uintptr_t)(struct msghdr *)msg',
    299272                'len': '1',
    300273                'msg_flags': 'flags'
     
    303276        Call('RECVMSG', 'ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags)', {
    304277                'fd': 'sockfd',
    305                 'addr': '(__u64)(struct msghdr *)msg',
     278                'addr': '(uintptr_t)(struct msghdr *)msg',
    306279                'len': '1',
    307280                'msg_flags': 'flags'
     
    310283        Call('SEND', 'ssize_t send(int sockfd, const void *buf, size_t len, int flags)', {
    311284                'fd': 'sockfd',
    312                 'addr': '(__u64)buf',
     285                'addr': '(uintptr_t)buf',
    313286                'len': 'len',
    314287                'msg_flags': 'flags'
     
    317290        Call('RECV', 'ssize_t recv(int sockfd, void *buf, size_t len, int flags)', {
    318291                'fd': 'sockfd',
    319                 'addr': '(__u64)buf',
     292                'addr': '(uintptr_t)buf',
    320293                'len': 'len',
    321294                'msg_flags': 'flags'
     
    324297        Call('ACCEPT', 'int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags)', {
    325298                'fd': 'sockfd',
    326                 'addr': '(__u64)addr',
    327                 'addr2': '(__u64)addrlen',
     299                'addr': '(uintptr_t)addr',
     300                'addr2': '(uintptr_t)addrlen',
    328301                'accept_flags': 'flags'
    329302        }),
     
    331304        Call('CONNECT', 'int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)', {
    332305                'fd': 'sockfd',
    333                 'addr': '(__u64)addr',
     306                'addr': '(uintptr_t)addr',
    334307                'off': 'addrlen'
    335308        }),
     
    337310        Call('FALLOCATE', 'int fallocate(int fd, int mode, off_t offset, off_t len)', {
    338311                'fd': 'fd',
    339                 'addr': '(__u64)len',
     312                'addr': '(uintptr_t)len',
    340313                'len': 'mode',
    341314                'off': 'offset'
     
    350323        # CFA_HAVE_IORING_OP_MADVISE
    351324        Call('MADVISE', 'int madvise(void *addr, size_t length, int advice)', {
    352                 'addr': '(__u64)addr',
     325                'addr': '(uintptr_t)addr',
    353326                'len': 'length',
    354327                'fadvise_advice': 'advice'
     
    357330        Call('OPENAT', 'int openat(int dirfd, const char *pathname, int flags, mode_t mode)', {
    358331                'fd': 'dirfd',
    359                 'addr': '(__u64)pathname',
     332                'addr': '(uintptr_t)pathname',
    360333                'len': 'mode',
    361334                'open_flags': 'flags;'
     
    366339                'addr': 'pathname',
    367340                'len': 'sizeof(*how)',
    368                 'off': '(__u64)how',
     341                'off': '(uintptr_t)how',
    369342        }, define = 'CFA_HAVE_OPENAT2'),
    370343        # CFA_HAVE_IORING_OP_CLOSE
     
    375348        Call('STATX', 'int statx(int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf)', {
    376349                'fd': 'dirfd',
    377                 'off': '(__u64)statxbuf',
     350                'off': '(uintptr_t)statxbuf',
    378351                'addr': 'pathname',
    379352                'len': 'mask',
     
    383356        Call('READ', 'ssize_t read(int fd, void * buf, size_t count)', {
    384357                'fd': 'fd',
    385                 'addr': '(__u64)buf',
     358                'addr': '(uintptr_t)buf',
    386359                'len': 'count'
    387360        }),
     
    389362        Call('WRITE', 'ssize_t write(int fd, void * buf, size_t count)', {
    390363                'fd': 'fd',
    391                 'addr': '(__u64)buf',
     364                'addr': '(uintptr_t)buf',
    392365                'len': 'count'
    393366        }),
    394367        # 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)', {
    396369                'splice_fd_in': 'fd_in',
    397370                'splice_off_in': 'off_in ? (__u64)*off_in : (__u64)-1',
     
    415388        if c.define:
    416389                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);
    418391#endif""".format(define=c.define,ret=c.ret, name=c.name, params=c.params))
    419392        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);"
    421394                .format(ret=c.ret, name=c.name, params=c.params))
    422395
     
    426399        if c.define:
    427400                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);
    429402#endif""".format(define=c.define,name=c.name, params=c.params))
    430403        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);"
    432405                .format(name=c.name, params=c.params))
    433406print("\n")
     
    474447
    475448print("""
    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         #else
    481                 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 found
    503                 if( future.result == -EALREADY) return true; // Entry found but in progress
    504                 if( future.result == -ENOENT ) return false; // Entry not found
    505                 return false;
    506         #endif
    507 }
    508 
    509449//-----------------------------------------------------------------------------
    510450// Check if a function is has asynchronous
  • libcfa/src/concurrency/io/setup.cfa

    rfeacef9 r5407cdc  
    2626
    2727#if !defined(CFA_HAVE_LINUX_IO_URING_H)
    28         void __kernel_io_startup() {
    29                 // Nothing to do without io_uring
    30         }
    31 
    32         void __kernel_io_shutdown() {
    33                 // Nothing to do without io_uring
    34         }
    35 
    3628        void ?{}(io_context_params & this) {}
    3729
    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 *) {}
    4639
    4740#else
     
    6861        void ?{}(io_context_params & this) {
    6962                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;
    7663        }
    7764
     
    10693
    10794//=============================================================================================
    108 // I/O Startup / Shutdown logic + Master Poller
    109 //=============================================================================================
    110 
    111         // IO Master poller loop forward
    112         static void * iopoll_loop( __attribute__((unused)) void * args );
    113 
    114         static struct {
    115                       pthread_t  thrd;    // pthread handle to io poller thread
    116                       void *     stack;   // pthread stack for io poller thread
    117                       int        epollfd; // file descriptor to the epoll instance
    118                 volatile     bool run;     // Whether or not to continue
    119                 volatile     bool stopped; // Whether the poller has finished running
    120                 volatile uint64_t epoch;   // Epoch used for memory reclamation
    121         } 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 shutdown
    141                 iopoll.run = false;
    142                 sigval val = { 1 };
    143                 pthread_sigqueue( iopoll.thrd, SIGUSR1, val );
    144 
    145                 // Wait for the io poller thread to finish
    146 
    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 stopped
    155 
    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 arrive
    167                 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 events
    176                 struct epoll_event events[10];
    177                 // Main loop
    178                 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 cycle
    182                         __atomic_fetch_add(&iopoll.epoch, 1, __ATOMIC_SEQ_CST);
    183 
    184                         // Wait for events
    185                         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 occured
    190                         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                                 #endif
    202 
    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 //=============================================================================================
    21895// I/O Context Constrution/Destruction
    21996//=============================================================================================
    22097
    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);
    326122        }
    327123
     
    329125        extern void __enable_interrupts_hard();
    330126
    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 ) {
    332128                // Step 1 : call to setup
    333129                struct io_uring_params params;
    334130                memset(&params, 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;
    337133
    338134                __u32 nentries = params_in.num_entries != 0 ? params_in.num_entries : 256;
     
    340136                        abort("ERROR: I/O setup 'num_entries' must be a power of 2\n");
    341137                }
    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                 }
    345138
    346139                int fd = syscall(__NR_io_uring_setup, nentries, &params );
     
    350143
    351144                // 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;
    355147
    356148                // calculate the right ring size
     
    401193                // Get the pointers from the kernel to fill the structure
    402194                // 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;
    436213
    437214                // completion queue
     
    446223                // io_uring_register is so f*cking slow on some machine that it
    447224                // 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
    448227                __disable_interrupts_hard();
    449228
    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);
    456230                if (ret < 0) {
    457231                        abort("KERNEL ERROR: IO_URING EVENTFD REGISTER - %s\n", strerror(errno));
     
    459233
    460234                __enable_interrupts_hard();
     235
     236                __cfadbg_print_safe(io_core, "Kernel I/O : registered %d for completion with ring %d\n", procfd, fd);
    461237
    462238                // some paranoid checks
     
    468244                /* 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 );
    469245                /* 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 );
    472248
    473249                // Update the global ring info
    474                 this.ring_flags = params.flags;
     250                this.ring_flags = 0;
    475251                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 ) {
    482255                // Shutdown the io rings
    483                 struct __submition_data  & sq = this.submit_q;
    484                 struct __completion_data & cq = this.completion_q;
     256                struct __sub_ring_t & sq = this.sq;
     257                struct __cmp_ring_t & cq = this.cq;
    485258
    486259                // unmap the submit queue entries
     
    497270                // close the file descriptor
    498271                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);
    502283        }
    503284
     
    505286// I/O Context Sleep
    506287//=============================================================================================
    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
    544326
    545327//=============================================================================================
    546328// I/O Context Misc Setup
    547329//=============================================================================================
    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
    562347#endif
  • libcfa/src/concurrency/io/types.hfa

    rfeacef9 r5407cdc  
    2222
    2323#include "bits/locks.hfa"
     24#include "bits/queue.hfa"
    2425#include "kernel/fwd.hfa"
    2526
    2627#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"
    3130
    32         static inline void ?{}( __leaderlock_t & this ) { this.value = 0p; }
     31        struct processor;
     32        monitor $io_arbiter;
    3333
    3434        //-----------------------------------------------------------------------
    3535        // 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
    4142
    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;
    4558
    4659                // number of entries and mask to go with it
     
    4861                const __u32 * mask;
    4962
    50                 // Submission flags (Not sure what for)
     63                // Submission flags, currently only IORING_SETUP_SQPOLL
    5164                __u32 * flags;
    5265
    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.
    5468                __u32 * dropped;
    5569
    56                 // Like head/tail but not seen by the kernel
    57                 volatile __u32 * ready;
    58                 __u32 ready_cnt;
    59                 __u32 prev_ready;
    60 
    61                 #if defined(LEADER_LOCK)
    62                         __leaderlock_t submit_lock;
    63                 #else
    64                         __spinlock_t submit_lock;
    65                 #endif
    66                 __spinlock_t  release_lock;
    67 
    6870                // A buffer of sqes (not the actual ring)
    69                 volatile struct io_uring_sqe * sqes;
     71                struct io_uring_sqe * sqes;
    7072
    7173                // The location and size of the mmaped area
     
    7476        };
    7577
    76         struct __completion_data {
     78        struct __cmp_ring_t {
    7779                // Head and tail of the ring
    7880                volatile __u32 * head;
     
    8385                const __u32 * num;
    8486
    85                 // number of cqes not submitted (whatever that means)
     87                // I don't know what this value is for
    8688                __u32 * overflow;
    8789
     
    9496        };
    9597
    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;
    99126                __u32 ring_flags;
    100127                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;
    104139        };
    105140
     
    133168        #endif
    134169
    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);
    140171#endif
    141172
     
    148179
    149180static inline {
    150         bool fulfil( io_future_t & this, __s32 result ) {
     181        $thread * fulfil( io_future_t & this, __s32 result, bool do_unpark = true ) {
    151182                this.result = result;
    152                 return fulfil(this.self);
     183                return fulfil(this.self, do_unpark);
    153184        }
    154185
  • libcfa/src/concurrency/iofwd.hfa

    rfeacef9 r5407cdc  
    1818#include <unistd.h>
    1919extern "C" {
    20         #include <sys/types.h>
     20        #include <asm/types.h>
    2121        #if CFA_HAVE_LINUX_IO_URING_H
    2222                #include <linux/io_uring.h>
     
    4848struct cluster;
    4949struct io_future_t;
    50 struct io_context;
    51 struct io_cancellation;
     50struct $io_context;
    5251
    5352struct iovec;
     
    5554struct sockaddr;
    5655struct statx;
     56struct epoll_event;
     57
     58struct io_uring_sqe;
     59
     60//----------
     61// underlying calls
     62extern struct $io_context * cfa_io_allocate(struct io_uring_sqe * out_sqes[], __u32 out_idxs[], __u32 want)  __attribute__((nonnull (1,2)));
     63extern void cfa_io_submit( struct $io_context * in_ctx, __u32 in_idxs[], __u32 have, bool lazy ) __attribute__((nonnull (1,2)));
    5764
    5865//----------
    5966// synchronous calls
    6067#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);
    6269#endif
    6370#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);
    6572#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);
     73extern int cfa_fsync(int fd, __u64 submit_flags);
     74extern int cfa_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event, __u64 submit_flags);
     75extern int cfa_sync_file_range(int fd, off64_t offset, off64_t nbytes, unsigned int flags, __u64 submit_flags);
     76extern  ssize_t cfa_sendmsg(int sockfd, const struct msghdr *msg, int flags, __u64 submit_flags);
     77extern ssize_t cfa_recvmsg(int sockfd, struct msghdr *msg, int flags, __u64 submit_flags);
     78extern ssize_t cfa_send(int sockfd, const void *buf, size_t len, int flags, __u64 submit_flags);
     79extern ssize_t cfa_recv(int sockfd, void *buf, size_t len, int flags, __u64 submit_flags);
     80extern int cfa_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags, __u64 submit_flags);
     81extern int cfa_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen, __u64 submit_flags);
     82extern int cfa_fallocate(int fd, int mode, off_t offset, off_t len, __u64 submit_flags);
     83extern int cfa_posix_fadvise(int fd, off_t offset, off_t len, int advice, __u64 submit_flags);
     84extern int cfa_madvise(void *addr, size_t length, int advice, __u64 submit_flags);
     85extern int cfa_openat(int dirfd, const char *pathname, int flags, mode_t mode, __u64 submit_flags);
    7986#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);
    8188#endif
    82 extern int cfa_close(int fd, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);
     89extern int cfa_close(int fd, __u64 submit_flags);
    8390#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);
    8592#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);
     93extern ssize_t cfa_read(int fd, void * buf, size_t count, __u64 submit_flags);
     94extern ssize_t cfa_write(int fd, void * buf, size_t count, __u64 submit_flags);
     95extern 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);
     96extern ssize_t cfa_tee(int fd_in, int fd_out, size_t len, unsigned int flags, __u64 submit_flags);
    9097
    9198//----------
    9299// asynchronous calls
    93100#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);
    95102#endif
    96103#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);
    98105#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);
     106extern void async_fsync(io_future_t & future, int fd, __u64 submit_flags);
     107extern void async_epoll_ctl(io_future_t & future, int epfd, int op, int fd, struct epoll_event *event, __u64 submit_flags);
     108extern void async_sync_file_range(io_future_t & future, int fd, off64_t offset, off64_t nbytes, unsigned int flags, __u64 submit_flags);
     109extern void async_sendmsg(io_future_t & future, int sockfd, const struct msghdr *msg, int flags, __u64 submit_flags);
     110extern void async_recvmsg(io_future_t & future, int sockfd, struct msghdr *msg, int flags, __u64 submit_flags);
     111extern void async_send(io_future_t & future, int sockfd, const void *buf, size_t len, int flags, __u64 submit_flags);
     112extern void async_recv(io_future_t & future, int sockfd, void *buf, size_t len, int flags, __u64 submit_flags);
     113extern void async_accept4(io_future_t & future, int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags, __u64 submit_flags);
     114extern void async_connect(io_future_t & future, int sockfd, const struct sockaddr *addr, socklen_t addrlen, __u64 submit_flags);
     115extern void async_fallocate(io_future_t & future, int fd, int mode, off_t offset, off_t len, __u64 submit_flags);
     116extern void async_posix_fadvise(io_future_t & future, int fd, off_t offset, off_t len, int advice, __u64 submit_flags);
     117extern void async_madvise(io_future_t & future, void *addr, size_t length, int advice, __u64 submit_flags);
     118extern void async_openat(io_future_t & future, int dirfd, const char *pathname, int flags, mode_t mode, __u64 submit_flags);
    112119#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);
    114121#endif
    115 extern void async_close(io_future_t & future, int fd, int submit_flags, io_cancellation * cancellation, io_context * context);
     122extern void async_close(io_future_t & future, int fd, __u64 submit_flags);
    116123#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);
    118125#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);
     126void async_read(io_future_t & future, int fd, void * buf, size_t count, __u64 submit_flags);
     127extern void async_write(io_future_t & future, int fd, void * buf, size_t count, __u64 submit_flags);
     128extern 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);
     129extern void async_tee(io_future_t & future, int fd_in, int fd_out, size_t len, unsigned int flags, __u64 submit_flags);
    123130
    124131
     
    126133// Check if a function is blocks a only the user thread
    127134bool 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  
    2222#include <signal.h>
    2323#include <unistd.h>
     24extern "C" {
     25        #include <sys/eventfd.h>
     26}
    2427
    2528//CFA Includes
     
    3134#include "invoke.h"
    3235
     36#if !defined(__CFA_NO_STATISTICS__)
     37        #define __STATS( ...) __VA_ARGS__
     38#else
     39        #define __STATS( ...)
     40#endif
    3341
    3442//-----------------------------------------------------------------------------
     
    107115static $thread * __next_thread(cluster * this);
    108116static $thread * __next_thread_slow(cluster * this);
     117static inline bool __must_unpark( $thread * thrd ) __attribute((nonnull(1)));
    109118static void __run_thread(processor * this, $thread * dst);
    110119static void __wake_one(cluster * cltr);
    111120
    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 
     121static void mark_idle (__cluster_proc_list & idles, processor & proc);
     122static void mark_awake(__cluster_proc_list & idles, processor & proc);
     123static [unsigned idle, unsigned total, * processor] query_idles( & __cluster_proc_list idles );
     124
     125extern void __cfa_io_start( processor * );
     126extern bool __cfa_io_drain( processor * );
     127extern void __cfa_io_flush( processor * );
     128extern void __cfa_io_stop ( processor * );
     129static inline bool __maybe_io_drain( processor * );
     130
     131extern void __disable_interrupts_hard();
     132extern void __enable_interrupts_hard();
     133
     134static inline void __disable_interrupts_checked() {
     135        /* paranoid */ verify( __preemption_enabled() );
     136        disable_interrupts();
     137        /* paranoid */ verify( ! __preemption_enabled() );
     138}
     139
     140static inline void __enable_interrupts_checked( bool poll = true ) {
     141        /* paranoid */ verify( ! __preemption_enabled() );
     142        enable_interrupts( poll );
     143        /* paranoid */ verify( __preemption_enabled() );
     144}
    116145
    117146//=============================================================================================
     
    129158        verify(this);
    130159
     160        __cfa_io_start( this );
     161
    131162        __cfadbg_print_safe(runtime_core, "Kernel : core %p starting\n", this);
    132163        #if !defined(__CFA_NO_STATISTICS__)
     
    140171                preemption_scope scope = { this };
    141172
    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                }
    146180
    147181                __cfadbg_print_safe(runtime_core, "Kernel : core %p started\n", this);
     
    150184                MAIN_LOOP:
    151185                for() {
     186                        // Check if there is pending io
     187                        __maybe_io_drain( this );
     188
    152189                        // Try to get the next thread
    153190                        readyThread = __next_thread( this->cltr );
    154191
    155192                        if( !readyThread ) {
     193                                __cfa_io_flush( this );
    156194                                readyThread = __next_thread_slow( this->cltr );
    157195                        }
     
    167205
    168206                                // Push self to idle stack
    169                                 push(this->cltr->idles, * this);
     207                                mark_idle(this->cltr->procs, * this);
    170208
    171209                                // Confirm the ready-queue is empty
     
    173211                                if( readyThread ) {
    174212                                        // A thread was found, cancel the halt
    175                                         remove(this->cltr->idles, * this);
     213                                        mark_awake(this->cltr->procs, * this);
    176214
    177215                                        #if !defined(__CFA_NO_STATISTICS__)
     
    189227                                #endif
    190228
    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();
    192235
    193236                                #if !defined(__CFA_NO_STATISTICS__)
     
    198241
    199242                                // We were woken up, remove self from idle
    200                                 remove(this->cltr->idles, * this);
     243                                mark_awake(this->cltr->procs, * this);
    201244
    202245                                // DON'T just proceed, start looking again
     
    205248
    206249                        /* paranoid */ verify( readyThread );
     250
     251                        // Reset io dirty bit
     252                        this->io.dirty = false;
    207253
    208254                        // We found a thread run it
     
    219265                                }
    220266                        #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 );
    221384                }
    222385
     
    224387        }
    225388
     389        __cfa_io_stop( this );
     390
    226391        post( this->terminated );
     392
    227393
    228394        if(this == mainProcessor) {
     
    247413        /* paranoid */ verifyf( thrd_dst->link.next == 0p, "Expected null got %p", thrd_dst->link.next );
    248414        __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);
    249417
    250418        $coroutine * proc_cor = get_coroutine(this->runner);
     
    297465                if(unlikely(thrd_dst->preempted != __NO_PREEMPTION)) {
    298466                        // The thread was preempted, reschedule it and reset the flag
    299                         __schedule_thread( thrd_dst );
     467                        schedule_thread$( thrd_dst );
    300468                        break RUNNING;
    301469                }
     
    318486                                break RUNNING;
    319487                        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
    320492                                // This is case 2, the racy case, someone tried to run this thread before it finished blocking
    321493                                // In this case, just run it again.
     
    330502        proc_cor->state = Active;
    331503
     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
    332511        /* paranoid */ verify( ! __preemption_enabled() );
    333512}
     
    339518        $thread * thrd_src = kernelTLS().this_thread;
    340519
    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; )
    344521
    345522        // Run the thread on this processor
     
    360537
    361538        #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) {
    363541                        __tls_stats()->ready.threads.migration++;
    364542                }
     
    373551// Scheduler routines
    374552// KERNEL ONLY
    375 void __schedule_thread( $thread * thrd ) {
     553static void __schedule_thread( $thread * thrd ) {
    376554        /* paranoid */ verify( ! __preemption_enabled() );
    377555        /* paranoid */ verify( kernelTLS().this_proc_id );
     556        /* paranoid */ verify( ready_schedule_islocked());
    378557        /* paranoid */ verify( thrd );
    379558        /* paranoid */ verify( thrd->state != Halted );
     
    391570        if (thrd->preempted == __NO_PREEMPTION) thrd->state = Ready;
    392571
     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
     604void schedule_thread$( $thread * thrd ) {
    393605        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 );
    404607        ready_schedule_unlock();
    405 
    406         /* paranoid */ verify( ! __preemption_enabled() );
    407608}
    408609
     
    413614
    414615        ready_schedule_lock();
    415                 $thread * thrd = pop( this );
     616                $thread * thrd = pop_fast( this );
    416617        ready_schedule_unlock();
    417618
     
    427628
    428629        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:
    430638        ready_schedule_unlock();
    431639
     
    435643}
    436644
    437 void unpark( $thread * thrd ) {
    438         if( !thrd ) return;
    439 
     645static inline bool __must_unpark( $thread * thrd ) {
    440646        int old_ticket = __atomic_fetch_add(&thrd->ticket, 1, __ATOMIC_SEQ_CST);
    441647        switch(old_ticket) {
    442648                case TICKET_RUNNING:
    443649                        // Wake won the race, the thread will reschedule/rerun itself
    444                         break;
     650                        return false;
    445651                case TICKET_BLOCKED:
    446652                        /* paranoid */ verify( ! thrd->preempted != __NO_PREEMPTION );
    447653                        /* 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;
    466655                default:
    467656                        // This makes no sense, something is wrong abort
     
    470659}
    471660
     661void __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
     676void 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
    472687void 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();
    483692
    484693}
     
    520729// KERNEL ONLY
    521730bool 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 );
    544746        return preempted;
    545747}
     
    557759        unsigned idle;
    558760        unsigned total;
    559         [idle, total, p] = query(this->idles);
     761        [idle, total, p] = query_idles(this->procs);
    560762
    561763        // If no one is sleeping, we are done
     
    563765
    564766        // We found a processor, wake it up
    565         post( p->idle );
     767        eventfd_t val;
     768        val = 1;
     769        eventfd_write( p->idle, val );
    566770
    567771        #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                }
    569778        #endif
    570779
     
    579788        __cfadbg_print_safe(runtime_core, "Kernel : waking Processor %p\n", this);
    580789
    581         disable_interrupts();
     790        __disable_interrupts_checked();
    582791                /* 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
     798static void mark_idle(__cluster_proc_list & this, processor & proc) {
    588799        /* paranoid */ verify( ! __preemption_enabled() );
    589800        lock( this );
    590801                this.idle++;
    591802                /* paranoid */ verify( this.idle <= this.total );
    592 
    593                 insert_first(this.list, proc);
     803                remove(proc);
     804                insert_first(this.idles, proc);
    594805        unlock( this );
    595806        /* paranoid */ verify( ! __preemption_enabled() );
    596807}
    597808
    598 static void remove(__cluster_idles & this, processor & proc) {
     809static void mark_awake(__cluster_proc_list & this, processor & proc) {
    599810        /* paranoid */ verify( ! __preemption_enabled() );
    600811        lock( this );
    601812                this.idle--;
    602813                /* paranoid */ verify( this.idle >= 0 );
    603 
    604814                remove(proc);
     815                insert_last(this.actives, proc);
    605816        unlock( this );
    606817        /* paranoid */ verify( ! __preemption_enabled() );
    607818}
    608819
    609 static [unsigned idle, unsigned total, * processor] query( & __cluster_idles this ) {
     820static [unsigned idle, unsigned total, * processor] query_idles( & __cluster_proc_list this ) {
     821        /* paranoid */ verify( ! __preemption_enabled() );
     822        /* paranoid */ verify( ready_schedule_islocked() );
     823
    610824        for() {
    611825                uint64_t l = __atomic_load_n(&this.lock, __ATOMIC_SEQ_CST);
     
    613827                unsigned idle    = this.idle;
    614828                unsigned total   = this.total;
    615                 processor * proc = &this.list`first;
     829                processor * proc = &this.idles`first;
    616830                // Compiler fence is unnecessary, but gcc-8 and older incorrectly reorder code without it
    617831                asm volatile("": : :"memory");
     
    619833                return [idle, total, proc];
    620834        }
     835
     836        /* paranoid */ verify( ready_schedule_islocked() );
     837        /* paranoid */ verify( ! __preemption_enabled() );
    621838}
    622839
     
    664881// Kernel Utilities
    665882//=============================================================================================
     883#if defined(CFA_HAVE_LINUX_IO_URING_H)
     884#include "io/types.hfa"
     885#endif
     886
     887static 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
    666904//-----------------------------------------------------------------------------
    667905// Debug
     
    691929                __print_stats( this.stats, this.print_stats, "Cluster", this.name, (void*)&this );
    692930        }
    693 
    694         extern int __print_alarm_stats;
    695         void print_alarm_stats() {
    696                 __print_alarm_stats = -1;
    697         }
    698931#endif
    699932// Local Variables: //
  • libcfa/src/concurrency/kernel.hfa

    rfeacef9 r5407cdc  
    2828}
    2929
    30 //-----------------------------------------------------------------------------
    31 // Underlying Locks
    3230#ifdef __CFA_WITH_VERIFY__
    3331        extern bool __cfaabi_dbg_in_kernel();
    3432#endif
    3533
    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
     36struct cluster;
     37struct $io_context;
     38struct $io_arbiter;
     39
     40struct io_context_params {
     41        int num_entries;
     42};
     43
     44void  ?{}(io_context_params & this);
    8945
    9046//-----------------------------------------------------------------------------
     
    9551struct __processor_id_t {
    9652        unsigned id:24;
    97         bool full_proc:1;
    9853
    9954        #if !defined(__CFA_NO_STATISTICS__)
     
    11469        struct cluster * cltr;
    11570
     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
    11680        // Set to true to notify the processor should terminate
    11781        volatile bool do_terminate;
     
    12589        // Handle to pthreads
    12690        pthread_t kernel_thread;
     91
     92        struct {
     93                $io_context * ctx;
     94                bool pending;
     95                bool dirty;
     96        } io;
    12797
    12898        // Preemption data
     
    134104
    135105        // Idle lock (kernel semaphore)
    136         __bin_sem_t idle;
     106        int idle;
    137107
    138108        // Termination synchronisation (user semaphore)
     
    144114        // Link lists fields
    145115        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;
    146124
    147125        #if !defined(__CFA_NO_STATISTICS__)
     
    159137void ^?{}(processor & this);
    160138
    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 }; }
     139static inline void  ?{}(processor & this)                        { this{ "Anonymous Processor", *mainCluster}; }
     140static inline void  ?{}(processor & this, struct cluster & cltr) { this{ "Anonymous Processor", cltr}; }
     141static inline void  ?{}(processor & this, const char name[])     { this{name, *mainCluster}; }
    164142
    165143DLISTED_MGD_IMPL_OUT(processor)
    166144
    167145//-----------------------------------------------------------------------------
    168 // I/O
    169 struct __io_data;
    170 
    171 // IO poller user-thread
    172 // Not using the "thread" keyword because we want to control
    173 // more carefully when to start/stop it
    174 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 //-----------------------------------------------------------------------------
    211146// Cluster Tools
    212147
    213 // Intrusives lanes which are used by the relaxed ready queue
     148// Intrusives lanes which are used by the ready queue
    214149struct __attribute__((aligned(128))) __intrusive_lane_t;
    215150void  ?{}(__intrusive_lane_t & this);
    216151void ^?{}(__intrusive_lane_t & this);
    217152
    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
     154struct __attribute__((aligned(128))) __timestamp_t;
     155void  ?{}(__timestamp_t & this);
     156void ^?{}(__timestamp_t & this);
    228157
    229158//TODO adjust cache size to ARCHITECTURE
    230159// Structure holding the relaxed ready queue
    231160struct __ready_queue_t {
    232         // Data tracking how many/which lanes are used
    233         // Aligned to 128 for cache locality
    234         __snzi_t snzi;
    235 
    236161        // Data tracking the actual lanes
    237162        // On a seperate cacheline from the used struct since
     
    242167                __intrusive_lane_t * volatile data;
    243168
     169                // Array of times
     170                __timestamp_t * volatile tscs;
     171
    244172                // Number of lanes (empty or not)
    245173                volatile size_t count;
     
    251179
    252180// Idle Sleep
    253 struct __cluster_idles {
     181struct __cluster_proc_list {
    254182        // Spin lock protecting the queue
    255183        volatile uint64_t lock;
     
    262190
    263191        // 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;
    265196};
    266197
     
    278209
    279210        // List of idle processors
    280         __cluster_idles idles;
     211        __cluster_proc_list procs;
    281212
    282213        // List of threads
     
    292223
    293224        struct {
    294                 io_context * ctxs;
    295                 unsigned cnt;
     225                $io_arbiter * arbiter;
     226                io_context_params params;
    296227        } io;
    297228
  • libcfa/src/concurrency/kernel/fwd.hfa

    rfeacef9 r5407cdc  
    108108
    109109        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 );
    112111
    113112        extern "Cforall" {
     
    220219                        // Mark as fulfilled, wake thread if needed
    221220                        // return true if a thread was unparked
    222                         bool post(oneshot & this) {
     221                        $thread * post(oneshot & this, bool do_unpark = true) {
    223222                                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;
    227226                        }
    228227                }
     
    336335                        // from the server side, mark the future as fulfilled
    337336                        // delete it if needed
    338                         bool fulfil( future_t & this ) {
     337                        $thread * fulfil( future_t & this, bool do_unpark = true ) {
    339338                                for() {
    340339                                        struct oneshot * expected = this.ptr;
     
    344343                                                #pragma GCC diagnostic ignored "-Wfree-nonheap-object"
    345344                                        #endif
    346                                                 if( expected == 3p ) { free( &this ); return false; }
     345                                                if( expected == 3p ) { free( &this ); return 0p; }
    347346                                        #if defined(__GNUC__) && __GNUC__ >= 7
    348347                                                #pragma GCC diagnostic pop
     
    356355                                        struct oneshot * want = expected == 0p ? 1p : 2p;
    357356                                        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 );
    360359                                                __atomic_store_n( &this.ptr, 1p, __ATOMIC_SEQ_CST);
    361360                                                return ret;
     
    403402                                        __VA_ARGS__ \
    404403                                } \
    405                                 if( !(in_kernel) ) enable_interrupts( __cfaabi_dbg_ctx ); \
     404                                if( !(in_kernel) ) enable_interrupts(); \
    406405                        }
    407406                #else
  • libcfa/src/concurrency/kernel/startup.cfa

    rfeacef9 r5407cdc  
    2222extern "C" {
    2323      #include <limits.h>       // PTHREAD_STACK_MIN
     24        #include <sys/eventfd.h>  // eventfd
    2425      #include <sys/mman.h>     // mprotect
    2526      #include <sys/resource.h> // getrlimit
     
    7273static void __kernel_first_resume( processor * this );
    7374static void __kernel_last_resume ( processor * this );
    74 static void init(processor & this, const char name[], cluster & _cltr);
     75static void init(processor & this, const char name[], cluster & _cltr, $thread * initT);
    7576static void deinit(processor & this);
    7677static void doregister( struct cluster & cltr );
     
    8990extern void __kernel_alarm_startup(void);
    9091extern void __kernel_alarm_shutdown(void);
    91 extern void __kernel_io_startup (void);
    92 extern void __kernel_io_shutdown(void);
    9392
    9493//-----------------------------------------------------------------------------
     
    102101KERNEL_STORAGE($thread,              mainThread);
    103102KERNEL_STORAGE(__stack_t,            mainThreadCtx);
    104 KERNEL_STORAGE(io_context,           mainPollerThread);
    105103KERNEL_STORAGE(__scheduler_RWLock_t, __scheduler_lock);
    106104#if !defined(__CFA_NO_STATISTICS__)
     
    198196
    199197        void ?{}(processor & this) with( this ) {
    200                 ( this.idle ){};
    201198                ( this.terminated ){};
    202199                ( this.runner ){};
    203                 init( this, "Main Processor", *mainCluster );
     200                init( this, "Main Processor", *mainCluster, 0p );
    204201                kernel_thread = pthread_self();
    205202
     
    226223        __kernel_alarm_startup();
    227224
    228         // Start IO
    229         __kernel_io_startup();
    230 
    231225        // Add the main thread to the ready queue
    232226        // 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);
    234228
    235229        // SKULLDUGGERY: Force a context switch to the main processor to set the main thread's context to the current UNIX
     
    241235        // THE SYSTEM IS NOW COMPLETELY RUNNING
    242236
    243 
    244         // SKULLDUGGERY: The constructor for the mainCluster will call alloc with a dimension of 0
    245         // malloc *can* return a non-null value, we should free it if that is the case
    246         free( mainCluster->io.ctxs );
    247 
    248         // Now that the system is up, finish creating systems that need threading
    249         mainCluster->io.ctxs = (io_context *)&storage_mainPollerThread;
    250         mainCluster->io.cnt  = 1;
    251         (*mainCluster->io.ctxs){ *mainCluster };
    252 
    253237        __cfadbg_print_safe(runtime_core, "Kernel : Started\n--------------------------------------------------\n\n");
    254238
    255239        /* paranoid */ verify( ! __preemption_enabled() );
    256         enable_interrupts( __cfaabi_dbg_ctx );
     240        enable_interrupts();
    257241        /* paranoid */ verify( __preemption_enabled() );
    258242
     
    260244
    261245static void __kernel_shutdown(void) {
    262         //Before we start shutting things down, wait for systems that need threading to shutdown
    263         ^(*mainCluster->io.ctxs){};
    264         mainCluster->io.cnt  = 0;
    265         mainCluster->io.ctxs = 0p;
    266 
    267246        /* paranoid */ verify( __preemption_enabled() );
    268247        disable_interrupts();
     
    283262        __kernel_alarm_shutdown();
    284263
    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
    287274
    288275        // Destroy the main processor and its context in reverse order of construction
     
    364351                        __print_stats( &local_stats, proc->print_stats, "Processor ", proc->name, (void*)proc );
    365352                }
     353                #if defined(CFA_STATS_ARRAY)
     354                        __flush_stat( &local_stats, "Processor", proc );
     355                #endif
    366356        #endif
    367357
     
    457447        link.next = 0p;
    458448        link.prev = 0p;
     449        link.preferred = -1u;
     450        last_proc = 0p;
    459451        #if defined( __CFA_WITH_VERIFY__ )
    460452                canary = 0x0D15EA5E0D15EA5Ep;
     
    476468}
    477469
    478 static void init(processor & this, const char name[], cluster & _cltr) with( this ) {
     470static void init(processor & this, const char name[], cluster & _cltr, $thread * initT) with( this ) {
    479471        this.name = name;
    480472        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;
    482478        do_terminate = false;
    483479        preemption_alarm = 0p;
    484480        pending_preemption = false;
    485481
     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
    486493        #if !defined(__CFA_NO_STATISTICS__)
    487494                print_stats = 0;
     
    489496        #endif
    490497
    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
     513static void deinit(processor & this) {
    497514        // Lock the RWlock so no-one pushes/pops while we are changing the queue
    498515        uint_fast32_t last_size = ready_mutate_lock();
     516                this.cltr->procs.total -= 1u;
     517                remove(this);
    499518
    500519                // 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
     528void ?{}(processor & this, const char name[], cluster & _cltr, $thread * initT) {
    530529        ( this.terminated ){};
    531530        ( this.runner ){};
    532531
    533532        disable_interrupts();
    534                 init( this, name, _cltr );
    535         enable_interrupts( __cfaabi_dbg_ctx );
     533                init( this, name, _cltr, initT );
     534        enable_interrupts();
    536535
    537536        __cfadbg_print_safe(runtime_core, "Kernel : Starting core %p\n", &this);
    538537
    539538        this.stack = __create_pthread( &this.kernel_thread, __invoke_processor, (void *)&this );
    540 
     539}
     540
     541void ?{}(processor & this, const char name[], cluster & _cltr) {
     542        (this){name, _cltr, 0p};
    541543}
    542544
     
    557559        disable_interrupts();
    558560                deinit( this );
    559         enable_interrupts( __cfaabi_dbg_ctx );
     561        enable_interrupts();
    560562}
    561563
    562564//-----------------------------------------------------------------------------
    563565// Cluster
    564 static void ?{}(__cluster_idles & this) {
     566static void ?{}(__cluster_proc_list & this) {
    565567        this.lock  = 0;
    566568        this.idle  = 0;
    567569        this.total = 0;
    568         (this.list){};
    569570}
    570571
     
    582583        threads{ __get };
    583584
     585        io.arbiter = create();
     586        io.params = io_params;
     587
    584588        doregister(this);
    585589
     
    589593
    590594                // Adjust the ready queue size
    591                 ready_queue_grow( &this, 0 );
     595                ready_queue_grow( &this );
    592596
    593597        // Unlock the RWlock
    594598        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
    603600}
    604601
    605602void ^?{}(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);
    610604
    611605        // Lock the RWlock so no-one pushes/pops while we are changing the queue
     
    614608
    615609                // Adjust the ready queue size
    616                 ready_queue_shrink( &this, 0 );
     610                ready_queue_shrink( &this );
    617611
    618612        // Unlock the RWlock
    619613        ready_mutate_unlock( last_size );
    620         enable_interrupts_noPoll(); // Don't poll, could be in main cluster
     614        enable_interrupts( false ); // Don't poll, could be in main cluster
    621615
    622616        #if !defined(__CFA_NO_STATISTICS__)
     
    624618                        __print_stats( this.stats, this.print_stats, "Cluster", this.name, (void*)&this );
    625619                }
     620                #if defined(CFA_STATS_ARRAY)
     621                        __flush_stat( this.stats, "Cluster", &this );
     622                #endif
    626623                free( this.stats );
    627624        #endif
     
    736733}
    737734
    738 
    739735#if defined(__CFA_WITH_VERIFY__)
    740736static bool verify_fwd_bck_rng(void) {
  • libcfa/src/concurrency/kernel_private.hfa

    rfeacef9 r5407cdc  
    2929extern "C" {
    3030        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
     34void schedule_thread$( $thread * ) __attribute__((nonnull (1)));
    4035
    4136extern bool __preemption_enabled();
     
    7772//-----------------------------------------------------------------------------
    7873// I/O
    79 void ^?{}(io_context & this, bool );
     74$io_arbiter * create(void);
     75void destroy($io_arbiter *);
    8076
    8177//=======================================================================
    8278// Cluster lock API
    8379//=======================================================================
    84 // Cells use by the reader writer lock
    85 // while not generic it only relies on a opaque pointer
    86 struct __attribute__((aligned(128))) __scheduler_lock_id_t {
    87         // Spin lock used as the underlying lock
    88         volatile bool lock;
    89 
    90         // Handle pointing to the proc owning this cell
    91         // Used for allocating cells and debugging
    92         __processor_id_t * volatile handle;
    93 
    94         #ifdef __CFA_WITH_VERIFY__
    95                 // Debug, check if this is owned for reading
    96                 bool owned;
    97         #endif
    98 };
    99 
    100 static_assert( sizeof(struct __scheduler_lock_id_t) <= __alignof(struct __scheduler_lock_id_t));
    101 
    10280// Lock-Free registering/unregistering of threads
    10381// Register a processor to a given cluster and get its unique id in return
    104 unsigned doregister( struct __processor_id_t * proc );
     82void register_proc_id( struct __processor_id_t * );
    10583
    10684// 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 }
     85void unregister_proc_id( struct __processor_id_t * proc );
    12686
    12787//=======================================================================
     
    151111        __atomic_store_n(ll, (bool)false, __ATOMIC_RELEASE);
    152112}
     113
     114// Cells use by the reader writer lock
     115// while not generic it only relies on a opaque pointer
     116struct __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
     130static_assert( sizeof(struct __scheduler_lock_id_t) <= __alignof(struct __scheduler_lock_id_t));
    153131
    154132//-----------------------------------------------------------------------
     
    246224void ready_mutate_unlock( uint_fast32_t /* value returned by lock */ );
    247225
     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
     230static 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
     237static 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
     244static 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
     265static 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
    248278//=======================================================================
    249279// Ready-Queue API
    250280//-----------------------------------------------------------------------
    251 // pop thread from the ready queue of a cluster
    252 // returns 0p if empty
    253 __attribute__((hot)) bool query(struct cluster * cltr);
    254 
    255 //-----------------------------------------------------------------------
    256281// push thread onto a ready queue for a cluster
    257282// returns true if the list was previously empty, false otherwise
    258 __attribute__((hot)) bool push(struct cluster * cltr, struct $thread * thrd);
    259 
    260 //-----------------------------------------------------------------------
    261 // pop thread from the ready queue of a cluster
     283__attribute__((hot)) void push(struct cluster * cltr, struct $thread * thrd);
     284
     285//-----------------------------------------------------------------------
     286// pop thread from the local queues of a cluster
    262287// returns 0p if empty
    263288// 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
    268299// returns 0p if empty
    269300// 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);
    276302
    277303//-----------------------------------------------------------------------
    278304// Increase the width of the ready queue (number of lanes) by 4
    279 void ready_queue_grow  (struct cluster * cltr, int target);
     305void ready_queue_grow  (struct cluster * cltr);
    280306
    281307//-----------------------------------------------------------------------
    282308// Decrease the width of the ready queue (number of lanes) by 4
    283 void ready_queue_shrink(struct cluster * cltr, int target);
     309void ready_queue_shrink(struct cluster * cltr);
    284310
    285311
  • libcfa/src/concurrency/locks.cfa

    rfeacef9 r5407cdc  
    134134        lock( lock __cfaabi_dbg_ctx2 );
    135135        /* 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 );
    137138
    138139        // if recursion count is zero release lock and set new owner if one is waiting
     
    146147size_t wait_count( blocking_lock & this ) with( this ) {
    147148        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;
    156149}
    157150
     
    173166}
    174167
    175 void on_wait( blocking_lock & this ) with( this ) {
     168size_t on_wait( blocking_lock & this ) with( this ) {
    176169        lock( lock __cfaabi_dbg_ctx2 );
    177170        /* paranoid */ verifyf( owner != 0p, "Attempt to release lock %p that isn't held", &this );
    178171        /* paranoid */ verifyf( owner == active_thread() || !strict_owner, "Thread %p other than the owner %p attempted to release owner lock %p", owner, active_thread(), &this );
    179172
     173        size_t ret = recursion_count;
     174
    180175        pop_and_set_new_owner( this );
    181176        unlock( lock );
     177        return ret;
     178}
     179
     180void on_wakeup( blocking_lock & this, size_t recursion ) with( this ) {
     181        recursion_count = recursion;
    182182}
    183183
     
    274274        }
    275275
    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        }
    277282
    278283        int counter( condition_variable(L) & this ) with(this) { return count; }
     
    285290                if (i->lock) {
    286291                        // 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 );
    289293                }
    290294                return recursion_count;
     
    301305
    302306                // 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);
    304308        }
    305309
     
    323327
    324328                // 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);
    326330        }
    327331
     
    373377}
    374378
    375 bool V(semaphore & this) with( this ) {
     379$thread * V (semaphore & this, const bool doUnpark ) with( this ) {
    376380        $thread * thrd = 0p;
    377381        lock( lock __cfaabi_dbg_ctx2 );
     
    385389
    386390        // make new owner
    387         unpark( thrd );
    388 
     391        if( doUnpark ) unpark( thrd );
     392
     393        return thrd;
     394}
     395
     396bool V(semaphore & this) with( this ) {
     397        $thread * thrd = V(this, true);
    389398        return thrd != 0p;
    390399}
  • libcfa/src/concurrency/locks.hfa

    rfeacef9 r5407cdc  
    2020
    2121#include "bits/weakso_locks.hfa"
     22#include "containers/queueLockFree.hfa"
     23
     24#include "thread.hfa"
    2225
    2326#include "time_t.hfa"
    2427#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
     36struct Semaphore0nary {
     37        __spinlock_t lock; // needed to protect
     38        mpsc_queue($thread) queue;
     39};
     40
     41static 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
     49static inline bool P(Semaphore0nary & this) {
     50    $thread * thrd = active_thread();
     51    P(this, thrd);
     52    park();
     53    return true;
     54}
     55
     56static 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
     71struct BinaryBenaphore {
     72        volatile ssize_t counter;
     73};
     74
     75static 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
     108struct ThreadBenaphore {
     109        BinaryBenaphore ben;
     110        Semaphore0nary  sem;
     111};
     112
     113static inline void ?{}(ThreadBenaphore & this) {}
     114static inline void ?{}(ThreadBenaphore & this, zero_t) { (this.ben){ 0 }; }
     115static inline void ?{}(ThreadBenaphore & this, one_t ) { (this.ben){ 1 }; }
     116
     117static inline bool P(ThreadBenaphore & this)              { return P(this.ben) ? false : P(this.sem); }
     118static inline bool tryP(ThreadBenaphore & this)           { return tryP(this.ben); }
     119static inline bool P(ThreadBenaphore & this, bool wait)   { return wait ? P(this) : tryP(this); }
     120
     121static 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
     128struct semaphore {
     129        __spinlock_t lock;
     130        int count;
     131        __queue_t($thread) waiting;
     132};
     133
     134void  ?{}(semaphore & this, int count = 1);
     135void ^?{}(semaphore & this);
     136bool   P (semaphore & this);
     137bool   V (semaphore & this);
     138bool   V (semaphore & this, unsigned count);
     139$thread * V (semaphore & this, bool );
    25140
    26141//----------
     
    31146static inline void  ?{}( single_acquisition_lock & this ) {((blocking_lock &)this){ false, false };}
    32147static 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 ); }
     148static inline void   lock     ( single_acquisition_lock & this ) { lock    ( (blocking_lock &)this ); }
     149static inline bool   try_lock ( single_acquisition_lock & this ) { return try_lock( (blocking_lock &)this ); }
     150static inline void   unlock   ( single_acquisition_lock & this ) { unlock  ( (blocking_lock &)this ); }
     151static inline size_t on_wait  ( single_acquisition_lock & this ) { return on_wait ( (blocking_lock &)this ); }
     152static inline void   on_wakeup( single_acquisition_lock & this, size_t v ) { on_wakeup ( (blocking_lock &)this, v ); }
     153static inline void   on_notify( single_acquisition_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); }
    39154
    40155//----------
     
    45160static inline void  ?{}( owner_lock & this ) {((blocking_lock &)this){ true, true };}
    46161static 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 ); }
     162static inline void   lock     ( owner_lock & this ) { lock    ( (blocking_lock &)this ); }
     163static inline bool   try_lock ( owner_lock & this ) { return try_lock( (blocking_lock &)this ); }
     164static inline void   unlock   ( owner_lock & this ) { unlock  ( (blocking_lock &)this ); }
     165static inline size_t on_wait  ( owner_lock & this ) { return on_wait ( (blocking_lock &)this ); }
     166static inline void   on_wakeup( owner_lock & this, size_t v ) { on_wakeup ( (blocking_lock &)this, v ); }
    50167static 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
     169struct fast_lock {
     170        $thread * volatile owner;
     171        ThreadBenaphore sem;
     172};
     173
     174static 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
     179static inline void lock( fast_lock & this ) __attribute__((artificial));
     180static 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
     190static inline bool try_lock( fast_lock & this ) __attribute__((artificial));
     191static 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
     197static inline $thread * unlock( fast_lock & this ) __attribute__((artificial));
     198static 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
     209static inline size_t on_wait( fast_lock & this ) { unlock(this); return 0; }
     210static inline void on_wakeup( fast_lock & this, size_t ) { lock(this); }
     211static inline void on_notify( fast_lock &, struct $thread * t ) { unpark(t); }
     212
     213struct mcs_node {
     214        mcs_node * volatile next;
     215        single_sem sem;
     216};
     217
     218static inline void ?{}(mcs_node & this) { this.next = 0p; }
     219
     220static inline mcs_node * volatile & ?`next ( mcs_node * node ) {
     221        return node->next;
     222}
     223
     224struct mcs_lock {
     225        mcs_queue(mcs_node) queue;
     226};
     227
     228static inline void lock(mcs_lock & l, mcs_node & n) {
     229        if(push(l.queue, &n))
     230                wait(n.sem);
     231}
     232
     233static inline void unlock(mcs_lock & l, mcs_node & n) {
     234        mcs_node * next = advance(l.queue, &n);
     235        if(next) post(next->sem);
     236}
    53237
    54238//-----------------------------------------------------------------------------
     
    59243
    60244        // 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 & );
    65246
    66247        // to set recursion count after getting signalled;
    67         void set_recursion_count( L &, size_t recursion );
     248        void on_wakeup( L &, size_t recursion );
    68249};
    69250
     
    119300        bool wait( condition_variable(L) & this, L & l, uintptr_t info, Time time );
    120301}
    121 
    122 //-----------------------------------------------------------------------------
    123 // Semaphore
    124 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  
    6161static inline forall( T & | sized(T) | { void ^?{}( T & mutex ); } )
    6262void delete( T * th ) {
    63         ^(*th){};
     63        if(th) ^(*th){};
    6464        free( th );
    6565}
  • libcfa/src/concurrency/preemption.cfa

    rfeacef9 r5407cdc  
    1515
    1616#define __cforall_thread__
     17// #define __CFA_DEBUG_PRINT_PREEMPTION__
    1718
    1819#include "preemption.hfa"
     
    2829#include "kernel_private.hfa"
    2930
     31
    3032#if !defined(__CFA_DEFAULT_PREEMPTION__)
    3133#define __CFA_DEFAULT_PREEMPTION__ 10`ms
    3234#endif
    3335
    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;
    3655}
    3756
     
    98117        //Loop throught every thing expired
    99118        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);
    101120                Duration period = node->period;
    102121                if( period == 0) {
     
    104123                }
    105124
     125                __cfadbg_print_buffer_local( preemption, " KERNEL: alarm ticking node %p.\n", node );
     126
     127
    106128                // Check if this is a kernel
    107129                if( node->type == Kernel ) {
     
    109131                }
    110132                else if( node->type == User ) {
     133                        __cfadbg_print_buffer_local( preemption, " KERNEL: alarm unparking %p.\n", node->thrd );
    111134                        timeout( node->thrd );
    112135                }
     
    117140                // Check if this is a periodic alarm
    118141                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 );
    120143                        node->alarm = currtime + period;    // Alarm is periodic, add currtime to it (used cached current time)
    121144                        insert( alarms, node );             // Reinsert the node for the next time it triggers
     
    125148        // If there are still alarms pending, reset the timer
    126149        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);
    128150                Duration delta = (*alarms)`first.alarm - currtime;
    129151                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 
    133152                __kernel_set_timer( capped );
    134153        }
     
    296315        // Enable interrupts by decrementing the counter
    297316        // If counter reaches 0, execute any pending __cfactx_switch
    298         void enable_interrupts( __cfaabi_dbg_ctx_param ) {
     317        void enable_interrupts( bool poll ) {
    299318                // Cache the processor now since interrupts can start happening after the atomic store
    300319                processor   * proc = __cfaabi_tls.this_processor;
    301                 /* paranoid */ verify( proc );
     320                /* paranoid */ verify( !poll || proc );
    302321
    303322                with( __cfaabi_tls.preemption_state ){
     
    321340                                // Signal the compiler that a fence is needed but only for signal handlers
    322341                                __atomic_signal_fence(__ATOMIC_RELEASE);
    323                                 if( proc->pending_preemption ) {
     342                                if( poll && proc->pending_preemption ) {
    324343                                        proc->pending_preemption = false;
    325344                                        force_yield( __POLL_PREEMPTION );
    326345                                }
    327346                        }
    328                 }
    329 
    330                 // For debugging purposes : keep track of the last person to enable the interrupts
    331                 __cfaabi_dbg_debug_do( proc->last_enable = caller; )
    332         }
    333 
    334         // Disable interrupts by incrementint the counter
    335         // Don't execute any pending __cfactx_switch even if counter reaches 0
    336         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 interrupts
    340                 /* paranoid */ verifyf( prev != 0u, "Incremented from %u\n", prev );
    341                 if( prev == 1 ) {
    342                         #if GCC_VERSION > 50000
    343                                 static_assert(__atomic_always_lock_free(sizeof(__cfaabi_tls.preemption_state.enabled), &__cfaabi_tls.preemption_state.enabled), "Must be lock-free");
    344                         #endif
    345                         // Set enabled flag to true
    346                         // 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 requirements
    348                         __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 handlers
    351                         __atomic_signal_fence(__ATOMIC_RELEASE);
    352347                }
    353348        }
     
    585580
    586581        // Setup proper signal handlers
    587         __cfaabi_sigaction( SIGUSR1, sigHandler_ctxSwitch, SA_SIGINFO | SA_RESTART ); // __cfactx_switch handler
    588         __cfaabi_sigaction( SIGALRM, sigHandler_alarm    , SA_SIGINFO | SA_RESTART ); // debug handler
     582        __cfaabi_sigaction( SIGUSR1, sigHandler_ctxSwitch, SA_SIGINFO ); // __cfactx_switch handler
     583        __cfaabi_sigaction( SIGALRM, sigHandler_alarm    , SA_SIGINFO ); // debug handler
    589584
    590585        signal_block( SIGALRM );
     
    689684}
    690685
    691 #if !defined(__CFA_NO_STATISTICS__)
    692         int __print_alarm_stats = 0;
    693 #endif
    694 
    695686// Main of the alarm thread
    696687// Waits on SIGALRM and send SIGUSR1 to whom ever needs it
    697688static void * alarm_loop( __attribute__((unused)) void * args ) {
    698689        __processor_id_t id;
    699         id.full_proc = false;
    700         id.id = doregister(&id);
     690        register_proc_id(&id);
    701691        __cfaabi_tls.this_proc_id = &id;
    702692
    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         #endif
    708693
    709694        // Block sigalrms to control when they arrive
     
    764749EXIT:
    765750        __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
    773753        return 0p;
    774754}
  • libcfa/src/concurrency/ready_queue.cfa

    rfeacef9 r5407cdc  
    1717// #define __CFA_DEBUG_PRINT_READY_QUEUE__
    1818
    19 // #define USE_SNZI
     19// #define USE_MPSC
     20
     21#define USE_RELAXED_FIFO
     22// #define USE_WORK_STEALING
    2023
    2124#include "bits/defs.hfa"
     
    2831#include <unistd.h>
    2932
    30 #include "snzi.hfa"
    3133#include "ready_subqueue.hfa"
    3234
    3335static 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
    3442
    3543// No overriden function, no environment variable, no define
     
    3947#endif
    4048
    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
     60static inline struct $thread * try_pop(struct cluster * cltr, unsigned w __STATS(, __stats_readyQ_pop_t & stats));
     61static inline struct $thread * try_pop(struct cluster * cltr, unsigned i, unsigned j __STATS(, __stats_readyQ_pop_t & stats));
     62static inline struct $thread * search(struct cluster * cltr);
     63static inline [unsigned, bool] idx_from_r(unsigned r, unsigned preferred);
     64
    4265
    4366// returns the maximum number of processors the RWLock support
     
    93116//=======================================================================
    94117// Lock-Free registering/unregistering of threads
    95 unsigned doregister( struct __processor_id_t * proc ) with(*__scheduler_lock) {
     118void register_proc_id( struct __processor_id_t * proc ) with(*__scheduler_lock) {
    96119        __cfadbg_print_safe(ready_queue, "Kernel : Registering proc %p for RW-Lock\n", proc);
    97120
     
    107130                        /*paranoid*/ verify(0 == (__alignof__(data[i]) % cache_line_size));
    108131                        /*paranoid*/ verify((((uintptr_t)&data[i]) % cache_line_size) == 0);
    109                         return i;
     132                        proc->id = i;
    110133                }
    111134        }
     
    134157        /*paranoid*/ verify(__alignof__(data[n]) == (2 * cache_line_size));
    135158        /*paranoid*/ verify((((uintptr_t)&data[n]) % cache_line_size) == 0);
    136         return n;
    137 }
    138 
    139 void unregister( struct __processor_id_t * proc ) with(*__scheduler_lock) {
     159        proc->id = n;
     160}
     161
     162void unregister_proc_id( struct __processor_id_t * proc ) with(*__scheduler_lock) {
    140163        unsigned id = proc->id;
    141164        /*paranoid*/ verify(id < ready);
     
    192215
    193216//=======================================================================
    194 // Cforall Reqdy Queue used for scheduling
     217// Cforall Ready Queue used for scheduling
    195218//=======================================================================
    196219void ?{}(__ready_queue_t & this) with (this) {
    197220        lanes.data  = 0p;
     221        lanes.tscs  = 0p;
    198222        lanes.count = 0;
    199223}
    200224
    201225void ^?{}(__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 );
    206227        free(lanes.data);
     228        free(lanes.tscs);
    207229}
    208230
    209231//-----------------------------------------------------------------------
    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;
    221238                unsigned rlow  = r % BIAS;
    222239                unsigned rhigh = r / BIAS;
     
    224241                        // (BIAS - 1) out of BIAS chances
    225242                        // Use perferred queues
    226                         i = preferred + (rhigh % 4);
     243                        i = preferred + (rhigh % READYQ_SHARD_FACTOR);
    227244                        local = true;
    228245                }
     
    233250                        local = false;
    234251                }
    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
     456static 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;
    238496        #endif
    239         return [i, local];
     497
     498        // return the popped thread
     499        return thrd;
    240500}
    241501
    242502//-----------------------------------------------------------------------
    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
     505static inline struct $thread * search(struct cluster * cltr) with (cltr->ready_queue) {
    381506        /* paranoid */ verify( lanes.count > 0 );
    382507        unsigned count = __atomic_load_n( &lanes.count, __ATOMIC_RELAXED );
     
    384509        for(i; count) {
    385510                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));
    387512                if(thrd) {
    388513                        return thrd;
     
    394519}
    395520
    396 
    397521//-----------------------------------------------------------------------
    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
    489523static void check( __ready_queue_t & q ) with (q) {
    490         #if defined(__CFA_WITH_VERIFY__)
     524        #if defined(__CFA_WITH_VERIFY__) && !defined(USE_MPSC)
    491525                {
    492526                        for( idx ; lanes.count ) {
     
    499533                                assert(tail(sl)->link.prev->link.next == tail(sl) );
    500534
    501                                 if(sl.before.link.ts == 0l) {
     535                                if(is_empty(sl)) {
    502536                                        assert(tail(sl)->link.prev == head(sl));
    503537                                        assert(head(sl)->link.next == tail(sl));
     
    511545}
    512546
     547//-----------------------------------------------------------------------
     548// Given 2 indexes, pick the list with the oldest push an try to pop from it
     549static 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
    513559// Call this function of the intrusive list was moved using memcpy
    514560// fixes the list so that the pointers back to anchors aren't left dangling
    515561static 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
     578static 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
     589static 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
     595static 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
    528602}
    529603
    530604// Grow the ready queue
    531 void ready_queue_grow  (struct cluster * cltr, int target) {
     605void ready_queue_grow(struct cluster * cltr) {
     606        size_t ncount;
     607        int target = cltr->procs.total;
     608
    532609        /* paranoid */ verify( ready_mutate_islocked() );
    533610        __cfadbg_print_safe(ready_queue, "Kernel : Growing ready queue\n");
     
    538615        // grow the ready queue
    539616        with( cltr->ready_queue ) {
    540                 #ifdef USE_SNZI
    541                         ^(snzi){};
    542                 #endif
    543 
    544617                // Find new count
    545618                // 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                }
    547624
    548625                // Allocate new array (uses realloc and memcpies the data)
     
    561638                // Update original
    562639                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);
    574645
    575646        // Make sure that everything is consistent
     
    582653
    583654// Shrink the ready queue
    584 void ready_queue_shrink(struct cluster * cltr, int target) {
     655void ready_queue_shrink(struct cluster * cltr) {
    585656        /* paranoid */ verify( ready_mutate_islocked() );
    586657        __cfadbg_print_safe(ready_queue, "Kernel : Shrinking ready queue\n");
     
    589660        /* paranoid */ check( cltr->ready_queue );
    590661
     662        int target = cltr->procs.total;
     663
    591664        with( cltr->ready_queue ) {
    592                 #ifdef USE_SNZI
    593                         ^(snzi){};
    594                 #endif
    595 
    596665                // Remember old count
    597666                size_t ocount = lanes.count;
     
    599668                // Find new count
    600669                // 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;
    602671                /* 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 );
    604673
    605674                // for printing count the number of displaced threads
     
    644713                        fix(lanes.data[idx]);
    645714                }
    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);
    657720
    658721        // Make sure that everything is consistent
  • libcfa/src/concurrency/ready_subqueue.hfa

    rfeacef9 r5407cdc  
    22
    33#define __CFA_NO_SCHED_STATS__
     4
     5#include "containers/queueLockFree.hfa"
    46
    57// Intrusives lanes which are used by the relaxed ready queue
    68struct __attribute__((aligned(128))) __intrusive_lane_t {
    79
    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
    1522
    1623        // spin lock protecting the queue
     
    3542// Get the head pointer (one before the first element) from the anchor
    3643static 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
    4253}
    4354
    4455// Get the tail pointer (one after the last element) from the anchor
    4556static 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
    5166}
    5267
     
    5570        this.lock = false;
    5671
    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
    85102}
    86103
    87104// Dtor is trivial
    88105void ^?{}( __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
    94113}
    95114
     
    97116// returns true of lane was empty before push, false otherwise
    98117bool 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
    107160                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
    141167}
    142168
     
    146172$thread * pop(__intrusive_lane_t & this) {
    147173        /* 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
    192225}
    193226
    194227// Check whether or not list is empty
    195228static 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
    198235}
    199236
    200237// Return the timestamp
    201238static 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
     250struct __attribute__((aligned(128))) __timestamp_t {
     251        volatile unsigned long long tv;
     252};
     253
     254void  ?{}(__timestamp_t & this) { this.tv = 0; }
     255void ^?{}(__timestamp_t & this) {}
  • libcfa/src/concurrency/stats.cfa

    rfeacef9 r5407cdc  
    55#include <inttypes.h>
    66#include "bits/debug.hfa"
     7#include "bits/locks.hfa"
    78#include "stats.hfa"
     9#include "strstream.hfa"
    810
    911#if !defined(__CFA_NO_STATISTICS__)
    1012        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;
    2039                stats->ready.threads.migration = 0;
     40                stats->ready.threads.extunpark = 0;
     41                stats->ready.threads.threads   = 0;
    2142                stats->ready.sleep.halts   = 0;
    2243                stats->ready.sleep.cancels = 0;
     
    2546
    2647                #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;
    4267                #endif
    4368        }
    4469
    4570        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;
    5597                __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;
    56100                __atomic_fetch_add( &cltr->ready.sleep.halts       , proc->ready.sleep.halts       , __ATOMIC_SEQ_CST ); proc->ready.sleep.halts        = 0;
    57101                __atomic_fetch_add( &cltr->ready.sleep.cancels     , proc->ready.sleep.cancels     , __ATOMIC_SEQ_CST ); proc->ready.sleep.cancels      = 0;
     
    60104
    61105                #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;
    78120                #endif
    79121        }
    80122
     123        #define eng3(X) (ws(3, 3, unit(eng( X ))))
     124
    81125        void __print_stats( struct __stats_t * stats, int flags, const char * type, const char * name, void * id ) with( *stats ) {
    82126
     127                char buf[1024];
     128                ostrstream sstr = { buf, 1024 };
     129
    83130                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;
    121162                }
    122163
    123164                #if defined(CFA_HAVE_LINUX_IO_URING_H)
    124165                        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;
    172187                        }
    173188                #endif
     189
     190                if(flags) write( sstr, stdout );
    174191        }
     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
    175237#endif
  • libcfa/src/concurrency/stats.hfa

    rfeacef9 r5407cdc  
    11#pragma once
     2
     3// #define CFA_STATS_ARRAY 10000
    24
    35#include <stdint.h>
     
    1416        static inline void __print_stats( struct __stats_t *, int, const char *, const char *, void * ) {}
    1517#else
     18        struct __stats_readyQ_pop_t {
     19                // number of attemps at poping something
     20                volatile uint64_t attempt;
    1621
    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
    1837                struct {
    19                         // Push statistic
    2038                        struct {
    21                                 // number of attemps at pushing something
     39                                // number of attemps at pushing something to preferred queues
    2240                                volatile uint64_t attempt;
    2341
    24                                 // number of successes at pushing
     42                                // number of successes at pushing to preferred queues
    2543                                volatile uint64_t success;
     44                        }
     45                        // Stats for local queue within cluster
     46                        local,
    2647
    27                                 // number of attemps at pushing something to preferred queues
    28                                 volatile uint64_t local;
     48                        // Stats for non-local queues within cluster
     49                        share,
    2950
    30                                 // number of successes at pushing to preferred queues
    31                                 volatile uint64_t lsuccess;
    32                         } push;
     51                        // Stats from outside cluster
     52                        extrn;
     53                } push;
    3354
    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;
    4059
    41                                 // number of attemps at poping something
    42                                 volatile uint64_t attempt;
     60                        // pop before looking at local queue
     61                        __stats_readyQ_pop_t help;
    4362
    44                                 // number of successes at poping
    45                                 volatile uint64_t success;
     63                        // pop from some other queue
     64                        __stats_readyQ_pop_t steal;
    4665
    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;
    4969
    50                                 // number of successes at poping to preferred queues
    51                                 volatile uint64_t lsuccess;
    52                         } pop;
    53                 } pick;
    5470                struct {
    5571                        volatile uint64_t migration;
     72                        volatile uint64_t extunpark;
     73                        volatile  int64_t threads; // number of threads in the system, includes only local change
    5674                } threads;
    5775                struct {
     
    6684                struct __attribute__((aligned(64))) __stats_io_t{
    6785                        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;
    68104                                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;
    88108                        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;
    95118                };
    96119        #endif
    97120
    98121        struct __attribute__((aligned(128))) __stats_t {
    99                 __stats_readQ_t ready;
     122                __stats_readyQ_t ready;
    100123                #if defined(CFA_HAVE_LINUX_IO_URING_H)
    101124                        __stats_io_t    io;
    102125                #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
    103134        };
    104135
     
    106137        void __tally_stats( struct __stats_t *, struct __stats_t * );
    107138        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
    108146#endif
    109147
  • libcfa/src/concurrency/thread.cfa

    rfeacef9 r5407cdc  
    3939        link.next = 0p;
    4040        link.prev = 0p;
    41         link.preferred = -1;
     41        link.preferred = -1u;
     42        last_proc = 0p;
    4243        #if defined( __CFA_WITH_VERIFY__ )
    4344                canary = 0x0D15EA5E0D15EA5Ep;
     
    6263}
    6364
    64 FORALL_DATA_INSTANCE(ThreadCancelled, (thread_t &), (thread_t))
    65 
    6665forall(T &)
    6766void copy(ThreadCancelled(T) * dst, ThreadCancelled(T) * src) {
     
    7372forall(T &)
    7473const char * msg(ThreadCancelled(T) *) {
    75         return "ThreadCancelled";
     74        return "ThreadCancelled(...)";
    7675}
    7776
    7877forall(T &)
    7978static void default_thread_cancel_handler(ThreadCancelled(T) & ) {
     79        // Improve this error message, can I do formatting?
    8080        abort( "Unhandled thread cancellation.\n" );
    8181}
    8282
    83 forall(T & | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T)))
     83forall(T & | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T))
     84    | { EHM_DEFAULT_VTABLE(ThreadCancelled, (T)); })
    8485void ?{}( thread_dtor_guard_t & this,
    8586                T & thrd, void(*cancelHandler)(ThreadCancelled(T) &)) {
    86         $monitor * m = get_monitor(thrd);
     87        $monitor * m = get_monitor(thrd);
    8788        $thread * desc = get_thread(thrd);
    8889
     
    103104        }
    104105        desc->state = Cancelled;
    105         void(*defaultResumptionHandler)(ThreadCancelled(T) &) = 
     106        void(*defaultResumptionHandler)(ThreadCancelled(T) &) =
    106107                join ? cancelHandler : default_thread_cancel_handler;
    107108
     109        // TODO: Remove explitate vtable set once trac#186 is fixed.
    108110        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;
    111112        except.the_thread = &thrd;
    112113        except.the_exception = __cfaehm_cancellation_exception( cancellation );
    113         throwResume except;
     114        // Why is this cast required?
     115        throwResume (ThreadCancelled(T) &)except;
    114116
    115117        except.the_exception->virtual_table->free( except.the_exception );
     
    134136        /* paranoid */ verify( this_thrd->context.SP );
    135137
    136         __schedule_thread( this_thrd );
    137         enable_interrupts( __cfaabi_dbg_ctx );
     138        schedule_thread$( this_thrd );
     139        enable_interrupts();
    138140}
    139141
     
    158160
    159161//-----------------------------------------------------------------------------
    160 forall(T & | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T)))
     162forall(T & | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T))
     163    | { EHM_DEFAULT_VTABLE(ThreadCancelled, (T)); })
    161164T & join( T & this ) {
    162165        thread_dtor_guard_t guard = { this, defaultResumptionHandler };
     
    167170        disable_interrupts();
    168171        uint64_t ret = __tls_rand();
    169         enable_interrupts( __cfaabi_dbg_ctx );
     172        enable_interrupts();
    170173        return ret;
    171174}
  • libcfa/src/concurrency/thread.hfa

    rfeacef9 r5407cdc  
    3232};
    3333
    34 FORALL_DATA_EXCEPTION(ThreadCancelled, (thread_t &), (thread_t)) (
     34EHM_FORALL_EXCEPTION(ThreadCancelled, (thread_t &), (thread_t)) (
    3535        thread_t * the_thread;
    3636        exception_t * the_exception;
     
    4242forall(T &)
    4343const char * msg(ThreadCancelled(T) *);
    44 
    45 // define that satisfies the trait without using the thread keyword
    46 #define DECL_THREAD(X) $thread* get_thread(X& this) __attribute__((const)) { return &this.__thrd; } void main(X& this)
    4744
    4845// Inline getters for threads/coroutines/monitors
     
    8279};
    8380
    84 forall( T & | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T)) )
     81forall( T & | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T))
     82    | { EHM_DEFAULT_VTABLE(ThreadCancelled, (T)); })
    8583void ?{}( thread_dtor_guard_t & this, T & thrd, void(*)(ThreadCancelled(T) &) );
    8684void ^?{}( thread_dtor_guard_t & this );
     
    128126//----------
    129127// join
    130 forall( T & | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T)) )
     128forall( T & | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T))
     129    | { EHM_DEFAULT_VTABLE(ThreadCancelled, (T)); })
    131130T & join( T & this );
    132131
  • libcfa/src/containers/list.hfa

    rfeacef9 r5407cdc  
    8383                (this.is_terminator){ 1 };
    8484        }
    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;
    8994        }
    9095        struct $dlinks {
     
    181186
    182187        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);
    185190                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);
    188193                $prev_link(singleton_to_insert) = & $tempcv_n2e(list_pos);
    189194                $next_link(singleton_to_insert) = $next_link(list_pos);
     
    204209
    205210        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);
    208213                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);
    211216                $next_link(singleton_to_insert) = & $tempcv_n2e(list_pos);
    212217                $prev_link(singleton_to_insert) = $prev_link(list_pos);
     
    227232
    228233    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);
    231236                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);
    234239
    235240                $prev_link(singleton_to_insert) = (void*) &list;
     
    249254
    250255    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);
    253258                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);
    256261
    257262                $next_link(singleton_to_insert) = (void*) &list;
     
    271276
    272277    static inline void remove(Tnode &list_pos) {
    273                 assert( &list_pos != 0p );
     278                verify( &list_pos != 0p );
    274279
    275280                $mgd_link(Telem) &incoming_from_prev = *0p;
     
    308313
    309314        static inline bool ?`is_empty(dlist(Tnode, Telem) &list) {
    310                 assert( &list != 0p );
     315                verify( &list != 0p );
    311316                $dlinks(Telem) *listLinks = & list.$links;
    312317                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);
    316321                        return true;
    317322                } 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);
    321326                        return false;
    322327                }
     
    324329
    325330        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 );
    328333                $dlinks(Telem) *listLinks = & list.$links;
    329334                Telem & first = *listLinks->next.elem;
     
    334339
    335340        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 );
    338343                $dlinks(Telem) *listLinks = & list.$links;
    339344                Telem & last = *listLinks->prev.elem;
  • libcfa/src/exception.c

    rfeacef9 r5407cdc  
    1010// Created On       : Mon Jun 26 15:13:00 2017
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Tue Oct 27 16:27:00 2020
    13 // Update Count     : 35
     12// Last Modified On : Wed Feb 24 13:40:00 2021
     13// Update Count     : 36
    1414//
    1515
     
    2626#include "concurrency/invoke.h"
    2727#include "stdhdr/assert.h"
     28#include "virtual.h"
    2829
    2930#if defined( __ARM_ARCH )
     
    4647const _Unwind_Exception_Class __cfaehm_exception_class = 0x4c50575500414643;
    4748
    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:
     50struct __cfa__parent_vtable __cfatid_exception_t = {
     51        NULL,
    5652};
    5753
  • libcfa/src/exception.h

    rfeacef9 r5407cdc  
    1010// Created On       : Mon Jun 26 15:11:00 2017
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Tue Oct 27 14:45:00 2020
    13 // Update Count     : 11
     12// Last Modified On : Thr Apr  8 15:20:00 2021
     13// Update Count     : 12
    1414//
    1515
     
    2929struct __cfaehm_base_exception_t;
    3030typedef struct __cfaehm_base_exception_t exception_t;
     31struct __cfa__parent_vtable;
    3132struct __cfaehm_base_exception_t_vtable {
    32         const struct __cfaehm_base_exception_t_vtable * parent;
     33        const struct __cfa__parent_vtable * __cfavir_typeid;
    3334        size_t size;
    3435        void (*copy)(struct __cfaehm_base_exception_t *this,
     
    4041        struct __cfaehm_base_exception_t_vtable const * virtual_table;
    4142};
    42 extern struct __cfaehm_base_exception_t_vtable
    43         ___cfaehm_base_exception_t_vtable_instance;
     43extern struct __cfa__parent_vtable __cfatid_exception_t;
    4444
    4545
     
    104104        /* The first field must be a pointer to a virtual table.
    105105         * 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.
    106108         */
    107         virtualT const & get_exception_vtable(exceptT *);
    108         // Always returns the virtual table for this type (associated types hack).
    109109};
    110110
  • libcfa/src/exception.hfa

    rfeacef9 r5407cdc  
    1010// Created On       : Thu Apr  7 10:25:00 2020
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Tue Aug  4 16:22:00 2020
    13 // Update Count     : 3
     12// Last Modified On : Thr Apr  8 15:16:00 2021
     13// Update Count     : 4
    1414//
    1515
     
    1818// -----------------------------------------------------------------------------------------------
    1919
    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, , )
    2528
    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)
    3033
    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)
    3840
    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)
    4750
    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)
    5456
    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)
    5965
    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
    12770
    12871// IS_EXCEPTION(exception_name [, (...parameters)])
     
    13578#define IS_TERMINATION_EXCEPTION(...) _IS_EXCEPTION(is_termination_exception, __VA_ARGS__, , ~)
    13679
    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.
    14381
    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
    15187
    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
    16091
    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, \
    178100        }
    179101
    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        }
    185108
    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        }
    194113
    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        }
    207119
    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        }
    219124
    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; \
    225133                size_t size; \
    226134                void (*copy)(exception_name parameters * this, exception_name parameters * other); \
    227135                void (*^?{})(exception_name parameters & this); \
    228136                const char * (*msg)(exception_name parameters * this); \
    229                 _CLOSE
     137        }
    230138
    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)
    245180
    246181#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  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Jun 19 16:24:54 2020
    13 // Update Count     : 384
    14 //
    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
    1717
    1818#include <stdio.h>                                                                              // vfprintf, vfscanf
    1919#include <stdlib.h>                                                                             // exit
    2020#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
    2422#include <assert.h>
    2523#include <errno.h>                                                                              // errno
    2624
    27 
    2825// *********************************** ofstream ***********************************
    2926
     
    3229
    3330void ?{}( 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 ) );
    4139        sepSet( os, " " );
    4240        sepSetTuple( os, ", " );
     
    4442
    4543// 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; }
     44bool sepPrt$( ofstream & os ) { setNL$( os, false ); return os.sepOnOff$; }
     45void sepReset$( ofstream & os ) { os.sepOnOff$ = os.sepDefault$; }
     46void sepReset$( ofstream & os, bool reset ) { os.sepDefault$ = reset; os.sepOnOff$ = os.sepDefault$; }
     47const char * sepGetCur$( ofstream & os ) { return os.sepCur$; }
     48void sepSetCur$( ofstream & os, const char sepCur[] ) { os.sepCur$ = sepCur; }
     49bool getNL$( ofstream & os ) { return os.sawNL$; }
     50void setNL$( ofstream & os, bool state ) { os.sawNL$ = state; }
     51bool getANL$( ofstream & os ) { return os.nlOnOff$; }
     52bool getPrt$( ofstream & os ) { return os.prt$; }
     53void setPrt$( ofstream & os, bool state ) { os.prt$ = state; }
    5654
    5755// public
    58 void ?{}( ofstream & os ) { os.$file = 0p; }
     56void ?{}( ofstream & os ) { os.file$ = 0p; }
    5957
    6058void ?{}( ofstream & os, const char name[], const char mode[] ) {
     
    7068} // ^?{}
    7169
    72 void sepOn( ofstream & os ) { os.$sepOnOff = ! $getNL( os ); }
    73 void sepOff( ofstream & os ) { os.$sepOnOff = false; }
     70void sepOn( ofstream & os ) { os.sepOnOff$ = ! getNL$( os ); }
     71void sepOff( ofstream & os ) { os.sepOnOff$ = false; }
    7472
    7573bool 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 );
    7977        return temp;
    8078} // sepDisable
    8179
    8280bool 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 ?
    8684        return temp;
    8785} // sepEnable
    8886
    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; }
     87void nlOn( ofstream & os ) { os.nlOnOff$ = true; }
     88void nlOff( ofstream & os ) { os.nlOnOff$ = false; }
     89
     90const char * sepGet( ofstream & os ) { return os.separator$; }
    9391void sepSet( ofstream & os, const char s[] ) {
    9492        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';
    9795} // sepSet
    9896
    99 const char * sepGetTuple( ofstream & os ) { return os.$tupleSeparator; }
     97const char * sepGetTuple( ofstream & os ) { return os.tupleSeparator$; }
    10098void sepSetTuple( ofstream & os, const char s[] ) {
    10199        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';
    104102} // sepSet
    105103
    106104void ends( ofstream & os ) {
    107         if ( $getANL( os ) ) nl( os );
    108         else $setPrt( os, false );                                                      // turn off
     105        if ( getANL$( os ) ) nl( os );
     106        else setPrt$( os, false );                                                      // turn off
    109107        if ( &os == &exit ) exit( EXIT_FAILURE );
    110108        if ( &os == &abort ) abort();
     109        if ( os.acquired$ ) { os.acquired$ = false; release( os ); }
    111110} // ends
    112111
    113 int fail( ofstream & os ) {
    114         return os.$file == 0 || ferror( (FILE *)(os.$file) );
     112bool fail( ofstream & os ) {
     113        return os.file$ == 0 || ferror( (FILE *)(os.file$) );
    115114} // fail
    116115
    117116int flush( ofstream & os ) {
    118         return fflush( (FILE *)(os.$file) );
     117        return fflush( (FILE *)(os.file$) );
    119118} // flush
    120119
     
    135134
    136135void 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 ) {
    141140                abort | IO_MSG "close output" | nl | strerror( errno );
    142141        } // if
    143         os.$file = 0p;
     142        os.file$ = 0p;
    144143} // close
    145144
     
    149148        } // if
    150149
    151         if ( fwrite( data, 1, size, (FILE *)(os.$file) ) != size ) {
     150        if ( fwrite( data, 1, size, (FILE *)(os.file$) ) != size ) {
    152151                abort | IO_MSG "write" | nl | strerror( errno );
    153152        } // if
     
    158157        va_list args;
    159158        va_start( args, format );
    160         int len = vfprintf( (FILE *)(os.$file), format, args );
     159        int len = vfprintf( (FILE *)(os.file$), format, args );
    161160        if ( len == EOF ) {
    162                 if ( ferror( (FILE *)(os.$file) ) ) {
     161                if ( ferror( (FILE *)(os.file$) ) ) {
    163162                        abort | IO_MSG "invalid write";
    164163                } // if
     
    166165        va_end( args );
    167166
    168         $setPrt( os, true );                                                            // called in output cascade
    169         $sepReset( os );                                                                        // reset separator
     167        setPrt$( os, true );                                                            // called in output cascade
     168        sepReset$( os );                                                                        // reset separator
    170169        return len;
    171170} // fmt
     171
     172inline void acquire( ofstream & os ) {
     173        lock( os.lock$ );
     174        if ( ! os.acquired$ ) os.acquired$ = true;
     175        else unlock( os.lock$ );
     176} // acquire
     177
     178inline void release( ofstream & os ) {
     179        unlock( os.lock$ );
     180} // release
     181
     182void ?{}( osacquire & acq, ofstream & os ) { &acq.os = &os; lock( os.lock$ ); }
     183void ^?{}( osacquire & acq ) { release( acq.os ); }
    172184
    173185static ofstream soutFile = { (FILE *)stdout };
     
    176188ofstream & serr = serrFile, & stderr = serrFile;
    177189
     190static ofstream lsoutFile = { (FILE *)stdout };
     191ofstream & lsout = lsoutFile;
     192
    178193static ofstream exitFile = { (FILE *)stdout };
    179194ofstream & exit = exitFile;
     
    181196ofstream & abort = abortFile;
    182197
     198ofstream & 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
    183208
    184209// *********************************** ifstream ***********************************
     
    187212// private
    188213void ?{}( ifstream & is, void * file ) {
    189         is.$file = file;
    190         is.$nlOnOff = false;
     214        is.file$ = file;
     215        is.nlOnOff$ = false;
     216        is.acquired$ = false;
    191217} // ?{}
    192218
    193219// public
    194 void ?{}( ifstream & is ) { is.$file = 0p; }
     220void ?{}( ifstream & is ) { is.file$ = 0p; }
    195221
    196222void ?{}( ifstream & is, const char name[], const char mode[] ) {
     
    206232} // ^?{}
    207233
    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 int fail( ifstream & is ) {
    213         return is.$file == 0p || ferror( (FILE *)(is.$file) );
     234void nlOn( ifstream & os ) { os.nlOnOff$ = true; }
     235void nlOff( ifstream & os ) { os.nlOnOff$ = false; }
     236bool getANL( ifstream & os ) { return os.nlOnOff$; }
     237
     238bool fail( ifstream & is ) {
     239        return is.file$ == 0p || ferror( (FILE *)(is.file$) );
    214240} // fail
    215241
     242void ends( ifstream & is ) {
     243        if ( is.acquired$ ) { is.acquired$ = false; release( is ); }
     244} // ends
     245
    216246int eof( ifstream & is ) {
    217         return feof( (FILE *)(is.$file) );
     247        return feof( (FILE *)(is.file$) );
    218248} // eof
    219249
     
    226256        } // if
    227257        #endif // __CFA_DEBUG__
    228         is.$file = file;
     258        is.file$ = file;
    229259} // open
    230260
     
    234264
    235265void 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 ) {
    240270                abort | IO_MSG "close input" | nl | strerror( errno );
    241271        } // if
    242         is.$file = 0p;
     272        is.file$ = 0p;
    243273} // close
    244274
     
    248278        } // if
    249279
    250         if ( fread( data, size, 1, (FILE *)(is.$file) ) == 0 ) {
     280        if ( fread( data, size, 1, (FILE *)(is.file$) ) == 0 ) {
    251281                abort | IO_MSG "read" | nl | strerror( errno );
    252282        } // if
     
    259289        } // if
    260290
    261         if ( ungetc( c, (FILE *)(is.$file) ) == EOF ) {
     291        if ( ungetc( c, (FILE *)(is.file$) ) == EOF ) {
    262292                abort | IO_MSG "ungetc" | nl | strerror( errno );
    263293        } // if
     
    269299
    270300        va_start( args, format );
    271         int len = vfscanf( (FILE *)(is.$file), format, args );
     301        int len = vfscanf( (FILE *)(is.file$), format, args );
    272302        if ( len == EOF ) {
    273                 if ( ferror( (FILE *)(is.$file) ) ) {
     303                if ( ferror( (FILE *)(is.file$) ) ) {
    274304                        abort | IO_MSG "invalid read";
    275305                } // if
     
    279309} // fmt
    280310
     311inline void acquire( ifstream & is ) {
     312        lock( is.lock$ );
     313        if ( ! is.acquired$ ) is.acquired$ = true;
     314        else unlock( is.lock$ );
     315} // acquire
     316
     317inline void release( ifstream & is ) {
     318        unlock( is.lock$ );
     319} // release
     320
     321void ?{}( isacquire & acq, ifstream & is ) { &acq.is = &is; lock( is.lock$ ); }
     322void ^?{}( isacquire & acq ) { release( acq.is ); }
     323
    281324static ifstream sinFile = { (FILE *)stdin };
    282325ifstream & sin = sinFile, & stdin = sinFile;
     
    286329
    287330
     331EHM_VIRTUAL_TABLE(Open_Failure, Open_Failure_main_table);
    288332void ?{}( Open_Failure & this, ofstream & ostream ) {
    289         VTABLE_INIT(this, Open_Failure);
     333        this.virtual_table = &Open_Failure_main_table;
    290334        this.ostream = &ostream;
    291335        this.tag = 1;
    292336}
    293337void ?{}( Open_Failure & this, ifstream & istream ) {
    294         VTABLE_INIT(this, Open_Failure);
     338        this.virtual_table = &Open_Failure_main_table;
    295339        this.istream = &istream;
    296340        this.tag = 0;
    297341}
    298 const char * Open_Failure_msg(Open_Failure * this) {
    299         return "Open_Failure";
    300 }
    301 VTABLE_INSTANCE(Open_Failure)(Open_Failure_msg);
    302342void throwOpen_Failure( ofstream & ostream ) {
    303343        Open_Failure exc = { ostream };
  • libcfa/src/fstream.hfa

    rfeacef9 r5407cdc  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Jun 19 16:29:17 2020
    13 // Update Count     : 189
     12// Last Modified On : Tue Apr 27 22:00:30 2021
     13// Update Count     : 226
    1414//
    1515
    1616#pragma once
    1717
    18 #include "bits/weakso_locks.hfa"
     18#include "bits/weakso_locks.hfa"                                                // mutex_lock
    1919#include "iostream.hfa"
    2020#include <exception.hfa>
     
    2424
    2525
    26 enum { sepSize = 16 };
     26enum { ofstream_sepSize = 16 };
    2727struct 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$;
    3839}; // ofstream
    3940
     41// Satisfies ostream
     42
    4043// 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 );
     44bool sepPrt$( ofstream & );
     45void sepReset$( ofstream & );
     46void sepReset$( ofstream &, bool );
     47const char * sepGetCur$( ofstream & );
     48void sepSetCur$( ofstream &, const char [] );
     49bool getNL$( ofstream & );
     50void setNL$( ofstream &, bool );
     51bool getANL$( ofstream & );
     52bool getPrt$( ofstream & );
     53void setPrt$( ofstream &, bool );
    5154
    5255// public
     
    6366void sepSetTuple( ofstream &, const char [] );
    6467
    65 void ends( ofstream & os );
    66 int fail( ofstream & );
     68void ends( ofstream & );
     69int fmt( ofstream &, const char format[], ... ) __attribute__(( format(printf, 2, 3) ));
     70
     71bool fail( ofstream & );
    6772int flush( ofstream & );
    68 void open( ofstream &, const char name[], const char mode[] );
     73void open( ofstream &, const char name[], const char mode[] ); // FIX ME: use default = "w"
    6974void open( ofstream &, const char name[] );
    7075void close( ofstream & );
    7176ofstream & write( ofstream &, const char data[], size_t size );
    72 int fmt( ofstream &, const char format[], ... ) __attribute__(( format(printf, 2, 3) ));
    7377
    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 );
     78void acquire( ofstream & );
     79void release( ofstream & );
     80
     81struct osacquire {
     82        ofstream & os;
     83};
     84void ?{}( osacquire & acq, ofstream & );
     85void ^?{}( osacquire & acq );
     86
     87void ?{}( ofstream & );
     88void ?{}( ofstream &, const char name[], const char mode[] ); // FIX ME: use default = "w"
     89void ?{}( ofstream &, const char name[] );
     90void ^?{}( ofstream & );
     91
     92// private
     93static inline ofstream & nl$( ofstream & os ) { return nl( os ); } // remember basic_ostream nl
     94// public
     95ofstream & nl( ofstream & os );                                                 // override basic_ostream nl
    7896
    7997extern ofstream & sout, & stdout, & serr, & stderr;             // aliases
     
    85103
    86104struct ifstream {
    87         void * $file;
    88         bool $nlOnOff;
     105        void * file$;
     106        bool nlOnOff$;
     107        multiple_acquisition_lock lock$;
     108        bool acquired$;
    89109}; // ifstream
     110
     111// Satisfies istream
    90112
    91113// public
     
    93115void nlOff( ifstream & );
    94116bool getANL( ifstream & );
    95 int fail( ifstream & is );
     117void ends( ifstream & );
     118int fmt( ifstream &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) ));
     119
     120bool fail( ifstream & is );
    96121int eof( ifstream & is );
    97 void open( ifstream & is, const char name[], const char mode[] );
     122void open( ifstream & is, const char name[], const char mode[] ); // FIX ME: use default = "r"
    98123void open( ifstream & is, const char name[] );
    99124void close( ifstream & is );
    100125ifstream & read( ifstream & is, char * data, size_t size );
    101126ifstream & ungetc( ifstream & is, char c );
    102 int fmt( ifstream &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) ));
     127
     128void acquire( ifstream & is );
     129void release( ifstream & is );
     130
     131struct isacquire {
     132        ifstream & is;
     133};
     134void ?{}( isacquire & acq, ifstream & is );
     135void ^?{}( isacquire & acq );
    103136
    104137void ?{}( ifstream & is );
    105 void ?{}( ifstream & is, const char name[], const char mode[] );
     138void ?{}( ifstream & is, const char name[], const char mode[] ); // FIX ME: use default = "r"
    106139void ?{}( ifstream & is, const char name[] );
    107140void ^?{}( ifstream & is );
     
    113146
    114147
    115 DATA_EXCEPTION(Open_Failure)(
     148EHM_EXCEPTION(Open_Failure)(
    116149        union {
    117150                ofstream * ostream;
     
    122155);
    123156
    124 void ?{}( Open_Failure & this, ofstream & ostream );
    125 void ?{}( Open_Failure & this, ifstream & istream );
     157void ?{}( Open_Failure & this, ofstream & );
     158void ?{}( Open_Failure & this, ifstream & );
    126159
    127160// Local Variables: //
  • libcfa/src/gmp.hfa

    rfeacef9 r5407cdc  
    1010// Created On       : Tue Apr 19 08:43:43 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Feb  9 09:56:54 2020
    13 // Update Count     : 31
     12// Last Modified On : Tue Apr 20 20:59:21 2021
     13// Update Count     : 32
    1414//
    1515
     
    263263        forall( ostype & | ostream( ostype ) ) {
    264264                ostype & ?|?( ostype & os, Int mp ) {
    265                         if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     265                        if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
    266266                        gmp_printf( "%Zd", mp.mpz );
    267267                        sepOn( os );
  • libcfa/src/heap.cfa

    rfeacef9 r5407cdc  
    1010// Created On       : Tue Dec 19 21:58:35 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Jan 10 11:20:49 2021
    13 // Update Count     : 1031
     12// Last Modified On : Tue Apr 20 21:20:48 2021
     13// Update Count     : 1033
    1414//
    1515
     
    5252static bool prtFree = false;
    5353
    54 inline bool prtFree() {
     54bool prtFree() {
    5555        return prtFree;
    5656} // prtFree
     
    11281128
    11291129        // 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 ) {
    11311131          if ( unlikely( addr == 0p ) ) return libAlign();      // minimum alignment
    11321132                size_t ret;
     
    11391139                } // if
    11401140                return ret;
    1141         } // $malloc_alignment_set
     1141        } // malloc_alignment_set$
    11421142
    11431143
     
    11531153
    11541154        // 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 ) {
    11561156          if ( unlikely( addr == 0p ) ) return false;           // null allocation is not zero fill
    11571157                HeapManager.Storage.Header * header = headerAddr( addr );
     
    11621162                header->kind.real.blockSize |= 2;                               // mark as zero filled
    11631163                return ret;
    1164         } // $malloc_zero_fill_set
     1164        } // malloc_zero_fill_set$
    11651165
    11661166
     
    11761176
    11771177        // 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 ) {
    11791179          if ( unlikely( addr == 0p ) ) return 0;                       // null allocation has 0 size
    11801180                HeapManager.Storage.Header * header = headerAddr( addr );
     
    11851185                header->kind.real.size = size;
    11861186                return ret;
    1187         } // $malloc_size_set
     1187        } // malloc_size_set$
    11881188
    11891189
  • libcfa/src/iostream.cfa

    rfeacef9 r5407cdc  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Mon Aug 24 08:31:35 2020
    13 // Update Count     : 1130
     12// Last Modified On : Tue Apr 27 18:01:03 2021
     13// Update Count     : 1330
    1414//
    1515
     
    3636
    3737
    38 forall( ostype & | ostream( ostype ) ) {
     38forall( ostype & | basic_ostream( ostype ) ) {
    3939        ostype & ?|?( ostype & os, bool b ) {
    40                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     40                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
    4141                fmt( os, "%s", b ? "true" : "false" );
    4242                return os;
     
    4848        ostype & ?|?( ostype & os, char c ) {
    4949                fmt( os, "%c", c );
    50                 if ( c == '\n' ) $setNL( os, true );
     50                if ( c == '\n' ) setNL$( os, true );
    5151                return sepOff( os );
    5252        } // ?|?
     
    5656
    5757        ostype & ?|?( ostype & os, signed char sc ) {
    58                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     58                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
    5959                fmt( os, "%hhd", sc );
    6060                return os;
     
    6565
    6666        ostype & ?|?( ostype & os, unsigned char usc ) {
    67                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     67                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
    6868                fmt( os, "%hhu", usc );
    6969                return os;
     
    7474
    7575        ostype & ?|?( ostype & os, short int si ) {
    76                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     76                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
    7777                fmt( os, "%hd", si );
    7878                return os;
     
    8383
    8484        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 ) );
    8686                fmt( os, "%hu", usi );
    8787                return os;
     
    9292
    9393        ostype & ?|?( ostype & os, int i ) {
    94                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     94                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
    9595                fmt( os, "%d", i );
    9696                return os;
     
    101101
    102102        ostype & ?|?( ostype & os, unsigned int ui ) {
    103                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     103                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
    104104                fmt( os, "%u", ui );
    105105                return os;
     
    110110
    111111        ostype & ?|?( ostype & os, long int li ) {
    112                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     112                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
    113113                fmt( os, "%ld", li );
    114114                return os;
     
    119119
    120120        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 ) );
    122122                fmt( os, "%lu", uli );
    123123                return os;
     
    128128
    129129        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 ) );
    131131                fmt( os, "%lld", lli );
    132132                return os;
     
    137137
    138138        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 ) );
    140140                fmt( os, "%llu", ulli );
    141141                return os;
     
    145145        } // ?|?
    146146
    147 #if defined( __SIZEOF_INT128__ )
     147        #if defined( __SIZEOF_INT128__ )
    148148        //      UINT64_MAX 18_446_744_073_709_551_615_ULL
    149149        #define P10_UINT64 10_000_000_000_000_000_000_ULL       // 19 zeroes
    150150
    151151        static inline void base10_128( ostype & os, unsigned int128 val ) {
    152 #if defined(__GNUC__) && __GNUC_PREREQ(7,0)                             // gcc version >= 7
     152                #if defined(__GNUC__) && __GNUC_PREREQ(7,0)             // gcc version >= 7
    153153                if ( val > P10_UINT64 ) {
    154 #else
     154                #else
    155155                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)
    157157                        base10_128( os, val / P10_UINT64 );                     // recursive
    158158                        fmt( os, "%.19lu", (uint64_t)(val % P10_UINT64) );
     
    171171
    172172        ostype & ?|?( ostype & os, int128 llli ) {
    173                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     173                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
    174174                base10_128( os, llli );
    175175                return os;
     
    180180
    181181        ostype & ?|?( ostype & os, unsigned int128 ullli ) {
    182                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     182                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
    183183                base10_128( os, ullli );
    184184                return os;
     
    187187                (ostype &)(os | ullli); ends( os );
    188188        } // ?|?
    189 #endif // __SIZEOF_INT128__
     189        #endif // __SIZEOF_INT128__
    190190
    191191        #define PrintWithDP( os, format, val, ... ) \
     
    195195                        int len = snprintf( buf, size, format, ##__VA_ARGS__, val ); \
    196196                        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 */ \
    198198                                for ( int i = 0;; i += 1 ) { \
    199199                                        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 ? */ \
    201201                                } /* for */ \
    202202                        } /* if */ \
     
    204204
    205205        ostype & ?|?( ostype & os, float f ) {
    206                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     206                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
    207207                PrintWithDP( os, "%g", f );
    208208                return os;
     
    213213
    214214        ostype & ?|?( ostype & os, double d ) {
    215                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     215                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
    216216                PrintWithDP( os, "%.*lg", d, DBL_DIG );
    217217                return os;
     
    222222
    223223        ostype & ?|?( ostype & os, long double ld ) {
    224                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     224                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
    225225                PrintWithDP( os, "%.*Lg", ld, LDBL_DIG );
    226226                return os;
     
    231231
    232232        ostype & ?|?( ostype & os, float _Complex fc ) {
    233                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     233                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
    234234//              os | crealf( fc ) | nonl;
    235235                PrintWithDP( os, "%g", crealf( fc ) );
     
    243243
    244244        ostype & ?|?( ostype & os, double _Complex dc ) {
    245                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     245                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
    246246//              os | creal( dc ) | nonl;
    247247                PrintWithDP( os, "%.*lg", creal( dc ), DBL_DIG );
     
    255255
    256256        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 ) );
    258258//              os | creall( ldc ) || nonl;
    259259                PrintWithDP( os, "%.*Lg", creall( ldc ), LDBL_DIG );
     
    266266        } // ?|?
    267267
    268         ostype & ?|?( ostype & os, const char str[] ) {
     268        ostype & ?|?( ostype & os, const char s[] ) {
    269269                enum { Open = 1, Close, OpenClose };
    270270                static const unsigned char mask[256] @= {
     
    282282                }; // mask
    283283
    284           if ( str[0] == '\0' ) { sepOff( os ); return os; } // null string => no separator
     284          if ( s[0] == '\0' ) { sepOff( os ); return os; } // null string => no separator
    285285
    286286                // first character IS NOT spacing or closing punctuation => add left separator
    287                 unsigned char ch = str[0];                                              // must make unsigned
    288                 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 ) );
    290290                } // if
    291291
    292292                // if string starts line, must reset to determine open state because separator is off
    293                 $sepReset( os );                                                                // reset separator
     293                sepReset$( os );                                                                // reset separator
    294294
    295295                // 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 ) {
    299300                        sepOn( os );
    300301                } else {
    301302                        sepOff( os );
    302303                } // if
    303                 if ( ch == '\n' ) $setNL( os, true );                   // check *AFTER* $sepPrt call above as it resets NL flag
    304                 return write( os, str, len );
    305         } // ?|?
    306 
    307         void ?|?( ostype & os, const char str[] ) {
    308                 (ostype &)(os | str); ends( os );
    309         } // ?|?
    310 
    311 //      ostype & ?|?( ostype & os, const char16_t * str ) {
    312 //              if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
    313 //              fmt( os, "%ls", str );
     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 );
    314315//              return os;
    315316//      } // ?|?
    316317
    317318// #if ! ( __ARM_ARCH_ISA_ARM == 1 && __ARM_32BIT_STATE == 1 ) // char32_t == wchar_t => ambiguous
    318 //      ostype & ?|?( ostype & os, const char32_t * str ) {
    319 //              if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
    320 //              fmt( os, "%ls", str );
     319//      ostype & ?|?( ostype & os, const char32_t * s ) {
     320//              if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
     321//              fmt( os, "%ls", s );
    321322//              return os;
    322323//      } // ?|?
    323324// #endif // ! ( __ARM_ARCH_ISA_ARM == 1 && __ARM_32BIT_STATE == 1 )
    324325
    325 //      ostype & ?|?( ostype & os, const wchar_t * str ) {
    326 //              if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
    327 //              fmt( os, "%ls", str );
     326//      ostype & ?|?( ostype & os, const wchar_t * s ) {
     327//              if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
     328//              fmt( os, "%ls", s );
    328329//              return os;
    329330//      } // ?|?
    330331
    331332        ostype & ?|?( ostype & os, const void * p ) {
    332                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     333                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
    333334                fmt( os, "%p", p );
    334335                return os;
     
    340341        // manipulators
    341342        ostype & ?|?( ostype & os, ostype & (* manip)( ostype & ) ) {
    342                 (ostype &)(manip( os ));
    343                 return os;
     343                return manip( os );
    344344        } // ?|?
    345345        void ?|?( ostype & os, ostype & (* manip)( ostype & ) ) {
    346                 (ostype &)(manip( os ));
    347                 if ( $getPrt( os ) ) ends( os );                                // something printed ?
    348                 $setPrt( os, false );                                                   // turn off
     346                manip( os );
     347                if ( getPrt$( os ) ) ends( os );                                // something printed ?
     348                setPrt$( os, false );                                                   // turn off
    349349        } // ?|?
    350350
     
    359359        ostype & nl( ostype & os ) {
    360360                (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 );
    364363                return sepOff( os );                                                    // prepare for next line
    365364        } // nl
    366365
    367366        ostype & nonl( ostype & os ) {
    368                 $setPrt( os, false );                                                   // turn off
     367                setPrt$( os, false );                                                   // turn off
    369368                return os;
    370369        } // nonl
     
    399398                return os;
    400399        } // nlOff
     400} // distribution
     401
     402forall( ostype & | ostream( ostype ) ) {
     403        ostype & acquire( ostype & os ) {
     404                acquire( os );                                                                  // call void returning
     405                return os;
     406        } // acquire
    401407} // distribution
    402408
     
    405411        ostype & ?|?( ostype & os, T arg, Params rest ) {
    406412                (ostype &)(os | arg);                                                   // print first argument
    407                 $sepSetCur( os, sepGetTuple( os ) );                    // switch to tuple separator
     413                sepSetCur$( os, sepGetTuple( os ) );                    // switch to tuple separator
    408414                (ostype &)(os | rest);                                                  // print remaining arguments
    409                 $sepSetCur( os, sepGet( os ) );                                 // switch to regular separator
     415                sepSetCur$( os, sepGet( os ) );                                 // switch to regular separator
    410416                return os;
    411417        } // ?|?
     
    413419                // (ostype &)(?|?( os, arg, rest )); ends( os );
    414420                (ostype &)(os | arg);                                                   // print first argument
    415                 $sepSetCur( os, sepGetTuple( os ) );                    // switch to tuple separator
     421                sepSetCur$( os, sepGetTuple( os ) );                    // switch to tuple separator
    416422                (ostype &)(os | rest);                                                  // print remaining arguments
    417                 $sepSetCur( os, sepGet( os ) );                                 // switch to regular separator
     423                sepSetCur$( os, sepGet( os ) );                                 // switch to regular separator
    418424                ends( os );
    419425        } // ?|?
     
    442448// Default prefix for non-decimal prints is 0b, 0, 0x.
    443449#define IntegralFMTImpl( T, IFMTNP, IFMTP ) \
    444 forall( ostype & | ostream( ostype ) ) { \
     450forall( ostype & | basic_ostream( ostype ) ) { \
    445451        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 ) ); \
    447453\
    448454                if ( f.base == 'b' || f.base == 'B' ) {                 /* bespoke binary format */ \
     
    517523                return os; \
    518524        } /* ?|? */ \
    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
     530IntegralFMTImpl( signed char, "     *hh ", "     *.*hh " )
     531IntegralFMTImpl( unsigned char, "     *hh ", "     *.*hh " )
     532IntegralFMTImpl( signed short int, "     *h ", "     *.*h " )
     533IntegralFMTImpl( unsigned short int, "     *h ", "     *.*h " )
     534IntegralFMTImpl( signed int, "     * ", "     *.* " )
     535IntegralFMTImpl( unsigned int, "     * ", "     *.* " )
     536IntegralFMTImpl( signed long int, "     *l ", "     *.*l " )
     537IntegralFMTImpl( unsigned long int, "     *l ", "     *.*l " )
     538IntegralFMTImpl( signed long long int, "     *ll ", "     *.*ll " )
     539IntegralFMTImpl( unsigned long long int, "     *ll ", "     *.*ll " )
     540
     541
    534542#if defined( __SIZEOF_INT128__ )
    535543// 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 ) )
     544forall( ostype & | basic_ostream( ostype ) )
    657545static 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 ) {
    658546        int wd = 1;                                                                                     // f.wd is never 0 because 0 implies left-pad
     
    719607
    720608#define IntegralFMTImpl128( T ) \
    721 forall( ostype & | ostream( ostype ) ) { \
     609forall( ostype & | basic_ostream( ostype ) ) { \
    722610        ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \
    723611                _Ostream_Manip(uint64_t) fmt; \
     
    741629IntegralFMTImpl128( unsigned int128 )
    742630#endif // __SIZEOF_INT128__
    743 #endif // 0
    744631
    745632// *********************************** floating point ***********************************
    746633
    747 #define PrintWithDP2( os, format, val, ... ) \
     634static 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, ... ) \
    748642        { \
    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 */ \
    762656                                } /* if */ \
    763657                        } /* 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] = ' '; \
    764677                } /* if */ \
    765678                fmt( os, "%s", &buf[bufbeg] ); \
     
    767680
    768681#define FloatingPointFMTImpl( T, DFMTNP, DFMTP ) \
    769 forall( ostype & | ostream( ostype ) ) { \
     682forall( 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\
    770691        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' */ \
    773698                if ( ! f.flags.pc ) memcpy( &fmtstr, DFMTNP, sizeof(DFMTNP) ); \
    774699                else memcpy( &fmtstr, DFMTP, sizeof(DFMTP) ); \
     
    784709                        fmtstr[sizeof(DFMTNP)-2] = f.base;                      /* sizeof includes '\0' */ \
    785710                        /* 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 ) \
    787712                } else {                                                                                /* precision */ \
    788713                        fmtstr[sizeof(DFMTP)-2] = f.base;                       /* sizeof includes '\0' */ \
    789714                        /* 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 ) \
    791716                } /* if */ \
    792717                return os; \
     
    796721} // distribution
    797722
    798 FloatingPointFMTImpl( double, "%    * ", "%    *.* " )
    799 FloatingPointFMTImpl( long double, "%    *L ", "%    *.*L " )
     723FloatingPointFMTImpl( double, "     * ", "     *.* " )
     724FloatingPointFMTImpl( long double, "     *L ", "     *.*L " )
    800725
    801726// *********************************** character ***********************************
    802727
    803 forall( ostype & | ostream( ostype ) ) {
     728forall( ostype & | basic_ostream( ostype ) ) {
    804729        ostype & ?|?( ostype & os, _Ostream_Manip(char) f ) {
    805730                if ( f.base != 'c' ) {                                                  // bespoke binary/octal/hex format
     
    812737                } // if
    813738
    814                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     739                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
    815740
    816741                #define CFMTNP "% * "
     
    834759// *********************************** C string ***********************************
    835760
    836 forall( ostype & | ostream( ostype ) ) {
     761forall( ostype & | basic_ostream( ostype ) ) {
    837762        ostype & ?|?( ostype & os, _Ostream_Manip(const char *) f ) {
    838763                if ( ! f.val ) return os;                                               // null pointer ?
     
    850775                } // if
    851776
    852                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     777                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
    853778
    854779                #define SFMTNP "% * "
     
    882807
    883808
    884 forall( istype & | istream( istype ) ) {
     809forall( istype & | basic_istream( istype ) ) {
    885810        istype & ?|?( istype & is, bool & b ) {
    886811                char val[6];
     
    894819                return is;
    895820        } // ?|?
     821        void ?|?( istype & is, bool & b ) {
     822                (istype &)(is | b); ends( is );
     823        } // ?|?
    896824
    897825        istype & ?|?( istype & is, char & c ) {
     
    905833                return is;
    906834        } // ?|?
     835        void ?|?( istype & is, char & c ) {
     836                (istype &)(is | c); ends( is );
     837        } // ?|?
    907838
    908839        istype & ?|?( istype & is, signed char & sc ) {
     
    910841                return is;
    911842        } // ?|?
     843        void ?|?( istype & is, signed char & sc ) {
     844                (istype &)(is | sc); ends( is );
     845        } // ?|?
    912846
    913847        istype & ?|?( istype & is, unsigned char & usc ) {
     
    915849                return is;
    916850        } // ?|?
     851        void ?|?( istype & is, unsigned char & usc ) {
     852                (istype &)(is | usc); ends( is );
     853        } // ?|?
    917854
    918855        istype & ?|?( istype & is, short int & si ) {
     
    920857                return is;
    921858        } // ?|?
     859        void ?|?( istype & is, short int & si ) {
     860                (istype &)(is | si); ends( is );
     861        } // ?|?
    922862
    923863        istype & ?|?( istype & is, unsigned short int & usi ) {
     
    925865                return is;
    926866        } // ?|?
     867        void ?|?( istype & is, unsigned short int & usi ) {
     868                (istype &)(is | usi); ends( is );
     869        } // ?|?
    927870
    928871        istype & ?|?( istype & is, int & i ) {
     
    930873                return is;
    931874        } // ?|?
     875        void ?|?( istype & is, int & i ) {
     876                (istype &)(is | i); ends( is );
     877        } // ?|?
    932878
    933879        istype & ?|?( istype & is, unsigned int & ui ) {
     
    935881                return is;
    936882        } // ?|?
     883        void ?|?( istype & is, unsigned int & ui ) {
     884                (istype &)(is | ui); ends( is );
     885        } // ?|?
    937886
    938887        istype & ?|?( istype & is, long int & li ) {
     
    940889                return is;
    941890        } // ?|?
     891        void ?|?( istype & is, long int & li ) {
     892                (istype &)(is | li); ends( is );
     893        } // ?|?
    942894
    943895        istype & ?|?( istype & is, unsigned long int & ulli ) {
     
    945897                return is;
    946898        } // ?|?
     899        void ?|?( istype & is, unsigned long int & ulli ) {
     900                (istype &)(is | ulli); ends( is );
     901        } // ?|?
    947902
    948903        istype & ?|?( istype & is, long long int & lli ) {
     
    950905                return is;
    951906        } // ?|?
     907        void ?|?( istype & is, long long int & lli ) {
     908                (istype &)(is | lli); ends( is );
     909        } // ?|?
    952910
    953911        istype & ?|?( istype & is, unsigned long long int & ulli ) {
     
    955913                return is;
    956914        } // ?|?
    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 ) {
    964928                char s[40];
    965929                bool sign = false;
     
    968932                // If the input is too large, the value returned is undefined. If there is no input, no value is returned
    969933                if ( fmt( is, "%39[0-9]%*[0-9]", s ) == 1 ) {   // take first 39 characters, ignore remaining
    970                         ui128 = 0;
     934                        ullli = 0;
    971935                        for ( unsigned int i = 0; s[i] != '\0'; i += 1 ) {
    972                                 ui128 = ui128 * 10 + s[i] - '0';
     936                                ullli = ullli * 10 + s[i] - '0';
    973937                        } // for
    974                         if ( sign ) ui128 = -ui128;
     938                        if ( sign ) ullli = -ullli;
    975939                } else if ( sign ) ungetc( is, '-' );                   // return minus when no digits
    976940                return is;
    977941        } // ?|?
    978 #endif // __SIZEOF_INT128__
     942        void ?|?( istype & is, unsigned int128 & ullli ) {
     943                (istype &)(is | ullli); ends( is );
     944        } // ?|?
     945        #endif // __SIZEOF_INT128__
    979946
    980947        istype & ?|?( istype & is, float & f ) {
     
    982949                return is;
    983950        } // ?|?
     951        void ?|?( istype & is, float & f ) {
     952                (istype &)(is | f); ends( is );
     953        } // ?|?
    984954
    985955        istype & ?|?( istype & is, double & d ) {
     
    987957                return is;
    988958        } // ?|?
     959        void ?|?( istype & is, double & d ) {
     960                (istype &)(is | d); ends( is );
     961        } // ?|?
    989962
    990963        istype & ?|?( istype & is, long double & ld ) {
     
    992965                return is;
    993966        } // ?|?
    994 
     967        void ?|?( istype & is, long double & ld ) {
     968                (istype &)(is | ld); ends( is );
     969        } // ?|?
    995970
    996971        istype & ?|?( istype & is, float _Complex & fc ) {
     
    1000975                return is;
    1001976        } // ?|?
     977        void ?|?( istype & is, float _Complex & fc ) {
     978                (istype &)(is | fc); ends( is );
     979        } // ?|?
    1002980
    1003981        istype & ?|?( istype & is, double _Complex & dc ) {
     
    1007985                return is;
    1008986        } // ?|?
     987        void ?|?( istype & is, double _Complex & dc ) {
     988                (istype &)(is | dc); ends( is );
     989        } // ?|?
    1009990
    1010991        istype & ?|?( istype & is, long double _Complex & ldc ) {
     
    1014995                return is;
    1015996        } // ?|?
     997        void ?|?( istype & is, long double _Complex & ldc ) {
     998                (istype &)(is | ldc); ends( is );
     999        } // ?|?
    10161000
    10171001        // istype & ?|?( istype & is, const char fmt[] ) {
     
    10201004        // } // ?|?
    10211005
    1022         istype & ?|?( istype & is, char * s ) {
     1006        istype & ?|?( istype & is, char s[] ) {
    10231007                fmt( is, "%s", s );
    10241008                return is;
     1009        } // ?|?
     1010        void ?|?( istype & is, char s[] ) {
     1011                (istype &)(is | s); ends( is );
    10251012        } // ?|?
    10261013
     
    10291016                return manip( is );
    10301017        } // ?|?
     1018        void ?|?( istype & is, istype & (* manip)( istype & ) ) {
     1019                manip( is ); ends( is );
     1020        } // ?|?
    10311021
    10321022        istype & nl( istype & is ) {
     
    10461036} // distribution
    10471037
     1038forall( istype & | istream( istype ) ) {
     1039        istype & acquire( istype & is ) {
     1040                acquire( is );                                                                  // call void returning
     1041                return is;
     1042        } // acquire
     1043} // distribution
     1044
    10481045// *********************************** manipulators ***********************************
    10491046
    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 );
     1047forall( 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 );
    10701078                fmt( is, fmtstr, f.s );
    10711079                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
    10901093
    10911094#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 } // ?|?
     1095forall( 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
    11051112
    11061113InputFMTImpl( signed char, "hhi" )
     
    11191126InputFMTImpl( long double, "Lf" )
    11201127
    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 } // ?|?
     1128forall( 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
    11531168
    11541169// Local Variables: //
  • libcfa/src/iostream.hfa

    rfeacef9 r5407cdc  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Aug 11 22:16:14 2020
    13 // Update Count     : 350
     12// Last Modified On : Tue Apr 27 17:59:21 2021
     13// Update Count     : 398
    1414//
    1515
     
    2222
    2323
    24 trait ostream( ostype & ) {
     24trait basic_ostream( ostype & ) {
    2525        // private
    26         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
     26        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
    3636        // public
    3737        void sepOn( ostype & );                                                         // turn separator state on
     
    4747        void sepSetTuple( ostype &, const char [] );            // set tuple separator to string (15 character maximum)
    4848
    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       
     53trait ostream( ostype & | basic_ostream( ostype ) ) {
    5154        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 & );
    5458        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
    5660}; // ostream
    5761
     
    6670// implement writable for intrinsic types
    6771
    68 forall( ostype & | ostream( ostype ) ) {
     72forall( ostype & | basic_ostream( ostype ) ) {
    6973        ostype & ?|?( ostype &, bool );
    7074        void ?|?( ostype &, bool );
     
    9397        ostype & ?|?( ostype &, unsigned long long int );
    9498        void ?|?( ostype &, unsigned long long int );
    95 #if defined( __SIZEOF_INT128__ )
     99        #if defined( __SIZEOF_INT128__ )
    96100        ostype & ?|?( ostype &, int128 );
    97101        void ?|?( ostype &, int128 );
    98102        ostype & ?|?( ostype &, unsigned int128 );
    99103        void ?|?( ostype &, unsigned int128 );
    100 #endif // __SIZEOF_INT128__
     104        #endif // __SIZEOF_INT128__
    101105
    102106        ostype & ?|?( ostype &, float );
     
    117121        void ?|?( ostype &, const char [] );
    118122        // ostype & ?|?( ostype &, const char16_t * );
    119 #if ! ( __ARM_ARCH_ISA_ARM == 1 && __ARM_32BIT_STATE == 1 ) // char32_t == wchar_t => ambiguous
     123        #if ! ( __ARM_ARCH_ISA_ARM == 1 && __ARM_32BIT_STATE == 1 ) // char32_t == wchar_t => ambiguous
    120124        // 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 )
    122126        // ostype & ?|?( ostype &, const wchar_t * );
    123127        ostype & ?|?( ostype &, const void * );
     
    139143} // distribution
    140144
     145forall( ostype & | ostream( ostype ) ) {
     146        ostype & acquire( ostype & );
     147} // distribution
     148
    141149// tuples
    142150forall( ostype &, T, Params... | writeable( T, ostype ) | { ostype & ?|?( ostype &, Params ); } ) {
     
    156164struct _Ostream_Manip {
    157165        T val;                                                                                          // polymorphic base-type
    158         unsigned int wd, pc;                                                            // width, precision
     166        int wd, pc;                                                                                     // width, precision: signed for computations
    159167        char base;                                                                                      // numeric base / floating-point style
    160168        union {
    161169                unsigned char all;
    162170                struct {
     171                        unsigned char eng:1;                                            // engineering notation
    163172                        unsigned char neg:1;                                            // val is negative
    164173                        unsigned char pc:1;                                                     // precision specified
     
    183192        _Ostream_Manip(T) hex( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'x', { .all : 0 } }; } \
    184193        _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 char pc, 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 } }; } \
    186195        _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 char pc, _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; } \
    188197        _Ostream_Manip(T) & left( _Ostream_Manip(T) & fmt ) { fmt.flags.left = true; return fmt; } \
    189198        _Ostream_Manip(T) & upcase( _Ostream_Manip(T) & fmt ) { if ( fmt.base == 'x' || fmt.base == 'b' ) fmt.base -= 32; /* upper case */ return fmt; } \
     
    193202        _Ostream_Manip(T) & sign( _Ostream_Manip(T) & fmt ) { fmt.flags.sign = true; return fmt; } \
    194203} /* distribution */ \
    195 forall( ostype & | ostream( ostype ) ) { \
     204forall( ostype & | basic_ostream( ostype ) ) { \
    196205        ostype & ?|?( ostype & os, _Ostream_Manip(T) f ); \
    197206        void ?|?( ostype & os, _Ostream_Manip(T) f ); \
     
    220229        _Ostream_Manip(T) hex( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'a', { .all : 0 } }; } \
    221230        _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; } \
    227238        _Ostream_Manip(T) & left( _Ostream_Manip(T) & fmt ) { fmt.flags.left = true; return fmt; } \
    228239        _Ostream_Manip(T) upcase( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'G', { .all : 0 } }; } \
     
    233244        _Ostream_Manip(T) nodp( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'g', { .flags.nobsdp : true } }; } \
    234245        _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; } \
    235248} /* distribution */ \
    236 forall( ostype & | ostream( ostype ) ) { \
     249forall( ostype & | basic_ostream( ostype ) ) { \
    237250        ostype & ?|?( ostype & os, _Ostream_Manip(T) f ); \
    238251        void ?|?( ostype & os, _Ostream_Manip(T) f ); \
     
    254267        _Ostream_Manip(char) & nobase( _Ostream_Manip(char) & fmt ) { fmt.flags.nobsdp = true; return fmt; }
    255268} // distribution
    256 forall( ostype & | ostream( ostype ) ) {
     269forall( ostype & | basic_ostream( ostype ) ) {
    257270        ostype & ?|?( ostype & os, _Ostream_Manip(char) f );
    258271        void ?|?( ostype & os, _Ostream_Manip(char) f );
     
    266279        _Ostream_Manip(const char *) hex( const char s[] ) { return (_Ostream_Manip(const char *))@{ s, 1, 0, 'x', { .all : 0 } }; }
    267280        _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 char pc, 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 } }; }
    269282        _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 char pc, _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; }
    271284        _Ostream_Manip(const char *) & left( _Ostream_Manip(const char *) & fmt ) { fmt.flags.left = true; return fmt; }
    272285        _Ostream_Manip(const char *) & nobase( _Ostream_Manip(const char *) & fmt ) { fmt.flags.nobsdp = true; return fmt; }
    273286} // distribution
    274 forall( ostype & | ostream( ostype ) ) {
     287forall( ostype & | basic_ostream( ostype ) ) {
    275288        ostype & ?|?( ostype & os, _Ostream_Manip(const char *) f );
    276289        void ?|?( ostype & os, _Ostream_Manip(const char *) f );
     
    281294
    282295
    283 trait istream( istype & ) {
     296trait basic_istream( istype & ) {
     297        bool getANL( istype & );                                                        // get scan newline (on/off)
    284298        void nlOn( istype & );                                                          // read newline
    285299        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 );
    288304        int eof( istype & );
     305}; // basic_istream
     306
     307trait istream( istype & | basic_istream( istype ) ) {
     308        bool fail( istype & );
    289309        void open( istype & is, const char name[] );
    290310        void close( istype & is );
    291311        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
    294313}; // istream
    295314
     
    298317}; // readable
    299318
    300 forall( istype & | istream( istype ) ) {
     319forall( istype & | basic_istream( istype ) ) {
    301320        istype & ?|?( istype &, bool & );
     321        void ?|?( istype &, bool & );
    302322
    303323        istype & ?|?( istype &, char & );
     324        void ?|?( istype &, char & );
    304325        istype & ?|?( istype &, signed char & );
     326        void ?|?( istype &, signed char & );
    305327        istype & ?|?( istype &, unsigned char & );
     328        void ?|?( istype &, unsigned char & );
    306329
    307330        istype & ?|?( istype &, short int & );
     331        void ?|?( istype &, short int & );
    308332        istype & ?|?( istype &, unsigned short int & );
     333        void ?|?( istype &, unsigned short int & );
    309334        istype & ?|?( istype &, int & );
     335        void ?|?( istype &, int & );
    310336        istype & ?|?( istype &, unsigned int & );
     337        void ?|?( istype &, unsigned int & );
    311338        istype & ?|?( istype &, long int & );
     339        void ?|?( istype &, long int & );
    312340        istype & ?|?( istype &, unsigned long int & );
     341        void ?|?( istype &, unsigned long int & );
    313342        istype & ?|?( istype &, long long int & );
     343        void ?|?( istype &, long long int & );
    314344        istype & ?|?( istype &, unsigned long long int & );
    315 #if defined( __SIZEOF_INT128__ )
     345        void ?|?( istype &, unsigned long long int & );
     346        #if defined( __SIZEOF_INT128__ )
    316347        istype & ?|?( istype &, int128 & );
     348        void ?|?( istype &, int128 & );
    317349        istype & ?|?( istype &, unsigned int128 & );
    318 #endif // __SIZEOF_INT128__
     350        void ?|?( istype &, unsigned int128 & );
     351        #endif // __SIZEOF_INT128__
    319352
    320353        istype & ?|?( istype &, float & );
     354        void ?|?( istype &, float & );
    321355        istype & ?|?( istype &, double & );
     356        void ?|?( istype &, double & );
    322357        istype & ?|?( istype &, long double & );
     358        void ?|?( istype &, long double & );
    323359
    324360        istype & ?|?( istype &, float _Complex & );
     361        void ?|?( istype &, float _Complex & );
    325362        istype & ?|?( istype &, double _Complex & );
     363        void ?|?( istype &, double _Complex & );
    326364        istype & ?|?( istype &, long double _Complex & );
     365        void ?|?( istype &, long double _Complex & );
    327366
    328367//      istype & ?|?( istype &, const char [] );
    329         istype & ?|?( istype &, char * );
     368        istype & ?|?( istype &, char [] );
     369        void ?|?( istype &, char [] );
    330370
    331371        // manipulators
    332372        istype & ?|?( istype &, istype & (*)( istype & ) );
     373        void ?|?( istype &, istype & (*)( istype & ) );
    333374        istype & nl( istype & is );
    334375        istype & nlOn( istype & );
    335376        istype & nlOff( istype & );
     377} // distribution
     378
     379forall( istype & | istream( istype ) ) {
     380        istype & acquire( istype & );
    336381} // distribution
    337382
     
    352397
    353398static inline {
     399        _Istream_Cstr skip( const char scanset[] ) { return (_Istream_Cstr){ 0p, scanset, -1, { .all : 0 } }; }
    354400        _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 } }; }
    356401        _Istream_Cstr incl( const char scanset[], char * s ) { return (_Istream_Cstr){ s, scanset, -1, { .flags.inex : false } }; }
    357402        _Istream_Cstr & incl( const char scanset[], _Istream_Cstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = false; return fmt; }
     
    363408        _Istream_Cstr & wdi( unsigned int w, _Istream_Cstr & fmt ) { fmt.wd = w; return fmt; }
    364409} // distribution
    365 forall( istype & | istream( istype ) ) istype & ?|?( istype & is, _Istream_Cstr f );
     410forall( istype & | basic_istream( istype ) ) {
     411        istype & ?|?( istype & is, _Istream_Cstr f );
     412        void ?|?( istype & is, _Istream_Cstr f );
     413}
    366414
    367415struct _Istream_Char {
     
    373421        _Istream_Char & ignore( _Istream_Char & fmt ) { fmt.ignore = true; return fmt; }
    374422} // distribution
    375 forall( istype & | istream( istype ) ) istype & ?|?( istype & is, _Istream_Char f );
     423forall( istype & | basic_istream( istype ) ) {
     424        istype & ?|?( istype & is, _Istream_Char f );
     425        void ?|?( istype & is, _Istream_Char f );
     426}
    376427
    377428forall( T & | sized( T ) )
     
    389440        _Istream_Manip(T) & wdi( unsigned int w, _Istream_Manip(T) & fmt ) { fmt.wd = w; return fmt; } \
    390441} /* distribution */ \
    391 forall( istype & | istream( istype ) ) { \
     442forall( istype & | basic_istream( istype ) ) { \
    392443        istype & ?|?( istype & is, _Istream_Manip(T) f ); \
     444        void ?|?( istype & is, _Istream_Manip(T) f ); \
    393445} // ?|?
    394446
  • libcfa/src/math.hfa

    rfeacef9 r5407cdc  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // math --
     7// math.hfa --
    88//
    99// Author           : Peter A. Buhr
    1010// Created On       : Mon Apr 18 23:37:04 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Mon Aug 24 08:56:20 2020
    13 // Update Count     : 126
     12// Last Modified On : Thu Apr 15 11:47:56 2021
     13// Update Count     : 132
    1414//
    1515
     
    100100        long double _Complex log( long double _Complex x ) { return clogl( x ); }
    101101
     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 ); }
    102107        float log2( float x ) { return log2f( x ); }
    103108        // extern "C" { double log2( double ); }
  • libcfa/src/startup.cfa

    rfeacef9 r5407cdc  
    3939
    4040    void disable_interrupts() __attribute__(( weak )) {}
    41     void enable_interrupts_noPoll() __attribute__(( weak )) {}
     41    void enable_interrupts() __attribute__(( weak )) {}
    4242} // extern "C"
    4343
  • libcfa/src/stdlib.hfa

    rfeacef9 r5407cdc  
    1010// Created On       : Thu Jan 28 17:12:35 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Jan 21 22:02:13 2021
    13 // Update Count     : 574
     12// Last Modified On : Tue Apr 20 21:20:03 2021
     13// Update Count     : 575
    1414//
    1515
     
    4444
    4545// Macro because of returns
    46 #define $ARRAY_ALLOC( allocation, alignment, dim ) \
     46#define ARRAY_ALLOC$( allocation, alignment, dim ) \
    4747        if ( _Alignof(T) <= libAlign() ) return (T *)(void *)allocation( dim, (size_t)sizeof(T) ); /* C allocation */ \
    4848        else return (T *)alignment( _Alignof(T), dim, sizeof(T) )
     
    5757
    5858        T * aalloc( size_t dim ) {
    59                 $ARRAY_ALLOC( aalloc, amemalign, dim );
     59                ARRAY_ALLOC$( aalloc, amemalign, dim );
    6060        } // aalloc
    6161
    6262        T * calloc( size_t dim ) {
    63                 $ARRAY_ALLOC( calloc, cmemalign, dim );
     63                ARRAY_ALLOC$( calloc, cmemalign, dim );
    6464        } // calloc
    6565
     
    119119                S_fill(T) ?`fill( T    a[], size_t nmemb )      { S_fill(T) ret = {'a', nmemb}; ret.fill.a = a; return ret; }
    120120
    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) {
     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) {
    123123                        T * ptr = NULL;
    124124                        size_t size = sizeof(T);
     
    145145
    146146                        return ptr;
    147                 } // $alloc_internal
     147                } // alloc_internal$
    148148*/
    149149
     
    175175        S_realloc(T)    ?`realloc ( T * a )                             { return (S_realloc(T)){a}; }
    176176
    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 ) {
    178178                T * ptr = NULL;
    179179                size_t size = sizeof(T);
     
    206206
    207207                return ptr;
    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);
     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);
    226226                }
    227227
    228228            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);
    230230            }
    231231
    232232            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);
    234234            }
    235235
  • libcfa/src/time.hfa

    rfeacef9 r5407cdc  
    1010// Created On       : Wed Mar 14 23:18:57 2018
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Jun 17 16:13:00 2020
    13 // Update Count     : 663
     12// Last Modified On : Wed Apr 21 06:32:31 2021
     13// Update Count     : 667
    1414//
    1515
     
    2828
    2929static 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
    3033        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        } // ?=?
    3142
    3243        Duration +?( Duration rhs ) with( rhs ) { return (Duration)@{ +tn }; }
     
    4960        Duration ?%?( Duration lhs, Duration rhs ) { return (Duration)@{ lhs.tn % rhs.tn }; }
    5061        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; }
    5169
    5270        bool ?==?( Duration lhs, Duration rhs ) { return lhs.tn == rhs.tn; }
     
    5674        bool ?>? ( Duration lhs, Duration rhs ) { return lhs.tn >  rhs.tn; }
    5775        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; }
    6576
    6677        Duration abs( Duration rhs ) { return rhs.tn >= 0 ? rhs : -rhs; }
     
    152163void ?{}( Time & time, int year, int month = 1, int day = 1, int hour = 0, int min = 0, int sec = 0, int64_t nsec = 0 );
    153164static 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
    154168        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; }
    157169        Time ?=?( Time & time, timeval t ) with( time ) {
    158170                tn = (int64_t)t.tv_sec * TIMEGRAN + t.tv_usec * (TIMEGRAN / 1_000_000LL);
    159171                return time;
    160172        } // ?=?
    161 
    162         void ?{}( Time & time, timespec t ) with( time ) { tn = (int64_t)t.tv_sec * TIMEGRAN + t.tv_nsec; }
    163173        Time ?=?( Time & time, timespec t ) with( time ) {
    164174                tn = (int64_t)t.tv_sec * TIMEGRAN + t.tv_nsec;
  • libcfa/src/virtual.c

    rfeacef9 r5407cdc  
    1515
    1616#include "virtual.h"
     17#include "assert.h"
    1718
    1819int __cfa__is_parent( struct __cfa__parent_vtable const * parent,
    1920        struct __cfa__parent_vtable const * child ) {
     21        assert( child );
    2022        do {
    2123                if ( parent == child )
     
    2830void * __cfa__virtual_cast( struct __cfa__parent_vtable const * parent,
    2931        struct __cfa__parent_vtable const * const * child ) {
     32        assert( child );
    3033        return (__cfa__is_parent(parent, *child)) ? (void *)child : (void *)0;
    3134}
Note: See TracChangeset for help on using the changeset viewer.