Ignore:
Timestamp:
Aug 11, 2020, 4:40:15 PM (5 years ago)
Author:
Thierry Delisle <tdelisle@…>
Branches:
ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, master, new-ast, new-ast-unique-expr, pthread-emulation, qualifiedEnum
Children:
0d070ca
Parents:
07d867b (diff), 129674b (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 new-ast

File:
1 edited

Legend:

Unmodified
Added
Removed
  • libcfa/src/concurrency/kernel_private.hfa

    r07d867b r22f94a4  
    2020
    2121#include "alarm.hfa"
    22 
     22#include "stats.hfa"
    2323
    2424//-----------------------------------------------------------------------------
    2525// Scheduler
     26
     27struct __attribute__((aligned(128))) __scheduler_lock_id_t;
    2628
    2729extern "C" {
     
    3133}
    3234
    33 void __schedule_thread( $thread * ) __attribute__((nonnull (1)));
     35void __schedule_thread( struct __processor_id_t *, $thread * )
     36#if defined(NDEBUG) || (!defined(__CFA_DEBUG__) && !defined(__CFA_VERIFY__))
     37        __attribute__((nonnull (2)))
     38#endif
     39;
    3440
    3541//Block current thread and release/wake-up the following resources
     
    4349
    4450
    45 
    46 struct event_kernel_t {
    47         alarm_list_t alarms;
    48         __spinlock_t lock;
    49 };
    50 
    51 extern event_kernel_t * event_kernel;
    52 
    53 struct __cfa_kernel_preemption_state_t {
    54         bool enabled;
    55         bool in_progress;
    56         unsigned short disable_count;
    57 };
    58 
    59 extern volatile thread_local __cfa_kernel_preemption_state_t preemption_state __attribute__ ((tls_model ( "initial-exec" )));
    6051
    6152extern cluster * mainCluster;
     
    7364
    7465// KERNEL ONLY unpark with out disabling interrupts
    75 void __unpark( $thread * thrd __cfaabi_dbg_ctx_param2 );
    76 
    77 //-----------------------------------------------------------------------------
    78 // I/O
    79 void __kernel_io_startup     ( cluster &, int, bool );
    80 void __kernel_io_finish_start( cluster & );
    81 void __kernel_io_prepare_stop( cluster & );
    82 void __kernel_io_shutdown    ( cluster &, bool );
     66void __unpark( struct __processor_id_t *, $thread * thrd __cfaabi_dbg_ctx_param2 );
     67
     68static inline bool __post(single_sem & this, struct __processor_id_t * id) {
     69        for() {
     70                struct $thread * expected = this.ptr;
     71                if(expected == 1p) return false;
     72                if(expected == 0p) {
     73                        if(__atomic_compare_exchange_n(&this.ptr, &expected, 1p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
     74                                return false;
     75                        }
     76                }
     77                else {
     78                        if(__atomic_compare_exchange_n(&this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
     79                                __unpark( id, expected __cfaabi_dbg_ctx2 );
     80                                return true;
     81                        }
     82                }
     83        }
     84}
    8385
    8486//-----------------------------------------------------------------------------
    8587// Utils
    86 #define KERNEL_STORAGE(T,X) static char storage_##X[sizeof(T)]
    87 
    88 static inline uint32_t __tls_rand() {
    89         kernelTLS.rand_seed ^= kernelTLS.rand_seed << 6;
    90         kernelTLS.rand_seed ^= kernelTLS.rand_seed >> 21;
    91         kernelTLS.rand_seed ^= kernelTLS.rand_seed << 7;
    92         return kernelTLS.rand_seed;
    93 }
    94 
    95 
    96 void doregister( struct cluster & cltr );
    97 void unregister( struct cluster & cltr );
    98 
    9988void doregister( struct cluster * cltr, struct $thread & thrd );
    10089void unregister( struct cluster * cltr, struct $thread & thrd );
    10190
    102 void doregister( struct cluster * cltr, struct processor * proc );
    103 void unregister( struct cluster * cltr, struct processor * proc );
     91//-----------------------------------------------------------------------------
     92// I/O
     93void ^?{}(io_context & this, bool );
     94
     95//=======================================================================
     96// Cluster lock API
     97//=======================================================================
     98// Cells use by the reader writer lock
     99// while not generic it only relies on a opaque pointer
     100struct __attribute__((aligned(128))) __scheduler_lock_id_t {
     101        // Spin lock used as the underlying lock
     102        volatile bool lock;
     103
     104        // Handle pointing to the proc owning this cell
     105        // Used for allocating cells and debugging
     106        __processor_id_t * volatile handle;
     107
     108        #ifdef __CFA_WITH_VERIFY__
     109                // Debug, check if this is owned for reading
     110                bool owned;
     111        #endif
     112};
     113
     114static_assert( sizeof(struct __scheduler_lock_id_t) <= __alignof(struct __scheduler_lock_id_t));
     115
     116// Lock-Free registering/unregistering of threads
     117// Register a processor to a given cluster and get its unique id in return
     118unsigned doregister( struct __processor_id_t * proc );
     119
     120// Unregister a processor from a given cluster using its id, getting back the original pointer
     121void     unregister( struct __processor_id_t * proc );
     122
     123//-----------------------------------------------------------------------
     124// Cluster idle lock/unlock
     125static inline void lock(__cluster_idles & this) {
     126        for() {
     127                uint64_t l = this.lock;
     128                if(
     129                        (0 == (l % 2))
     130                        && __atomic_compare_exchange_n(&this.lock, &l, l + 1, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
     131                ) return;
     132                Pause();
     133        }
     134}
     135
     136static inline void unlock(__cluster_idles & this) {
     137        /* paranoid */ verify( 1 == (this.lock % 2) );
     138        __atomic_fetch_add( &this.lock, 1, __ATOMIC_SEQ_CST );
     139}
     140
     141//=======================================================================
     142// Reader-writer lock implementation
     143// Concurrent with doregister/unregister,
     144//    i.e., threads can be added at any point during or between the entry/exit
     145
     146//-----------------------------------------------------------------------
     147// simple spinlock underlying the RWLock
     148// Blocking acquire
     149static inline void __atomic_acquire(volatile bool * ll) {
     150        while( __builtin_expect(__atomic_exchange_n(ll, (bool)true, __ATOMIC_SEQ_CST), false) ) {
     151                while(__atomic_load_n(ll, (int)__ATOMIC_RELAXED))
     152                        asm volatile("pause");
     153        }
     154        /* paranoid */ verify(*ll);
     155}
     156
     157// Non-Blocking acquire
     158static inline bool __atomic_try_acquire(volatile bool * ll) {
     159        return !__atomic_exchange_n(ll, (bool)true, __ATOMIC_SEQ_CST);
     160}
     161
     162// Release
     163static inline void __atomic_unlock(volatile bool * ll) {
     164        /* paranoid */ verify(*ll);
     165        __atomic_store_n(ll, (bool)false, __ATOMIC_RELEASE);
     166}
     167
     168//-----------------------------------------------------------------------
     169// Reader-Writer lock protecting the ready-queues
     170// while this lock is mostly generic some aspects
     171// have been hard-coded to for the ready-queue for
     172// simplicity and performance
     173struct __scheduler_RWLock_t {
     174        // total cachelines allocated
     175        unsigned int max;
     176
     177        // cachelines currently in use
     178        volatile unsigned int alloc;
     179
     180        // cachelines ready to itereate over
     181        // (!= to alloc when thread is in second half of doregister)
     182        volatile unsigned int ready;
     183
     184        // writer lock
     185        volatile bool lock;
     186
     187        // data pointer
     188        __scheduler_lock_id_t * data;
     189};
     190
     191void  ?{}(__scheduler_RWLock_t & this);
     192void ^?{}(__scheduler_RWLock_t & this);
     193
     194extern __scheduler_RWLock_t * __scheduler_lock;
     195
     196//-----------------------------------------------------------------------
     197// Reader side : acquire when using the ready queue to schedule but not
     198//  creating/destroying queues
     199static inline void ready_schedule_lock( struct __processor_id_t * proc) with(*__scheduler_lock) {
     200        unsigned iproc = proc->id;
     201        /*paranoid*/ verify(data[iproc].handle == proc);
     202        /*paranoid*/ verify(iproc < ready);
     203
     204        // Step 1 : make sure no writer are in the middle of the critical section
     205        while(__atomic_load_n(&lock, (int)__ATOMIC_RELAXED))
     206                asm volatile("pause");
     207
     208        // Fence needed because we don't want to start trying to acquire the lock
     209        // before we read a false.
     210        // Not needed on x86
     211        // std::atomic_thread_fence(std::memory_order_seq_cst);
     212
     213        // Step 2 : acquire our local lock
     214        __atomic_acquire( &data[iproc].lock );
     215        /*paranoid*/ verify(data[iproc].lock);
     216
     217        #ifdef __CFA_WITH_VERIFY__
     218                // Debug, check if this is owned for reading
     219                data[iproc].owned = true;
     220        #endif
     221}
     222
     223static inline void ready_schedule_unlock( struct __processor_id_t * proc) with(*__scheduler_lock) {
     224        unsigned iproc = proc->id;
     225        /*paranoid*/ verify(data[iproc].handle == proc);
     226        /*paranoid*/ verify(iproc < ready);
     227        /*paranoid*/ verify(data[iproc].lock);
     228        /*paranoid*/ verify(data[iproc].owned);
     229        #ifdef __CFA_WITH_VERIFY__
     230                // Debug, check if this is owned for reading
     231                data[iproc].owned = false;
     232        #endif
     233        __atomic_unlock(&data[iproc].lock);
     234}
     235
     236#ifdef __CFA_WITH_VERIFY__
     237        static inline bool ready_schedule_islocked( struct __processor_id_t * proc) {
     238                return __scheduler_lock->data[proc->id].owned;
     239        }
     240
     241        static inline bool ready_mutate_islocked() {
     242                return __scheduler_lock->lock;
     243        }
     244#endif
     245
     246//-----------------------------------------------------------------------
     247// Writer side : acquire when changing the ready queue, e.g. adding more
     248//  queues or removing them.
     249uint_fast32_t ready_mutate_lock( void );
     250
     251void ready_mutate_unlock( uint_fast32_t /* value returned by lock */ );
     252
     253//=======================================================================
     254// Ready-Queue API
     255//-----------------------------------------------------------------------
     256// pop thread from the ready queue of a cluster
     257// returns 0p if empty
     258__attribute__((hot)) bool query(struct cluster * cltr);
     259
     260//-----------------------------------------------------------------------
     261// push thread onto a ready queue for a cluster
     262// returns true if the list was previously empty, false otherwise
     263__attribute__((hot)) bool push(struct cluster * cltr, struct $thread * thrd);
     264
     265//-----------------------------------------------------------------------
     266// pop thread from the ready queue of a cluster
     267// returns 0p if empty
     268// May return 0p spuriously
     269__attribute__((hot)) struct $thread * pop(struct cluster * cltr);
     270
     271//-----------------------------------------------------------------------
     272// pop thread from the ready queue of a cluster
     273// returns 0p if empty
     274// guaranteed to find any threads added before this call
     275__attribute__((hot)) struct $thread * pop_slow(struct cluster * cltr);
     276
     277//-----------------------------------------------------------------------
     278// remove thread from the ready queue of a cluster
     279// returns bool if it wasn't found
     280bool remove_head(struct cluster * cltr, struct $thread * thrd);
     281
     282//-----------------------------------------------------------------------
     283// Increase the width of the ready queue (number of lanes) by 4
     284void ready_queue_grow  (struct cluster * cltr, int target);
     285
     286//-----------------------------------------------------------------------
     287// Decrease the width of the ready queue (number of lanes) by 4
     288void ready_queue_shrink(struct cluster * cltr, int target);
     289
    104290
    105291// Local Variables: //
Note: See TracChangeset for help on using the changeset viewer.