Changeset 22f94a4 for libcfa/src/concurrency/kernel_private.hfa
- Timestamp:
- Aug 11, 2020, 4:40:15 PM (5 years ago)
- 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. - File:
-
- 1 edited
-
libcfa/src/concurrency/kernel_private.hfa (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
libcfa/src/concurrency/kernel_private.hfa
r07d867b r22f94a4 20 20 21 21 #include "alarm.hfa" 22 22 #include "stats.hfa" 23 23 24 24 //----------------------------------------------------------------------------- 25 25 // Scheduler 26 27 struct __attribute__((aligned(128))) __scheduler_lock_id_t; 26 28 27 29 extern "C" { … … 31 33 } 32 34 33 void __schedule_thread( $thread * ) __attribute__((nonnull (1))); 35 void __schedule_thread( struct __processor_id_t *, $thread * ) 36 #if defined(NDEBUG) || (!defined(__CFA_DEBUG__) && !defined(__CFA_VERIFY__)) 37 __attribute__((nonnull (2))) 38 #endif 39 ; 34 40 35 41 //Block current thread and release/wake-up the following resources … … 43 49 44 50 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" )));60 51 61 52 extern cluster * mainCluster; … … 73 64 74 65 // 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 ); 66 void __unpark( struct __processor_id_t *, $thread * thrd __cfaabi_dbg_ctx_param2 ); 67 68 static 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 } 83 85 84 86 //----------------------------------------------------------------------------- 85 87 // 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 99 88 void doregister( struct cluster * cltr, struct $thread & thrd ); 100 89 void unregister( struct cluster * cltr, struct $thread & thrd ); 101 90 102 void doregister( struct cluster * cltr, struct processor * proc ); 103 void unregister( struct cluster * cltr, struct processor * proc ); 91 //----------------------------------------------------------------------------- 92 // I/O 93 void ^?{}(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 100 struct __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 114 static_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 118 unsigned doregister( struct __processor_id_t * proc ); 119 120 // Unregister a processor from a given cluster using its id, getting back the original pointer 121 void unregister( struct __processor_id_t * proc ); 122 123 //----------------------------------------------------------------------- 124 // Cluster idle lock/unlock 125 static 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 136 static 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 149 static 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 158 static inline bool __atomic_try_acquire(volatile bool * ll) { 159 return !__atomic_exchange_n(ll, (bool)true, __ATOMIC_SEQ_CST); 160 } 161 162 // Release 163 static 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 173 struct __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 191 void ?{}(__scheduler_RWLock_t & this); 192 void ^?{}(__scheduler_RWLock_t & this); 193 194 extern __scheduler_RWLock_t * __scheduler_lock; 195 196 //----------------------------------------------------------------------- 197 // Reader side : acquire when using the ready queue to schedule but not 198 // creating/destroying queues 199 static 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 223 static 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. 249 uint_fast32_t ready_mutate_lock( void ); 250 251 void 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 280 bool remove_head(struct cluster * cltr, struct $thread * thrd); 281 282 //----------------------------------------------------------------------- 283 // Increase the width of the ready queue (number of lanes) by 4 284 void ready_queue_grow (struct cluster * cltr, int target); 285 286 //----------------------------------------------------------------------- 287 // Decrease the width of the ready queue (number of lanes) by 4 288 void ready_queue_shrink(struct cluster * cltr, int target); 289 104 290 105 291 // Local Variables: //
Note:
See TracChangeset
for help on using the changeset viewer.