source: libcfa/src/concurrency/ready_queue.cfa@ b353a49

ADT arm-eh ast-experimental enum forall-pointer-decay jacob/cs343-translation new-ast new-ast-unique-expr pthread-emulation qualifiedEnum
Last change on this file since b353a49 was 62502cc4, checked in by Thierry Delisle <tdelisle@…>, 5 years ago

Fixed deadlock where threads could acquire the central scheduler lock for writing while preemption was enabled, leading to any attempt at running any thread to deadlock.
Also added runtime checks to catch new code which could forget to disable interrupts

  • Property mode set to 100644
File size: 18.0 KB
RevLine 
[7768b8d]1//
2// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// ready_queue.cfa --
8//
9// Author : Thierry Delisle
10// Created On : Mon Nov dd 16:29:18 2019
11// Last Modified By :
12// Last Modified On :
13// Update Count :
14//
15
16#define __cforall_thread__
[1b143de]17// #define __CFA_DEBUG_PRINT_READY_QUEUE__
[7768b8d]18
[1eb239e4]19// #define USE_SNZI
20
[7768b8d]21#include "bits/defs.hfa"
22#include "kernel_private.hfa"
23
24#define _GNU_SOURCE
25#include "stdlib.hfa"
[61d7bec]26#include "math.hfa"
[7768b8d]27
[04b5cef]28#include <unistd.h>
29
[13c5e19]30#include "snzi.hfa"
31#include "ready_subqueue.hfa"
32
[7768b8d]33static const size_t cache_line_size = 64;
34
[dca5802]35// No overriden function, no environment variable, no define
36// fall back to a magic number
37#ifndef __CFA_MAX_PROCESSORS__
[b388ee81]38 #define __CFA_MAX_PROCESSORS__ 1024
[dca5802]39#endif
[7768b8d]40
[320ec6fc]41#define BIAS 16
[04b5cef]42
[dca5802]43// returns the maximum number of processors the RWLock support
[7768b8d]44__attribute__((weak)) unsigned __max_processors() {
45 const char * max_cores_s = getenv("CFA_MAX_PROCESSORS");
46 if(!max_cores_s) {
[504a7dc]47 __cfadbg_print_nolock(ready_queue, "No CFA_MAX_PROCESSORS in ENV\n");
[dca5802]48 return __CFA_MAX_PROCESSORS__;
[7768b8d]49 }
50
51 char * endptr = 0p;
52 long int max_cores_l = strtol(max_cores_s, &endptr, 10);
53 if(max_cores_l < 1 || max_cores_l > 65535) {
[504a7dc]54 __cfadbg_print_nolock(ready_queue, "CFA_MAX_PROCESSORS out of range : %ld\n", max_cores_l);
[dca5802]55 return __CFA_MAX_PROCESSORS__;
[7768b8d]56 }
57 if('\0' != *endptr) {
[504a7dc]58 __cfadbg_print_nolock(ready_queue, "CFA_MAX_PROCESSORS not a decimal number : %s\n", max_cores_s);
[dca5802]59 return __CFA_MAX_PROCESSORS__;
[7768b8d]60 }
61
62 return max_cores_l;
63}
64
65//=======================================================================
66// Cluster wide reader-writer lock
67//=======================================================================
[b388ee81]68void ?{}(__scheduler_RWLock_t & this) {
[7768b8d]69 this.max = __max_processors();
70 this.alloc = 0;
71 this.ready = 0;
72 this.lock = false;
73 this.data = alloc(this.max);
74
75 /*paranoid*/ verify( 0 == (((uintptr_t)(this.data )) % 64) );
76 /*paranoid*/ verify( 0 == (((uintptr_t)(this.data + 1)) % 64) );
77 /*paranoid*/ verify(__atomic_is_lock_free(sizeof(this.alloc), &this.alloc));
78 /*paranoid*/ verify(__atomic_is_lock_free(sizeof(this.ready), &this.ready));
79
80}
[b388ee81]81void ^?{}(__scheduler_RWLock_t & this) {
[7768b8d]82 free(this.data);
83}
84
[9b1dcc2]85void ?{}( __scheduler_lock_id_t & this, __processor_id_t * proc ) {
[7768b8d]86 this.handle = proc;
87 this.lock = false;
[64a7146]88 #ifdef __CFA_WITH_VERIFY__
89 this.owned = false;
90 #endif
[7768b8d]91}
92
93//=======================================================================
94// Lock-Free registering/unregistering of threads
[9b1dcc2]95unsigned doregister( struct __processor_id_t * proc ) with(*__scheduler_lock) {
[b388ee81]96 __cfadbg_print_safe(ready_queue, "Kernel : Registering proc %p for RW-Lock\n", proc);
[504a7dc]97
[7768b8d]98 // Step - 1 : check if there is already space in the data
99 uint_fast32_t s = ready;
100
101 // Check among all the ready
102 for(uint_fast32_t i = 0; i < s; i++) {
[9b1dcc2]103 __processor_id_t * null = 0p; // Re-write every loop since compare thrashes it
[7768b8d]104 if( __atomic_load_n(&data[i].handle, (int)__ATOMIC_RELAXED) == null
105 && __atomic_compare_exchange_n( &data[i].handle, &null, proc, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
106 /*paranoid*/ verify(i < ready);
[64a7146]107 /*paranoid*/ verify(0 == (__alignof__(data[i]) % cache_line_size));
[7768b8d]108 /*paranoid*/ verify((((uintptr_t)&data[i]) % cache_line_size) == 0);
109 return i;
110 }
111 }
112
[b388ee81]113 if(max <= alloc) abort("Trying to create more than %ud processors", __scheduler_lock->max);
[7768b8d]114
115 // Step - 2 : F&A to get a new spot in the array.
116 uint_fast32_t n = __atomic_fetch_add(&alloc, 1, __ATOMIC_SEQ_CST);
[b388ee81]117 if(max <= n) abort("Trying to create more than %ud processors", __scheduler_lock->max);
[7768b8d]118
119 // Step - 3 : Mark space as used and then publish it.
[9b1dcc2]120 __scheduler_lock_id_t * storage = (__scheduler_lock_id_t *)&data[n];
[7768b8d]121 (*storage){ proc };
122 while(true) {
123 unsigned copy = n;
124 if( __atomic_load_n(&ready, __ATOMIC_RELAXED) == n
125 && __atomic_compare_exchange_n(&ready, &copy, n + 1, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
126 break;
127 asm volatile("pause");
128 }
129
[1b143de]130 __cfadbg_print_safe(ready_queue, "Kernel : Registering proc %p done, id %lu\n", proc, n);
[504a7dc]131
[7768b8d]132 // Return new spot.
133 /*paranoid*/ verify(n < ready);
[37ba662]134 /*paranoid*/ verify(__alignof__(data[n]) == (2 * cache_line_size));
[7768b8d]135 /*paranoid*/ verify((((uintptr_t)&data[n]) % cache_line_size) == 0);
136 return n;
137}
138
[9b1dcc2]139void unregister( struct __processor_id_t * proc ) with(*__scheduler_lock) {
[7768b8d]140 unsigned id = proc->id;
141 /*paranoid*/ verify(id < ready);
142 /*paranoid*/ verify(proc == __atomic_load_n(&data[id].handle, __ATOMIC_RELAXED));
143 __atomic_store_n(&data[id].handle, 0p, __ATOMIC_RELEASE);
[504a7dc]144
145 __cfadbg_print_safe(ready_queue, "Kernel : Unregister proc %p\n", proc);
[7768b8d]146}
147
148//-----------------------------------------------------------------------
149// Writer side : acquire when changing the ready queue, e.g. adding more
150// queues or removing them.
[b388ee81]151uint_fast32_t ready_mutate_lock( void ) with(*__scheduler_lock) {
[62502cc4]152 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
153
[7768b8d]154 // Step 1 : lock global lock
155 // It is needed to avoid processors that register mid Critical-Section
156 // to simply lock their own lock and enter.
157 __atomic_acquire( &lock );
158
159 // Step 2 : lock per-proc lock
160 // Processors that are currently being registered aren't counted
161 // but can't be in read_lock or in the critical section.
162 // All other processors are counted
163 uint_fast32_t s = ready;
164 for(uint_fast32_t i = 0; i < s; i++) {
165 __atomic_acquire( &data[i].lock );
166 }
167
[62502cc4]168 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
[7768b8d]169 return s;
170}
171
[b388ee81]172void ready_mutate_unlock( uint_fast32_t last_s ) with(*__scheduler_lock) {
[62502cc4]173 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
174
[7768b8d]175 // Step 1 : release local locks
176 // This must be done while the global lock is held to avoid
177 // threads that where created mid critical section
178 // to race to lock their local locks and have the writer
179 // immidiately unlock them
180 // Alternative solution : return s in write_lock and pass it to write_unlock
181 for(uint_fast32_t i = 0; i < last_s; i++) {
182 verify(data[i].lock);
183 __atomic_store_n(&data[i].lock, (bool)false, __ATOMIC_RELEASE);
184 }
185
186 // Step 2 : release global lock
187 /*paranoid*/ assert(true == lock);
188 __atomic_store_n(&lock, (bool)false, __ATOMIC_RELEASE);
[62502cc4]189
190 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
[7768b8d]191}
192
193//=======================================================================
[13c5e19]194// Cforall Reqdy Queue used for scheduling
[b798713]195//=======================================================================
196void ?{}(__ready_queue_t & this) with (this) {
[28d73c1]197 lanes.data = 0p;
198 lanes.count = 0;
[b798713]199}
200
201void ^?{}(__ready_queue_t & this) with (this) {
[39fc03e]202 verify( 1 == lanes.count );
[1eb239e4]203 #ifdef USE_SNZI
204 verify( !query( snzi ) );
205 #endif
[dca5802]206 free(lanes.data);
207}
208
[64a7146]209//-----------------------------------------------------------------------
210__attribute__((hot)) bool query(struct cluster * cltr) {
[1eb239e4]211 #ifdef USE_SNZI
212 return query(cltr->ready_queue.snzi);
213 #endif
214 return true;
[64a7146]215}
216
[dca5802]217//-----------------------------------------------------------------------
[504a7dc]218__attribute__((hot)) bool push(struct cluster * cltr, struct $thread * thrd) with (cltr->ready_queue) {
[61d7bec]219 __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr);
[1b143de]220
[dca5802]221 // write timestamp
[b798713]222 thrd->link.ts = rdtscl();
223
[52769ba]224 #if defined(BIAS) && !defined(__CFA_NO_STATISTICS__)
225 bool local = false;
[d72c074]226 int preferred =
227 //*
228 kernelTLS.this_processor ? kernelTLS.this_processor->id * 4 : -1;
229 /*/
230 thrd->link.preferred * 4;
231 //*/
232
233
[52769ba]234 #endif
235
[dca5802]236 // Try to pick a lane and lock it
237 unsigned i;
238 do {
239 // Pick the index of a lane
[04b5cef]240 #if defined(BIAS)
241 unsigned r = __tls_rand();
242 unsigned rlow = r % BIAS;
243 unsigned rhigh = r / BIAS;
[d72c074]244 if((0 != rlow) && preferred >= 0) {
[04b5cef]245 // (BIAS - 1) out of BIAS chances
246 // Use perferred queues
[d72c074]247 i = preferred + (rhigh % 4);
[13c5e19]248
249 #if !defined(__CFA_NO_STATISTICS__)
[52769ba]250 local = true;
[13c5e19]251 __tls_stats()->ready.pick.push.local++;
252 #endif
[04b5cef]253 }
254 else {
255 // 1 out of BIAS chances
256 // Use all queues
257 i = rhigh;
[52769ba]258 local = false;
[04b5cef]259 }
260 #else
261 i = __tls_rand();
262 #endif
263
264 i %= __atomic_load_n( &lanes.count, __ATOMIC_RELAXED );
[b798713]265
266 #if !defined(__CFA_NO_STATISTICS__)
[8834751]267 __tls_stats()->ready.pick.push.attempt++;
[b798713]268 #endif
269
270 // If we can't lock it retry
[dca5802]271 } while( !__atomic_try_acquire( &lanes.data[i].lock ) );
[b798713]272
[dca5802]273 bool first = false;
274
275 // Actually push it
276 bool lane_first = push(lanes.data[i], thrd);
277
[1eb239e4]278 #ifdef USE_SNZI
279 // If this lane used to be empty we need to do more
280 if(lane_first) {
281 // Check if the entire queue used to be empty
282 first = !query(snzi);
[61d7bec]283
[1eb239e4]284 // Update the snzi
285 arrive( snzi, i );
286 }
287 #endif
[dca5802]288
289 // Unlock and return
290 __atomic_unlock( &lanes.data[i].lock );
291
[1b143de]292 __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);
293
[dca5802]294 // Update statistics
295 #if !defined(__CFA_NO_STATISTICS__)
[52769ba]296 #if defined(BIAS)
297 if( local ) __tls_stats()->ready.pick.push.lsuccess++;
298 #endif
[8834751]299 __tls_stats()->ready.pick.push.success++;
[dca5802]300 #endif
301
302 // return whether or not the list was empty before this push
303 return first;
[b798713]304}
305
[13c5e19]306static struct $thread * try_pop(struct cluster * cltr, unsigned i, unsigned j);
307static struct $thread * try_pop(struct cluster * cltr, unsigned i);
308
309// Pop from the ready queue from a given cluster
310__attribute__((hot)) $thread * pop(struct cluster * cltr) with (cltr->ready_queue) {
311 /* paranoid */ verify( lanes.count > 0 );
[1eb239e4]312 unsigned count = __atomic_load_n( &lanes.count, __ATOMIC_RELAXED );
[13c5e19]313 #if defined(BIAS)
314 // Don't bother trying locally too much
315 int local_tries = 8;
316 #endif
317
318 // As long as the list is not empty, try finding a lane that isn't empty and pop from it
[1eb239e4]319 #ifdef USE_SNZI
320 while( query(snzi) ) {
321 #else
322 for(25) {
323 #endif
[13c5e19]324 // Pick two lists at random
325 unsigned i,j;
326 #if defined(BIAS)
[52769ba]327 #if !defined(__CFA_NO_STATISTICS__)
328 bool local = false;
329 #endif
[13c5e19]330 uint64_t r = __tls_rand();
331 unsigned rlow = r % BIAS;
332 uint64_t rhigh = r / BIAS;
333 if(local_tries && 0 != rlow) {
334 // (BIAS - 1) out of BIAS chances
335 // Use perferred queues
336 unsigned pid = kernelTLS.this_processor->id * 4;
337 i = pid + (rhigh % 4);
338 j = pid + ((rhigh >> 32ull) % 4);
339
340 // count the tries
341 local_tries--;
342
343 #if !defined(__CFA_NO_STATISTICS__)
[52769ba]344 local = true;
[13c5e19]345 __tls_stats()->ready.pick.pop.local++;
346 #endif
347 }
348 else {
349 // 1 out of BIAS chances
350 // Use all queues
351 i = rhigh;
352 j = rhigh >> 32ull;
353 }
354 #else
355 i = __tls_rand();
356 j = __tls_rand();
357 #endif
358
[1eb239e4]359 i %= count;
360 j %= count;
[13c5e19]361
362 // try popping from the 2 picked lists
363 struct $thread * thrd = try_pop(cltr, i, j);
[52769ba]364 if(thrd) {
365 #if defined(BIAS) && !defined(__CFA_NO_STATISTICS__)
366 if( local ) __tls_stats()->ready.pick.pop.lsuccess++;
367 #endif
368 return thrd;
369 }
[13c5e19]370 }
371
372 // All lanes where empty return 0p
373 return 0p;
374}
375
[1eb239e4]376__attribute__((hot)) struct $thread * pop_slow(struct cluster * cltr) with (cltr->ready_queue) {
377 /* paranoid */ verify( lanes.count > 0 );
378 unsigned count = __atomic_load_n( &lanes.count, __ATOMIC_RELAXED );
379 unsigned offset = __tls_rand();
380 for(i; count) {
381 unsigned idx = (offset + i) % count;
382 struct $thread * thrd = try_pop(cltr, idx);
383 if(thrd) {
384 return thrd;
385 }
386 }
387
388 // All lanes where empty return 0p
389 return 0p;
390}
391
392
[b798713]393//-----------------------------------------------------------------------
[dca5802]394// Given 2 indexes, pick the list with the oldest push an try to pop from it
[13c5e19]395static inline struct $thread * try_pop(struct cluster * cltr, unsigned i, unsigned j) with (cltr->ready_queue) {
[b798713]396 #if !defined(__CFA_NO_STATISTICS__)
[8834751]397 __tls_stats()->ready.pick.pop.attempt++;
[b798713]398 #endif
399
400 // Pick the bet list
401 int w = i;
[dca5802]402 if( __builtin_expect(!is_empty(lanes.data[j]), true) ) {
403 w = (ts(lanes.data[i]) < ts(lanes.data[j])) ? i : j;
[b798713]404 }
405
[13c5e19]406 return try_pop(cltr, w);
407}
408
409static inline struct $thread * try_pop(struct cluster * cltr, unsigned w) with (cltr->ready_queue) {
[dca5802]410 // Get relevant elements locally
411 __intrusive_lane_t & lane = lanes.data[w];
412
[b798713]413 // If list looks empty retry
[dca5802]414 if( is_empty(lane) ) return 0p;
[b798713]415
416 // If we can't get the lock retry
[dca5802]417 if( !__atomic_try_acquire(&lane.lock) ) return 0p;
[b798713]418
419
420 // If list is empty, unlock and retry
[dca5802]421 if( is_empty(lane) ) {
422 __atomic_unlock(&lane.lock);
[b798713]423 return 0p;
424 }
425
426 // Actually pop the list
[504a7dc]427 struct $thread * thrd;
[343d10e]428 thrd = pop(lane);
[b798713]429
[dca5802]430 /* paranoid */ verify(thrd);
431 /* paranoid */ verify(lane.lock);
[b798713]432
[1eb239e4]433 #ifdef USE_SNZI
434 // If this was the last element in the lane
435 if(emptied) {
436 depart( snzi, w );
437 }
438 #endif
[b798713]439
440 // Unlock and return
[dca5802]441 __atomic_unlock(&lane.lock);
[b798713]442
[dca5802]443 // Update statistics
[b798713]444 #if !defined(__CFA_NO_STATISTICS__)
[8834751]445 __tls_stats()->ready.pick.pop.success++;
[b798713]446 #endif
447
[d72c074]448 // Update the thread bias
449 thrd->link.preferred = w / 4;
450
[dca5802]451 // return the popped thread
[b798713]452 return thrd;
453}
[13c5e19]454//-----------------------------------------------------------------------
[b798713]455
[13c5e19]456bool remove_head(struct cluster * cltr, struct $thread * thrd) with (cltr->ready_queue) {
457 for(i; lanes.count) {
458 __intrusive_lane_t & lane = lanes.data[i];
[b798713]459
[13c5e19]460 bool removed = false;
[04b5cef]461
[13c5e19]462 __atomic_acquire(&lane.lock);
463 if(head(lane)->link.next == thrd) {
464 $thread * pthrd;
[343d10e]465 pthrd = pop(lane);
[04b5cef]466
[13c5e19]467 /* paranoid */ verify( pthrd == thrd );
[61d7bec]468
[13c5e19]469 removed = true;
[1eb239e4]470 #ifdef USE_SNZI
471 if(emptied) {
472 depart( snzi, i );
473 }
474 #endif
[13c5e19]475 }
476 __atomic_unlock(&lane.lock);
[b798713]477
[13c5e19]478 if( removed ) return true;
479 }
480 return false;
[b798713]481}
482
483//-----------------------------------------------------------------------
484
485static void check( __ready_queue_t & q ) with (q) {
486 #if defined(__CFA_WITH_VERIFY__)
487 {
[dca5802]488 for( idx ; lanes.count ) {
489 __intrusive_lane_t & sl = lanes.data[idx];
490 assert(!lanes.data[idx].lock);
[b798713]491
492 assert(head(sl)->link.prev == 0p );
493 assert(head(sl)->link.next->link.prev == head(sl) );
494 assert(tail(sl)->link.next == 0p );
495 assert(tail(sl)->link.prev->link.next == tail(sl) );
496
497 if(sl.before.link.ts == 0l) {
498 assert(tail(sl)->link.prev == head(sl));
499 assert(head(sl)->link.next == tail(sl));
[1b143de]500 } else {
501 assert(tail(sl)->link.prev != head(sl));
502 assert(head(sl)->link.next != tail(sl));
[b798713]503 }
504 }
505 }
506 #endif
507}
508
509// Call this function of the intrusive list was moved using memcpy
[dca5802]510// fixes the list so that the pointers back to anchors aren't left dangling
511static inline void fix(__intrusive_lane_t & ll) {
512 // if the list is not empty then follow he pointer and fix its reverse
513 if(!is_empty(ll)) {
[b798713]514 head(ll)->link.next->link.prev = head(ll);
515 tail(ll)->link.prev->link.next = tail(ll);
516 }
517 // Otherwise just reset the list
518 else {
[dca5802]519 verify(tail(ll)->link.next == 0p);
[b798713]520 tail(ll)->link.prev = head(ll);
521 head(ll)->link.next = tail(ll);
[dca5802]522 verify(head(ll)->link.prev == 0p);
[b798713]523 }
524}
525
[dca5802]526// Grow the ready queue
[320ec6fc]527void ready_queue_grow (struct cluster * cltr, int target) {
[64a7146]528 /* paranoid */ verify( ready_mutate_islocked() );
[504a7dc]529 __cfadbg_print_safe(ready_queue, "Kernel : Growing ready queue\n");
[b798713]530
[dca5802]531 // Make sure that everything is consistent
532 /* paranoid */ check( cltr->ready_queue );
533
534 // grow the ready queue
[b798713]535 with( cltr->ready_queue ) {
[1eb239e4]536 #ifdef USE_SNZI
537 ^(snzi){};
538 #endif
[b798713]539
[39fc03e]540 // Find new count
541 // Make sure we always have atleast 1 list
542 size_t ncount = target >= 2 ? target * 4: 1;
[b798713]543
[dca5802]544 // Allocate new array (uses realloc and memcpies the data)
[39fc03e]545 lanes.data = alloc(lanes.data, ncount);
[b798713]546
547 // Fix the moved data
[dca5802]548 for( idx; (size_t)lanes.count ) {
549 fix(lanes.data[idx]);
[b798713]550 }
551
552 // Construct new data
[dca5802]553 for( idx; (size_t)lanes.count ~ ncount) {
554 (lanes.data[idx]){};
[b798713]555 }
556
557 // Update original
[dca5802]558 lanes.count = ncount;
559
[1eb239e4]560 #ifdef USE_SNZI
561 // Re-create the snzi
562 snzi{ log2( lanes.count / 8 ) };
563 for( idx; (size_t)lanes.count ) {
564 if( !is_empty(lanes.data[idx]) ) {
565 arrive(snzi, idx);
566 }
[61d7bec]567 }
[1eb239e4]568 #endif
[b798713]569 }
570
571 // Make sure that everything is consistent
[dca5802]572 /* paranoid */ check( cltr->ready_queue );
573
[504a7dc]574 __cfadbg_print_safe(ready_queue, "Kernel : Growing ready queue done\n");
[dca5802]575
[64a7146]576 /* paranoid */ verify( ready_mutate_islocked() );
[b798713]577}
578
[dca5802]579// Shrink the ready queue
[320ec6fc]580void ready_queue_shrink(struct cluster * cltr, int target) {
[64a7146]581 /* paranoid */ verify( ready_mutate_islocked() );
[504a7dc]582 __cfadbg_print_safe(ready_queue, "Kernel : Shrinking ready queue\n");
[dca5802]583
584 // Make sure that everything is consistent
585 /* paranoid */ check( cltr->ready_queue );
586
[b798713]587 with( cltr->ready_queue ) {
[1eb239e4]588 #ifdef USE_SNZI
589 ^(snzi){};
590 #endif
[61d7bec]591
[39fc03e]592 // Remember old count
[dca5802]593 size_t ocount = lanes.count;
[b798713]594
[39fc03e]595 // Find new count
596 // Make sure we always have atleast 1 list
597 lanes.count = target >= 2 ? target * 4: 1;
598 /* paranoid */ verify( ocount >= lanes.count );
[320ec6fc]599 /* paranoid */ verify( lanes.count == target * 4 || target < 2 );
[dca5802]600
601 // for printing count the number of displaced threads
[504a7dc]602 #if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_READY_QUEUE__)
[dca5802]603 __attribute__((unused)) size_t displaced = 0;
604 #endif
[b798713]605
606 // redistribute old data
[dca5802]607 for( idx; (size_t)lanes.count ~ ocount) {
608 // Lock is not strictly needed but makes checking invariants much easier
[1b143de]609 __attribute__((unused)) bool locked = __atomic_try_acquire(&lanes.data[idx].lock);
[b798713]610 verify(locked);
[dca5802]611
612 // As long as we can pop from this lane to push the threads somewhere else in the queue
613 while(!is_empty(lanes.data[idx])) {
[504a7dc]614 struct $thread * thrd;
[343d10e]615 thrd = pop(lanes.data[idx]);
[dca5802]616
[b798713]617 push(cltr, thrd);
[dca5802]618
619 // for printing count the number of displaced threads
[504a7dc]620 #if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_READY_QUEUE__)
[dca5802]621 displaced++;
622 #endif
[b798713]623 }
624
[dca5802]625 // Unlock the lane
626 __atomic_unlock(&lanes.data[idx].lock);
[b798713]627
628 // TODO print the queue statistics here
629
[dca5802]630 ^(lanes.data[idx]){};
[b798713]631 }
632
[504a7dc]633 __cfadbg_print_safe(ready_queue, "Kernel : Shrinking ready queue displaced %zu threads\n", displaced);
[c84b4be]634
[dca5802]635 // Allocate new array (uses realloc and memcpies the data)
[39fc03e]636 lanes.data = alloc(lanes.data, lanes.count);
[b798713]637
638 // Fix the moved data
[dca5802]639 for( idx; (size_t)lanes.count ) {
640 fix(lanes.data[idx]);
[b798713]641 }
[c84b4be]642
[1eb239e4]643 #ifdef USE_SNZI
644 // Re-create the snzi
645 snzi{ log2( lanes.count / 8 ) };
646 for( idx; (size_t)lanes.count ) {
647 if( !is_empty(lanes.data[idx]) ) {
648 arrive(snzi, idx);
649 }
[61d7bec]650 }
[1eb239e4]651 #endif
[b798713]652 }
653
654 // Make sure that everything is consistent
[dca5802]655 /* paranoid */ check( cltr->ready_queue );
656
[504a7dc]657 __cfadbg_print_safe(ready_queue, "Kernel : Shrinking ready queue done\n");
[64a7146]658 /* paranoid */ verify( ready_mutate_islocked() );
[8834751]659}
Note: See TracBrowser for help on using the repository browser.