Ignore:
Timestamp:
Sep 21, 2022, 11:02:15 AM (22 months ago)
Author:
Thierry Delisle <tdelisle@…>
Branches:
ADT, ast-experimental, master, pthread-emulation
Children:
95dab9e
Parents:
428adbc (diff), 0bd46fd (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' into pthread-emulation

Location:
libcfa/src/concurrency/kernel
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • libcfa/src/concurrency/kernel/cluster.cfa

    r428adbc r7f6a7c9  
    9393//=======================================================================
    9494void  ?{}(__scheduler_RWLock_t & this) {
    95         this.max   = __max_processors();
    96         this.alloc = 0;
    97         this.ready = 0;
    98         this.data  = alloc(this.max);
    99         this.write_lock  = false;
    100 
    101         /*paranoid*/ verify(__atomic_is_lock_free(sizeof(this.alloc), &this.alloc));
    102         /*paranoid*/ verify(__atomic_is_lock_free(sizeof(this.ready), &this.ready));
     95        this.lock.max   = __max_processors();
     96        this.lock.alloc = 0;
     97        this.lock.ready = 0;
     98        this.lock.data  = alloc(this.lock.max);
     99        this.lock.write_lock  = false;
     100
     101        /*paranoid*/ verify(__atomic_is_lock_free(sizeof(this.lock.alloc), &this.lock.alloc));
     102        /*paranoid*/ verify(__atomic_is_lock_free(sizeof(this.lock.ready), &this.lock.ready));
    103103
    104104}
    105105void ^?{}(__scheduler_RWLock_t & this) {
    106         free(this.data);
     106        free(this.lock.data);
    107107}
    108108
     
    110110//=======================================================================
    111111// Lock-Free registering/unregistering of threads
    112 unsigned register_proc_id( void ) with(*__scheduler_lock) {
     112unsigned register_proc_id( void ) with(__scheduler_lock.lock) {
    113113        __kernel_rseq_register();
    114114
     
    132132        }
    133133
    134         if(max <= alloc) abort("Trying to create more than %ud processors", __scheduler_lock->max);
     134        if(max <= alloc) abort("Trying to create more than %ud processors", __scheduler_lock.lock.max);
    135135
    136136        // Step - 2 : F&A to get a new spot in the array.
    137137        uint_fast32_t n = __atomic_fetch_add(&alloc, 1, __ATOMIC_SEQ_CST);
    138         if(max <= n) abort("Trying to create more than %ud processors", __scheduler_lock->max);
     138        if(max <= n) abort("Trying to create more than %ud processors", __scheduler_lock.lock.max);
    139139
    140140        // Step - 3 : Mark space as used and then publish it.
     
    154154}
    155155
    156 void unregister_proc_id( unsigned id ) with(*__scheduler_lock) {
     156void unregister_proc_id( unsigned id ) with(__scheduler_lock.lock) {
    157157        /* paranoid */ verify(id < ready);
    158158        /* paranoid */ verify(id == kernelTLS().sched_id);
     
    169169// Writer side : acquire when changing the ready queue, e.g. adding more
    170170//  queues or removing them.
    171 uint_fast32_t ready_mutate_lock( void ) with(*__scheduler_lock) {
     171uint_fast32_t ready_mutate_lock( void ) with(__scheduler_lock.lock) {
    172172        /* paranoid */ verify( ! __preemption_enabled() );
    173173
     
    196196}
    197197
    198 void ready_mutate_unlock( uint_fast32_t last_s ) with(*__scheduler_lock) {
     198void ready_mutate_unlock( uint_fast32_t last_s ) with(__scheduler_lock.lock) {
    199199        /* paranoid */ verify( ! __preemption_enabled() );
    200200
     
    278278
    279279#if defined(CFA_HAVE_LINUX_IO_URING_H)
    280         static void assign_io($io_context ** data, size_t count, dlist(processor) & list) {
     280        static void assign_io(io_context$ ** data, size_t count, dlist(processor) & list) {
    281281                processor * it = &list`first;
    282282                while(it) {
  • libcfa/src/concurrency/kernel/cluster.hfa

    r428adbc r7f6a7c9  
    2424// Calc moving average based on existing average, before and current time.
    2525static inline unsigned long long moving_average(unsigned long long currtsc, unsigned long long instsc, unsigned long long old_avg) {
    26         /* paranoid */ verifyf( currtsc < 45000000000000000, "Suspiciously large current time: %'llu (%llx)\n", currtsc, currtsc );
    27         /* paranoid */ verifyf( instsc  < 45000000000000000, "Suspiciously large insert time: %'llu (%llx)\n", instsc, instsc );
    2826        /* paranoid */ verifyf( old_avg < 15000000000000, "Suspiciously large previous average: %'llu (%llx)\n", old_avg, old_avg );
    2927
     
    6563                }
    6664        }
    67         return (max + 2 * max) / 2;
     65        return 8 * max;
    6866}
    6967
  • libcfa/src/concurrency/kernel/fwd.hfa

    r428adbc r7f6a7c9  
    3535extern "C" {
    3636        extern "Cforall" {
    37                 extern __attribute__((aligned(64))) thread_local struct KernelThreadData {
     37                extern __attribute__((aligned(64))) __thread struct KernelThreadData {
    3838                        struct thread$          * volatile this_thread;
    3939                        struct processor        * volatile this_processor;
     
    179179                // Similar to a binary semaphore with a 'one shot' semantic
    180180                // is expected to be discarded after each party call their side
     181                enum(struct thread$ *) { oneshot_ARMED = 0p, oneshot_FULFILLED = 1p };
    181182                struct oneshot {
    182183                        // Internal state :
    183                         //     0p     : is initial state (wait will block)
    184                         //     1p     : fulfilled (wait won't block)
     184                        // armed      : initial state, wait will block
     185                        // fulfilled  : wait won't block
    185186                        // any thread : a thread is currently waiting
    186187                        struct thread$ * volatile ptr;
     
    189190                static inline {
    190191                        void  ?{}(oneshot & this) {
    191                                 this.ptr = 0p;
     192                                this.ptr = oneshot_ARMED;
    192193                        }
    193194
     
    199200                                for() {
    200201                                        struct thread$ * expected = this.ptr;
    201                                         if(expected == 1p) return false;
     202                                        if(expected == oneshot_FULFILLED) return false;
    202203                                        if(__atomic_compare_exchange_n(&this.ptr, &expected, active_thread(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
    203204                                                park();
    204                                                 /* paranoid */ verify( this.ptr == 1p );
     205                                                /* paranoid */ verify( this.ptr == oneshot_FULFILLED );
    205206                                                return true;
    206207                                        }
     
    211212                        // return true if a thread was unparked
    212213                        thread$ * post(oneshot & this, bool do_unpark = true) {
    213                                 struct thread$ * got = __atomic_exchange_n( &this.ptr, 1p, __ATOMIC_SEQ_CST);
    214                                 if( got == 0p || got == 1p ) return 0p;
     214                                struct thread$ * got = __atomic_exchange_n( &this.ptr, oneshot_FULFILLED, __ATOMIC_SEQ_CST);
     215                                if( got == oneshot_ARMED || got == oneshot_FULFILLED ) return 0p;
    215216                                if(do_unpark) unpark( got );
    216217                                return got;
     
    223224                // thread on "any of" [a given set of] futures.
    224225                // does not support multiple threads waiting on the same future
     226                enum(struct oneshot *) { future_ARMED = 0p, future_FULFILLED = 1p, future_PROGRESS = 2p, future_ABANDONED = 3p };
    225227                struct future_t {
    226228                        // Internal state :
    227                         //     0p      : is initial state (wait will block)
    228                         //     1p      : fulfilled (wait won't block)
    229                         //     2p      : in progress ()
    230                         //     3p      : abandoned, server should delete
     229                        // armed       : initial state, wait will block
     230                        // fulfilled   : result is ready, wait won't block
     231                        // progress    : someone else is in the process of fulfilling this
     232                        // abandoned   : client no longer cares, server should delete
    231233                        // any oneshot : a context has been setup to wait, a thread could wait on it
    232234                        struct oneshot * volatile ptr;
     
    235237                static inline {
    236238                        void  ?{}(future_t & this) {
    237                                 this.ptr = 0p;
     239                                this.ptr = future_ARMED;
    238240                        }
    239241
     
    242244                        void reset(future_t & this) {
    243245                                // needs to be in 0p or 1p
    244                                 __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST);
     246                                __atomic_exchange_n( &this.ptr, future_ARMED, __ATOMIC_SEQ_CST);
    245247                        }
    246248
    247249                        // check if the future is available
    248250                        bool available( future_t & this ) {
    249                                 while( this.ptr == 2p ) Pause();
    250                                 return this.ptr == 1p;
     251                                while( this.ptr == future_PROGRESS ) Pause();
     252                                return this.ptr == future_FULFILLED;
    251253                        }
    252254
     
    254256                        // intented to be use by wait, wait_any, waitfor, etc. rather than used directly
    255257                        bool setup( future_t & this, oneshot & wait_ctx ) {
    256                                 /* paranoid */ verify( wait_ctx.ptr == 0p || wait_ctx.ptr == 1p );
     258                                /* paranoid */ verify( wait_ctx.ptr == oneshot_ARMED || wait_ctx.ptr == oneshot_FULFILLED );
    257259                                // The future needs to set the wait context
    258260                                for() {
    259261                                        struct oneshot * expected = this.ptr;
    260262                                        // Is the future already fulfilled?
    261                                         if(expected == 1p) return false; // Yes, just return false (didn't block)
     263                                        if(expected == future_FULFILLED) return false; // Yes, just return false (didn't block)
    262264
    263265                                        // The future is not fulfilled, try to setup the wait context
     
    277279
    278280                                // attempt to remove the context so it doesn't get consumed.
    279                                 if(__atomic_compare_exchange_n( &this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
     281                                if(__atomic_compare_exchange_n( &this.ptr, &expected, future_ARMED, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
    280282                                        // we still have the original context, then no one else saw it
    281283                                        return false;
    282284                                }
    283285
    284                                 // expected == 0p: future was never actually setup, just return
    285                                 if( expected == 0p ) return false;
    286 
    287                                 // expected == 1p: the future is ready and the context was fully consumed
     286                                // expected == ARMED: future was never actually setup, just return
     287                                if( expected == future_ARMED ) return false;
     288
     289                                // expected == FULFILLED: the future is ready and the context was fully consumed
    288290                                // the server won't use the pointer again
    289291                                // It is safe to delete (which could happen after the return)
    290                                 if( expected == 1p ) return true;
    291 
    292                                 // expected == 2p: the future is ready but the context hasn't fully been consumed
     292                                if( expected == future_FULFILLED ) return true;
     293
     294                                // expected == PROGRESS: the future is ready but the context hasn't fully been consumed
    293295                                // spin until it is safe to move on
    294                                 if( expected == 2p ) {
    295                                         while( this.ptr != 1p ) Pause();
    296                                         /* paranoid */ verify( this.ptr == 1p );
     296                                if( expected == future_PROGRESS ) {
     297                                        while( this.ptr != future_FULFILLED ) Pause();
     298                                        /* paranoid */ verify( this.ptr == future_FULFILLED );
    297299                                        return true;
    298300                                }
     
    305307                        // Mark the future as abandoned, meaning it will be deleted by the server
    306308                        bool abandon( future_t & this ) {
    307                                 /* paranoid */ verify( this.ptr != 3p );
     309                                /* paranoid */ verify( this.ptr != future_ABANDONED );
    308310
    309311                                // Mark the future as abandonned
    310                                 struct oneshot * got = __atomic_exchange_n( &this.ptr, 3p, __ATOMIC_SEQ_CST);
     312                                struct oneshot * got = __atomic_exchange_n( &this.ptr, future_ABANDONED, __ATOMIC_SEQ_CST);
    311313
    312314                                // If the future isn't already fulfilled, let the server delete it
    313                                 if( got == 0p ) return false;
    314 
    315                                 // got == 2p: the future is ready but the context hasn't fully been consumed
     315                                if( got == future_ARMED ) return false;
     316
     317                                // got == PROGRESS: the future is ready but the context hasn't fully been consumed
    316318                                // spin until it is safe to move on
    317                                 if( got == 2p ) {
    318                                         while( this.ptr != 1p ) Pause();
    319                                         got = 1p;
     319                                if( got == future_PROGRESS ) {
     320                                        while( this.ptr != future_FULFILLED ) Pause();
     321                                        got = future_FULFILLED;
    320322                                }
    321323
    322324                                // The future is completed delete it now
    323                                 /* paranoid */ verify( this.ptr != 1p );
     325                                /* paranoid */ verify( this.ptr != future_FULFILLED );
    324326                                free( &this );
    325327                                return true;
     
    336338                                                #pragma GCC diagnostic ignored "-Wfree-nonheap-object"
    337339                                        #endif
    338                                                 if( expected == 3p ) { free( &this ); return 0p; }
     340                                                if( expected == future_ABANDONED ) { free( &this ); return 0p; }
    339341                                        #if defined(__GNUC__) && __GNUC__ >= 7
    340342                                                #pragma GCC diagnostic pop
    341343                                        #endif
    342344
    343                                         /* paranoid */ verify( expected != 1p ); // Future is already fulfilled, should not happen
    344                                         /* paranoid */ verify( expected != 2p ); // Future is bein fulfilled by someone else, this is even less supported then the previous case.
     345                                        /* paranoid */ verify( expected != future_FULFILLED ); // Future is already fulfilled, should not happen
     346                                        /* paranoid */ verify( expected != future_PROGRESS ); // Future is bein fulfilled by someone else, this is even less supported then the previous case.
    345347
    346348                                        // If there is a wait context, we need to consume it and mark it as consumed after
    347349                                        // If there is no context then we can skip the in progress phase
    348                                         struct oneshot * want = expected == 0p ? 1p : 2p;
     350                                        struct oneshot * want = expected == future_ARMED ? future_FULFILLED : future_PROGRESS;
    349351                                        if(__atomic_compare_exchange_n(&this.ptr, &expected, want, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
    350                                                 if( expected == 0p ) { return 0p; }
     352                                                if( expected == future_ARMED ) { return 0p; }
    351353                                                thread$ * ret = post( *expected, do_unpark );
    352                                                 __atomic_store_n( &this.ptr, 1p, __ATOMIC_SEQ_CST);
     354                                                __atomic_store_n( &this.ptr, future_FULFILLED, __ATOMIC_SEQ_CST);
    353355                                                return ret;
    354356                                        }
     
    366368
    367369                                // Wait for the future to tru
    368                                 while( this.ptr == 2p ) Pause();
     370                                while( this.ptr == future_PROGRESS ) Pause();
    369371                                // Make sure the state makes sense
    370372                                // Should be fulfilled, could be in progress but it's out of date if so
     
    372374                                // and the oneshot should not be needed any more
    373375                                __attribute__((unused)) struct oneshot * was = this.ptr;
    374                                 /* paranoid */ verifyf( was == 1p, "Expected this.ptr to be 1p, was %p\n", was );
     376                                /* paranoid */ verifyf( was == future_FULFILLED, "Expected this.ptr to be 1p, was %p\n", was );
    375377
    376378                                // Mark the future as fulfilled, to be consistent
  • libcfa/src/concurrency/kernel/private.hfa

    r428adbc r7f6a7c9  
    8888#elif defined(CFA_HAVE_LINUX_RSEQ_H)
    8989        extern "Cforall" {
    90                 extern __attribute__((aligned(64))) thread_local volatile struct rseq __cfaabi_rseq;
     90                extern __attribute__((aligned(64))) __thread volatile struct rseq __cfaabi_rseq;
    9191        }
    9292#else
     
    139139//-----------------------------------------------------------------------------
    140140// I/O
    141 $io_arbiter * create(void);
    142 void destroy($io_arbiter *);
     141io_arbiter$ * create(void);
     142void destroy(io_arbiter$ *);
    143143
    144144//=======================================================================
     
    161161// Blocking acquire
    162162static inline void __atomic_acquire(volatile bool * ll) {
     163        /* paranoid */ verify( ! __preemption_enabled() );
     164        /* paranoid */ verify(ll);
     165
    163166        while( __builtin_expect(__atomic_exchange_n(ll, (bool)true, __ATOMIC_SEQ_CST), false) ) {
    164167                while(__atomic_load_n(ll, (int)__ATOMIC_RELAXED))
     
    166169        }
    167170        /* paranoid */ verify(*ll);
     171        /* paranoid */ verify( ! __preemption_enabled() );
    168172}
    169173
    170174// Non-Blocking acquire
    171175static inline bool __atomic_try_acquire(volatile bool * ll) {
     176        /* paranoid */ verify( ! __preemption_enabled() );
     177        /* paranoid */ verify(ll);
     178
    172179        return !__atomic_exchange_n(ll, (bool)true, __ATOMIC_SEQ_CST);
    173180}
     
    175182// Release
    176183static inline void __atomic_unlock(volatile bool * ll) {
     184        /* paranoid */ verify( ! __preemption_enabled() );
     185        /* paranoid */ verify(ll);
    177186        /* paranoid */ verify(*ll);
    178187        __atomic_store_n(ll, (bool)false, __ATOMIC_RELEASE);
     
    184193// have been hard-coded to for the ready-queue for
    185194// simplicity and performance
    186 struct __scheduler_RWLock_t {
    187         // total cachelines allocated
    188         unsigned int max;
    189 
    190         // cachelines currently in use
    191         volatile unsigned int alloc;
    192 
    193         // cachelines ready to itereate over
    194         // (!= to alloc when thread is in second half of doregister)
    195         volatile unsigned int ready;
    196 
    197         // writer lock
    198         volatile bool write_lock;
    199 
    200         // data pointer
    201         volatile bool * volatile * data;
     195union __attribute__((aligned(64))) __scheduler_RWLock_t {
     196        struct {
     197                __attribute__((aligned(64))) char padding;
     198
     199                // total cachelines allocated
     200                __attribute__((aligned(64))) unsigned int max;
     201
     202                // cachelines currently in use
     203                volatile unsigned int alloc;
     204
     205                // cachelines ready to itereate over
     206                // (!= to alloc when thread is in second half of doregister)
     207                volatile unsigned int ready;
     208
     209                // writer lock
     210                volatile bool write_lock;
     211
     212                // data pointer
     213                volatile bool * volatile * data;
     214        } lock;
     215        char pad[192];
    202216};
    203217
     
    205219void ^?{}(__scheduler_RWLock_t & this);
    206220
    207 extern __scheduler_RWLock_t * __scheduler_lock;
     221extern __scheduler_RWLock_t __scheduler_lock;
    208222
    209223//-----------------------------------------------------------------------
    210224// Reader side : acquire when using the ready queue to schedule but not
    211225//  creating/destroying queues
    212 static inline void ready_schedule_lock(void) with(*__scheduler_lock) {
     226static inline void ready_schedule_lock(void) with(__scheduler_lock.lock) {
    213227        /* paranoid */ verify( ! __preemption_enabled() );
    214228        /* paranoid */ verify( ! kernelTLS().in_sched_lock );
     
    235249}
    236250
    237 static inline void ready_schedule_unlock(void) with(*__scheduler_lock) {
     251static inline void ready_schedule_unlock(void) with(__scheduler_lock.lock) {
    238252        /* paranoid */ verify( ! __preemption_enabled() );
    239253        /* paranoid */ verify( data[kernelTLS().sched_id] == &kernelTLS().sched_lock );
     
    256270
    257271        static inline bool ready_mutate_islocked() {
    258                 return __scheduler_lock->write_lock;
     272                return __scheduler_lock.lock.write_lock;
    259273        }
    260274#endif
  • libcfa/src/concurrency/kernel/startup.cfa

    r428adbc r7f6a7c9  
    113113KERNEL_STORAGE(thread$,              mainThread);
    114114KERNEL_STORAGE(__stack_t,            mainThreadCtx);
    115 KERNEL_STORAGE(__scheduler_RWLock_t, __scheduler_lock);
     115// KERNEL_STORAGE(__scheduler_RWLock_t, __scheduler_lock);
    116116KERNEL_STORAGE(eventfd_t,            mainIdleEventFd);
    117117KERNEL_STORAGE(io_future_t,          mainIdleFuture);
     
    123123processor            * mainProcessor;
    124124thread$              * mainThread;
    125 __scheduler_RWLock_t * __scheduler_lock;
    126125
    127126extern "C" {
     
    134133//-----------------------------------------------------------------------------
    135134// Global state
    136 thread_local struct KernelThreadData __cfaabi_tls __attribute__ ((tls_model ( "initial-exec" ))) @= {
     135__thread struct KernelThreadData __cfaabi_tls __attribute__ ((tls_model ( "initial-exec" ))) @= {
    137136        NULL,                                                                                           // cannot use 0p
    138137        NULL,
     
    148147};
    149148
     149__scheduler_RWLock_t __scheduler_lock @= { 0 };
     150
    150151#if   defined(CFA_HAVE_LINUX_LIBRSEQ)
    151152        // No data needed
    152153#elif defined(CFA_HAVE_LINUX_RSEQ_H)
    153154        extern "Cforall" {
    154                 __attribute__((aligned(64))) thread_local volatile struct rseq __cfaabi_rseq @= {
     155                __attribute__((aligned(64))) __thread volatile struct rseq __cfaabi_rseq @= {
    155156                        .cpu_id : RSEQ_CPU_ID_UNINITIALIZED,
    156157                };
     
    198199
    199200        // Initialize the global scheduler lock
    200         __scheduler_lock = (__scheduler_RWLock_t*)&storage___scheduler_lock;
    201         (*__scheduler_lock){};
     201        // __scheduler_lock = (__scheduler_RWLock_t*)&storage___scheduler_lock;
     202        (__scheduler_lock){};
    202203
    203204        // Initialize the main cluster
     
    345346        ^(*mainCluster){};
    346347
    347         ^(*__scheduler_lock){};
     348        ^(__scheduler_lock){};
    348349
    349350        ^(__cfa_dbg_global_clusters.list){};
Note: See TracChangeset for help on using the changeset viewer.