Changes in / [97392b69:ee06db5c]
- Files:
-
- 6 deleted
- 18 edited
-
doc/theses/thierry_delisle_PhD/code/bitbench/select.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/process.sh (deleted)
-
doc/theses/thierry_delisle_PhD/code/relaxed_list.cpp (modified) (17 diffs)
-
doc/theses/thierry_delisle_PhD/code/relaxed_list.hpp (modified) (17 diffs)
-
doc/theses/thierry_delisle_PhD/code/runperf.sh (deleted)
-
doc/theses/thierry_delisle_PhD/code/snzi.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/snzm.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/utils.hpp (modified) (2 diffs)
-
libcfa/src/Makefile.am (modified) (1 diff)
-
libcfa/src/Makefile.in (modified) (4 diffs)
-
libcfa/src/bits/debug.hfa (modified) (1 diff)
-
libcfa/src/bits/defs.hfa (modified) (1 diff)
-
libcfa/src/concurrency/invoke.h (modified) (3 diffs)
-
libcfa/src/concurrency/io.cfa (modified) (1 diff)
-
libcfa/src/concurrency/kernel.cfa (modified) (15 diffs)
-
libcfa/src/concurrency/kernel.hfa (modified) (3 diffs)
-
libcfa/src/concurrency/kernel_private.hfa (modified) (2 diffs)
-
libcfa/src/concurrency/monitor.cfa (modified) (5 diffs)
-
libcfa/src/concurrency/preemption.cfa (modified) (1 diff)
-
libcfa/src/concurrency/ready_queue.cfa (deleted)
-
libcfa/src/concurrency/thread.cfa (modified) (1 diff)
-
libcfa/src/stdhdr/assert.h (modified) (1 diff)
-
tests/concurrent/examples/datingService.cfa (modified) (2 diffs)
-
tests/concurrent/waitfor/when.cfa (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
doc/theses/thierry_delisle_PhD/code/relaxed_list.cpp
r97392b69 ree06db5c 57 57 size_t valmax = 0; 58 58 size_t valmin = 100000000ul; 59 struct {60 size_t val = 0;61 size_t cnt = 0;62 } comp;63 struct {64 size_t val = 0;65 size_t cnt = 0;66 } subm;67 59 }; 68 60 … … 75 67 std::atomic_size_t valmax = { 0 }; 76 68 std::atomic_size_t valmin = { 100000000ul }; 77 struct {78 std::atomic_size_t val = { 0 };79 std::atomic_size_t cnt = { 0 };80 } comp;81 struct {82 std::atomic_size_t val = { 0 };83 std::atomic_size_t cnt = { 0 };84 } subm;85 69 }; 86 70 … … 112 96 global.crc_out += local.crc_out; 113 97 114 global.comp.val += local.comp.val;115 global.comp.cnt += local.comp.cnt;116 global.subm.val += local.subm.val;117 global.subm.cnt += local.subm.cnt;118 119 98 atomic_max(global.valmax, local.valmax); 120 99 atomic_min(global.valmin, local.valmin); … … 127 106 auto before = Clock::now(); 128 107 barrier.wait(0); 129 bool is_tty = isatty(STDOUT_FILENO);130 108 131 109 while(true) { … … 137 115 break; 138 116 } 139 if(is_tty) { 140 std::cout << "\r" << std::setprecision(4) << durr.count(); 141 std::cout.flush(); 142 } 117 std::cout << "\r" << std::setprecision(4) << durr.count(); 118 std::cout.flush(); 143 119 } 144 120 … … 183 159 auto dur_nano = duration_cast<std::nano>(1.0); 184 160 185 if(global.valmax != 0) {186 std::cout << "Max runs : " << global.valmax << "\n";187 std::cout << "Min runs : " << global.valmin << "\n";188 }189 if(global.comp.cnt != 0) {190 std::cout << "Submit count : " << global.subm.cnt << "\n";191 std::cout << "Submit average: " << ((double(global.subm.val)) / global.subm.cnt) << "\n";192 std::cout << "Complete count: " << global.comp.cnt << "\n";193 std::cout << "Complete avg : " << ((double(global.comp.val)) / global.comp.cnt) << "\n";194 }195 161 std::cout << "Duration : " << duration << "s\n"; 196 162 std::cout << "ns/Op : " << ( dur_nano / ops_thread )<< "\n"; … … 198 164 std::cout << "Ops/sec : " << ops_sec << "\n"; 199 165 std::cout << "Total ops : " << ops << "(" << global.in << "i, " << global.out << "o, " << global.empty << "e)\n"; 166 if(global.valmax != 0) { 167 std::cout << "Max runs : " << global.valmax << "\n"; 168 std::cout << "Min runs : " << global.valmin << "\n"; 169 } 200 170 #ifndef NO_STATS 201 171 relaxed_list<Node>::stats_print(std::cout); … … 425 395 426 396 enable_stats = false; 427 }428 429 print_stats(duration, nthread, global);430 }431 432 // ================================================================================================433 struct __attribute__((aligned(64))) Slot {434 Node * volatile node;435 };436 437 __attribute__((noinline)) void runProducer_body(438 std::atomic<bool>& done,439 Random & rand,440 Slot * slots,441 int nslots,442 local_stat_t & local,443 relaxed_list<Node> & list444 ) {445 while(__builtin_expect(!done.load(std::memory_order_relaxed), true)) {446 447 Node * node = list.pop();448 if(!node) {449 local.empty ++;450 continue;451 }452 453 local.crc_out += node->value;454 local.out++;455 456 if(node->id == 0) {457 unsigned cnt = 0;458 for(int i = 0; i < nslots; i++) {459 Node * found = __atomic_exchange_n( &slots[i].node, nullptr, __ATOMIC_SEQ_CST );460 if( found ) {461 local.crc_in += found->value;462 local.in++;463 cnt++;464 list.push( found );465 }466 }467 468 local.crc_in += node->value;469 local.in++;470 list.push( node );471 472 local.comp.cnt++;473 local.comp.val += cnt;474 }475 else {476 unsigned len = 0;477 while(true) {478 auto off = rand.next();479 for(int i = 0; i < nslots; i++) {480 Node * expected = nullptr;481 int idx = (i + off) % nslots;482 Slot & slot = slots[ idx ];483 if(484 slot.node == nullptr &&485 __atomic_compare_exchange_n( &slot.node, &expected, node, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST )486 ) {487 local.subm.cnt++;488 local.subm.val += len;489 goto LOOP;490 }491 assert( expected != node );492 len++;493 }494 }495 }496 497 LOOP:;498 }499 }500 501 void runProducer(unsigned nthread, unsigned nqueues, double duration, unsigned nnodes) {502 std::cout << "Producer Benchmark" << std::endl;503 504 // Barrier for synchronization505 barrier_t barrier(nthread + 1);506 507 // Data to check everything is OK508 global_stat_t global;509 510 // Flag to signal termination511 std::atomic_bool done = { false };512 513 std::cout << "Initializing ";514 515 int nslots = nnodes * 4;516 Slot * slots = new Slot[nslots];517 std::cout << nnodes << " nodes (" << nslots << " slots)" << std::endl;518 519 // List being tested520 relaxed_list<Node> list = { nthread * nqueues };521 {522 Random rand(rdtscl());523 for(unsigned i = 0; i < nnodes; i++) {524 Node * node = new Node(rand.next() % 100);525 node->id = i;526 global.crc_in += node->value;527 list.push(node);528 }529 530 for(int i = 0; i < nslots; i++) {531 slots[i].node = nullptr;532 }533 }534 535 {536 enable_stats = true;537 538 std::thread * threads[nthread];539 unsigned i = 1;540 for(auto & t : threads) {541 t = new std::thread([&done, &list, &barrier, &global, slots, nslots](unsigned tid) {542 Random rand(tid + rdtscl());543 544 local_stat_t local;545 barrier.wait(tid);546 547 // EXPERIMENT START548 549 runProducer_body(done, rand, slots, nslots, local, list);550 551 // EXPERIMENT END552 553 barrier.wait(tid);554 555 tally_stats(global, local);556 }, i++);557 }558 559 waitfor(duration, barrier, done);560 561 for(auto t : threads) {562 t->join();563 delete t;564 }565 566 enable_stats = false;567 }568 569 {570 while(Node * node = list.pop()) {571 global.crc_out += node->value;572 delete node;573 }574 575 for(int i = 0; i < nslots; i++) {576 delete slots[i].node;577 }578 579 delete [] slots;580 397 } 581 398 … … 704 521 print_stats(duration, nthread, global); 705 522 706 //save_fairness(data_out.get(), 100, nthread, width, length, output);523 save_fairness(data_out.get(), 100, nthread, width, length, output); 707 524 } 708 525 … … 730 547 Churn, 731 548 PingPong, 732 Producer,733 549 Fairness, 734 550 NONE … … 761 577 case PingPong: 762 578 nnodes = 1; 579 nslots = 1; 763 580 switch(argc - optind) { 764 581 case 0: break; … … 774 591 break; 775 592 default: 776 std::cerr << "'PingPong' benchmark doesn't accept more than 1 extra arguments" << std::endl; 777 goto usage; 778 } 779 break; 780 case Producer: 781 nnodes = 32; 782 switch(argc - optind) { 783 case 0: break; 784 case 1: 785 try { 786 arg = optarg = argv[optind]; 787 nnodes = stoul(optarg, &len); 788 if(len != arg.size()) { throw std::invalid_argument(""); } 789 } catch(std::invalid_argument &) { 790 std::cerr << "Number of nodes must be a positive integer, was " << arg << std::endl; 791 goto usage; 792 } 793 break; 794 default: 795 std::cerr << "'Producer' benchmark doesn't accept more than 1 extra arguments" << std::endl; 593 std::cerr << "'PingPong' benchmark doesn't accept more than 2 extra arguments" << std::endl; 796 594 goto usage; 797 595 } … … 864 662 break; 865 663 } 866 if(iequals(arg, "producer")) {867 benchmark = Producer;868 break;869 }870 664 if(iequals(arg, "fairness")) { 871 665 benchmark = Fairness; … … 908 702 std::cerr << "Usage: " << argv[0] << ": [options] -b churn [NNODES] [NSLOTS = NNODES]" << std::endl; 909 703 std::cerr << " or: " << argv[0] << ": [options] -b pingpong [NNODES]" << std::endl; 910 std::cerr << " or: " << argv[0] << ": [options] -b producer [NNODES]" << std::endl;911 704 std::cerr << std::endl; 912 705 std::cerr << " -d, --duration=DURATION Duration of the experiment, in seconds" << std::endl; … … 921 714 922 715 std::cout << "Running " << nthreads << " threads (" << (nthreads * nqueues) << " queues) for " << duration << " seconds" << std::endl; 923 std::cout << "Relaxed list variant: " << relaxed_list<Node>::name() << std::endl;924 716 switch(benchmark) { 925 717 case Churn: … … 928 720 case PingPong: 929 721 runPingPong(nthreads, nqueues, duration, nnodes); 930 break;931 case Producer:932 runProducer(nthreads, nqueues, duration, nnodes);933 722 break; 934 723 case Fairness: … … 1012 801 } 1013 802 1014 //void save_fairness(const int data[], int factor, unsigned nthreads, size_t columns, size_t rows, const std::string & output) {1015 //std::ofstream os(output);1016 //os << "<html>\n";1017 //os << "<head>\n";1018 //os << "<style>\n";1019 //os << "</style>\n";1020 //os << "</head>\n";1021 //os << "<body>\n";1022 //os << "<table style=\"width=100%\">\n";1023 1024 //size_t idx = 0;1025 //for(size_t r = 0ul; r < rows; r++) {1026 //os << "<tr>\n";1027 //for(size_t c = 0ul; c < columns; c++) {1028 //os << "<td class=\"custom custom" << data[idx] << "\"></td>\n";1029 //idx++;1030 //}1031 //os << "</tr>\n";1032 //}1033 1034 //os << "</table>\n";1035 //os << "</body>\n";1036 //os << "</html>\n";1037 //os << std::endl;1038 //}1039 1040 //#include <png.h>1041 //#include <setjmp.h>803 void save_fairness(const int data[], int factor, unsigned nthreads, size_t columns, size_t rows, const std::string & output) { 804 std::ofstream os(output); 805 os << "<html>\n"; 806 os << "<head>\n"; 807 os << "<style>\n"; 808 os << "</style>\n"; 809 os << "</head>\n"; 810 os << "<body>\n"; 811 os << "<table style=\"width=100%\">\n"; 812 813 size_t idx = 0; 814 for(size_t r = 0ul; r < rows; r++) { 815 os << "<tr>\n"; 816 for(size_t c = 0ul; c < columns; c++) { 817 os << "<td class=\"custom custom" << data[idx] << "\"></td>\n"; 818 idx++; 819 } 820 os << "</tr>\n"; 821 } 822 823 os << "</table>\n"; 824 os << "</body>\n"; 825 os << "</html>\n"; 826 os << std::endl; 827 } 828 829 #include <png.h> 830 #include <setjmp.h> 1042 831 1043 832 /* -
doc/theses/thierry_delisle_PhD/code/relaxed_list.hpp
r97392b69 ree06db5c 1 1 #pragma once 2 3 #define MACRO_XSTR(s) MACRO_STR(s)4 #define MACRO_STR(s) #s5 6 #define VANILLA 07 #define SNZI 18 #define BITMASK 29 #define DISCOVER 310 #define SNZM 411 12 #ifndef VARIANT13 #define VARIANT VANILLA14 #endif15 2 16 3 #ifndef NO_STATS … … 18 5 #endif 19 6 20 #include <cmath>21 7 #include <memory> 22 8 #include <mutex> … … 25 11 #include "assert.hpp" 26 12 #include "utils.hpp" 27 #include "snzi.hpp"28 #include "snzm.hpp"29 13 30 14 using namespace std; 15 16 struct spinlock_t { 17 std::atomic_bool ll = { false }; 18 19 inline void lock() { 20 while( __builtin_expect(ll.exchange(true),false) ) { 21 while(ll.load(std::memory_order_relaxed)) 22 asm volatile("pause"); 23 } 24 } 25 26 inline bool try_lock() { 27 return false == ll.exchange(true); 28 } 29 30 inline void unlock() { 31 ll.store(false, std::memory_order_release); 32 } 33 34 inline explicit operator bool() { 35 return ll.load(std::memory_order_relaxed); 36 } 37 }; 38 39 static inline bool bts(std::atomic_size_t & target, size_t bit ) { 40 //* 41 int result = 0; 42 asm volatile( 43 "LOCK btsq %[bit], %[target]\n\t" 44 :"=@ccc" (result) 45 : [target] "m" (target), [bit] "r" (bit) 46 ); 47 return result != 0; 48 /*/ 49 size_t mask = 1ul << bit; 50 size_t ret = target.fetch_or(mask, std::memory_order_relaxed); 51 return (ret & mask) != 0; 52 //*/ 53 } 54 55 static inline bool btr(std::atomic_size_t & target, size_t bit ) { 56 //* 57 int result = 0; 58 asm volatile( 59 "LOCK btrq %[bit], %[target]\n\t" 60 :"=@ccc" (result) 61 : [target] "m" (target), [bit] "r" (bit) 62 ); 63 return result != 0; 64 /*/ 65 size_t mask = 1ul << bit; 66 size_t ret = target.fetch_and(~mask, std::memory_order_relaxed); 67 return (ret & mask) != 0; 68 //*/ 69 } 31 70 32 71 extern bool enable_stats; … … 41 80 size_t success = 0; 42 81 size_t mask_attempt = 0; 43 size_t mask_reset = 0;44 82 } pop; 45 83 }; … … 68 106 69 107 public: 70 static const char * name() {71 const char * names[] = {72 "VANILLA",73 "SNZI",74 "BITMASK",75 "SNZI + DISCOVERED MASK",76 "SNZI + MASK"77 };78 return names[VARIANT];79 }80 81 108 relaxed_list(unsigned numLists) 82 109 : lists(new intrusive_queue_t[numLists]) 83 110 , numLists(numLists) 84 #if VARIANT == SNZI85 , snzi( std::log2( numLists / 8 ), 2 )86 #elif VARIANT == SNZM || VARIANT == DISCOVER87 , snzm( numLists )88 #endif89 111 { 90 112 assertf(7 * 8 * 8 >= numLists, "List currently only supports 448 sublists"); … … 117 139 if( !lists[i].lock.try_lock() ) continue; 118 140 119 #if VARIANT != SNZM && VARIANT != SNZI && VARIANT != DISCOVER 120 __attribute__((unused)) int num = numNonEmpty; 121 #endif 141 __attribute__((unused)) int num = numNonEmpty; 122 142 123 143 // Actually push it 124 144 if(lists[i].push(node)) { 125 #if VARIANT == DISCOVER 126 size_t qword = i >> 6ull; 127 size_t bit = i & 63ull; 128 assert(qword == 0); 129 bts(tls.mask, bit); 130 snzm.arrive(i); 131 #elif VARIANT == SNZI 132 snzi.arrive(i); 133 #elif VARIANT == SNZM 134 snzm.arrive(i); 135 #elif VARIANT == BITMASK 136 numNonEmpty++; 137 size_t qword = i >> 6ull; 138 size_t bit = i & 63ull; 139 assertf((list_mask[qword] & (1ul << bit)) == 0, "Before set %zu:%zu (%u), %zx & %zx", qword, bit, i, list_mask[qword].load(), (1ul << bit)); 140 __attribute__((unused)) bool ret = bts(list_mask[qword], bit); 141 assert(!ret); 142 assertf((list_mask[qword] & (1ul << bit)) != 0, "After set %zu:%zu (%u), %zx & %zx", qword, bit, i, list_mask[qword].load(), (1ul << bit)); 143 #else 144 numNonEmpty++; 145 #endif 146 } 147 #if VARIANT != SNZM && VARIANT != SNZI && VARIANT != DISCOVER 148 assert(numNonEmpty <= (int)numLists); 149 #endif 145 numNonEmpty++; 146 size_t qword = i >> 6ull; 147 size_t bit = i & 63ull; 148 assertf((list_mask[qword] & (1ul << bit)) == 0, "Before set %zu:%zu (%u), %zx & %zx", qword, bit, i, list_mask[qword].load(), (1ul << bit)); 149 __attribute__((unused)) bool ret = bts(list_mask[qword], bit); 150 assert(!ret); 151 assertf((list_mask[qword] & (1ul << bit)) != 0, "After set %zu:%zu (%u), %zx & %zx", qword, bit, i, list_mask[qword].load(), (1ul << bit)); 152 } 153 assert(numNonEmpty <= (int)numLists); 150 154 151 155 // Unlock and return … … 154 158 #ifndef NO_STATS 155 159 tls.pick.push.success++; 156 #if VARIANT != SNZM && VARIANT != SNZI && VARIANT != DISCOVER 157 tls.empty.push.value += num; 158 tls.empty.push.count += 1; 159 #endif 160 tls.empty.push.value += num; 161 tls.empty.push.count += 1; 160 162 #endif 161 163 return; … … 164 166 165 167 __attribute__((noinline, hot)) node_t * pop() { 166 #if VARIANT == DISCOVER 167 assert(numLists <= 64); 168 while(snzm.query()) { 169 tls.pick.pop.mask_attempt++; 170 unsigned i, j; 171 { 172 // Pick first list totally randomly 173 i = tls.rng.next() % numLists; 174 175 // Pick the other according to the bitmask 176 unsigned r = tls.rng.next(); 177 178 size_t mask = tls.mask.load(std::memory_order_relaxed); 179 if(mask == 0) { 180 tls.pick.pop.mask_reset++; 181 mask = (1U << numLists) - 1; 182 tls.mask.store(mask, std::memory_order_relaxed); 183 } 184 185 unsigned b = rand_bit(r, mask); 186 187 assertf(b < 64, "%zu %u", mask, b); 188 189 j = b; 190 191 assert(j < numLists); 192 } 193 194 if(auto node = try_pop(i, j)) return node; 195 } 196 #elif VARIANT == SNZI 197 while(snzi.query()) { 198 // Pick two lists at random 199 int i = tls.rng.next() % numLists; 200 int j = tls.rng.next() % numLists; 201 202 if(auto node = try_pop(i, j)) return node; 203 } 204 #elif VARIANT == SNZM 205 //* 206 while(snzm.query()) { 207 tls.pick.pop.mask_attempt++; 208 unsigned i, j; 209 { 210 // Pick two random number 211 unsigned ri = tls.rng.next(); 212 unsigned rj = tls.rng.next(); 213 214 // Pick two nodes from it 215 unsigned wdxi = ri & snzm.mask; 216 unsigned wdxj = rj & snzm.mask; 217 218 // Get the masks from the nodes 219 size_t maski = snzm.masks(wdxi); 220 size_t maskj = snzm.masks(wdxj); 221 222 if(maski == 0 && maskj == 0) continue; 223 224 #if defined(__BMI2__) 225 uint64_t idxsi = _pext_u64(snzm.indexes, maski); 226 uint64_t idxsj = _pext_u64(snzm.indexes, maskj); 227 228 auto pi = __builtin_popcountll(maski); 229 auto pj = __builtin_popcountll(maskj); 230 231 ri = pi ? ri & ((pi >> 3) - 1) : 0; 232 rj = pj ? rj & ((pj >> 3) - 1) : 0; 233 234 unsigned bi = (idxsi >> (ri << 3)) & 0xff; 235 unsigned bj = (idxsj >> (rj << 3)) & 0xff; 236 #else 237 unsigned bi = rand_bit(ri >> snzm.depth, maski); 238 unsigned bj = rand_bit(rj >> snzm.depth, maskj); 239 #endif 240 241 i = (bi << snzm.depth) | wdxi; 242 j = (bj << snzm.depth) | wdxj; 243 244 /* paranoid */ assertf(i < numLists, "%u %u", bj, wdxi); 245 /* paranoid */ assertf(j < numLists, "%u %u", bj, wdxj); 246 } 247 248 if(auto node = try_pop(i, j)) return node; 249 } 250 /*/ 251 while(snzm.query()) { 252 // Pick two lists at random 253 int i = tls.rng.next() % numLists; 254 int j = tls.rng.next() % numLists; 255 256 if(auto node = try_pop(i, j)) return node; 257 } 258 //*/ 259 #elif VARIANT == BITMASK 168 #if !defined(NO_BITMASK) 169 // for(int r = 0; r < 10 && numNonEmpty != 0; r++) { 170 // // Pick two lists at random 171 // unsigned i = tls.rng.next() % numLists; 172 // unsigned j = tls.rng.next() % numLists; 173 174 // if(auto node = try_pop(i, j)) return node; 175 // } 260 176 int nnempty; 261 177 while(0 != (nnempty = numNonEmpty)) { 262 178 tls.pick.pop.mask_attempt++; 263 179 unsigned i, j; 180 // if( numLists < 4 || (numLists / nnempty) < 4 ) { 181 // // Pick two lists at random 182 // i = tls.rng.next() % numLists; 183 // j = tls.rng.next() % numLists; 184 // } else 264 185 { 186 #ifndef NO_STATS 187 // tls.pick.push.mask_attempt++; 188 #endif 189 265 190 // Pick two lists at random 266 191 unsigned num = ((numLists - 1) >> 6) + 1; … … 311 236 #endif 312 237 313 #if VARIANT == DISCOVER314 if(lists[i].ts() > 0) bts(tls.mask, i); else btr(tls.mask, i);315 if(lists[j].ts() > 0) bts(tls.mask, j); else btr(tls.mask, j);316 #endif317 318 238 // Pick the bet list 319 239 int w = i; … … 329 249 if( !list.lock.try_lock() ) return nullptr; 330 250 331 #if VARIANT != SNZM && VARIANT != SNZI && VARIANT != DISCOVER 332 __attribute__((unused)) int num = numNonEmpty; 333 #endif 251 __attribute__((unused)) int num = numNonEmpty; 334 252 335 253 // If list is empty, unlock and retry … … 346 264 347 265 if(emptied) { 348 #if VARIANT == DISCOVER 349 size_t qword = w >> 6ull; 350 size_t bit = w & 63ull; 351 assert(qword == 0); 352 __attribute__((unused)) bool ret = btr(tls.mask, bit); 353 snzm.depart(w); 354 #elif VARIANT == SNZI 355 snzi.depart(w); 356 #elif VARIANT == SNZM 357 snzm.depart(w); 358 #elif VARIANT == BITMASK 359 numNonEmpty--; 360 size_t qword = w >> 6ull; 361 size_t bit = w & 63ull; 362 assert((list_mask[qword] & (1ul << bit)) != 0); 363 __attribute__((unused)) bool ret = btr(list_mask[qword], bit); 364 assert(ret); 365 assert((list_mask[qword] & (1ul << bit)) == 0); 366 #else 367 numNonEmpty--; 368 #endif 266 numNonEmpty--; 267 size_t qword = w >> 6ull; 268 size_t bit = w & 63ull; 269 assert((list_mask[qword] & (1ul << bit)) != 0); 270 __attribute__((unused)) bool ret = btr(list_mask[qword], bit); 271 assert(ret); 272 assert((list_mask[qword] & (1ul << bit)) == 0); 369 273 } 370 274 371 275 // Unlock and return 372 276 list.lock.unlock(); 373 #if VARIANT != SNZM && VARIANT != SNZI && VARIANT != DISCOVER 374 assert(numNonEmpty >= 0); 375 #endif 277 assert(numNonEmpty >= 0); 376 278 #ifndef NO_STATS 377 279 tls.pick.pop.success++; 378 #if VARIANT != SNZM && VARIANT != SNZI && VARIANT != DISCOVER 379 tls.empty.pop.value += num; 380 tls.empty.pop.count += 1; 381 #endif 280 tls.empty.pop.value += num; 281 tls.empty.pop.count += 1; 382 282 #endif 383 283 return node; … … 396 296 size_t push = 0; 397 297 size_t pop = 0; 298 // size_t value = 0; 299 // size_t count = 0; 398 300 }; 399 301 … … 515 417 pick_stat pick; 516 418 empty_stat empty; 517 __attribute__((aligned(64))) std::atomic_size_t mask = { 0 };518 419 } tls; 519 420 421 public: 422 std::atomic_int numNonEmpty = { 0 }; // number of non-empty lists 423 std::atomic_size_t list_mask[7] = { {0}, {0}, {0}, {0}, {0}, {0}, {0} }; // which queues are empty 520 424 private: 521 425 __attribute__((aligned(64))) std::unique_ptr<intrusive_queue_t []> lists; 522 426 const unsigned numLists; 523 private:524 #if VARIANT == SNZI525 snzi_t snzi;526 #elif VARIANT == SNZM || VARIANT == DISCOVER527 snzm_t snzm;528 #else529 std::atomic_int numNonEmpty = { 0 }; // number of non-empty lists530 #endif531 #if VARIANT == BITMASK532 std::atomic_size_t list_mask[7] = { {0}, {0}, {0}, {0}, {0}, {0}, {0} }; // which queues are empty533 #endif534 427 535 428 public: … … 551 444 global_stats.pick.pop .success += tls.pick.pop.success; 552 445 global_stats.pick.pop .mask_attempt += tls.pick.pop.mask_attempt; 553 global_stats.pick.pop .mask_reset += tls.pick.pop.mask_reset;554 446 555 447 global_stats.qstat.push.value += tls.empty.push.value; … … 570 462 std::atomic_size_t success = { 0 }; 571 463 std::atomic_size_t mask_attempt = { 0 }; 572 std::atomic_size_t mask_reset = { 0 };573 464 } pop; 574 465 } pick; … … 592 483 void stats_print_local(std::ostream & os ) { 593 484 std::cout << "----- Relaxed List Stats -----" << std::endl; 594 //{595 //ssize_t diff = 0;596 //size_t num = 0;597 //ssize_t max = 0;598 599 //for(size_t i = 0; i < numLists; i++) {600 //const auto & list = lists[i];601 //diff+= list.s.diff;602 //num ++;603 //max = std::abs(max) > std::abs(list.s.diff) ? max : list.s.diff;604 //os << "Local Q ops : " << (list.s.push + list.s.pop) << "(" << list.s.push << "i, " << list.s.pop << "o)\n";605 //}606 607 //os << "Difference : " << ssize_t(double(diff) / num ) << " avg\t" << max << "max" << std::endl;608 //}485 { 486 ssize_t diff = 0; 487 size_t num = 0; 488 ssize_t max = 0; 489 490 for(size_t i = 0; i < numLists; i++) { 491 const auto & list = lists[i]; 492 diff+= list.s.diff; 493 num ++; 494 max = std::abs(max) > std::abs(list.s.diff) ? max : list.s.diff; 495 os << "Local Q ops : " << (list.s.push + list.s.pop) << "(" << list.s.push << "i, " << list.s.pop << "o)\n"; 496 } 497 498 os << "Difference : " << ssize_t(double(diff) / num ) << " avg\t" << max << "max" << std::endl; 499 } 609 500 610 501 const auto & global = global_stats; … … 613 504 double pop_sur = (100.0 * double(global.pick.pop .success) / global.pick.pop .attempt); 614 505 double mpop_sur = (100.0 * double(global.pick.pop .success) / global.pick.pop .mask_attempt); 615 double rpop_sur = (100.0 * double(global.pick.pop .success) / global.pick.pop .mask_reset); 616 617 double push_len = double(global.pick.push.attempt ) / global.pick.push.success; 618 double pop_len = double(global.pick.pop .attempt ) / global.pick.pop .success; 619 double mpop_len = double(global.pick.pop .mask_attempt) / global.pick.pop .success; 620 double rpop_len = double(global.pick.pop .mask_reset ) / global.pick.pop .success; 621 622 os << "Push Pick : " << push_sur << " %, len " << push_len << " (" << global.pick.push.attempt << " / " << global.pick.push.success << ")\n"; 623 os << "Pop Pick : " << pop_sur << " %, len " << pop_len << " (" << global.pick.pop .attempt << " / " << global.pick.pop .success << ")\n"; 624 os << "TryPop Pick : " << mpop_sur << " %, len " << mpop_len << " (" << global.pick.pop .mask_attempt << " / " << global.pick.pop .success << ")\n"; 625 os << "Pop M Reset : " << rpop_sur << " %, len " << rpop_len << " (" << global.pick.pop .mask_reset << " / " << global.pick.pop .success << ")\n"; 506 507 os << "Push Pick % : " << push_sur << "(" << global.pick.push.success << " / " << global.pick.push.attempt << ")\n"; 508 os << "Pop Pick % : " << pop_sur << "(" << global.pick.pop .success << " / " << global.pick.pop .attempt << ")\n"; 509 os << "TryPop Pick % : " << mpop_sur << "(" << global.pick.pop .success << " / " << global.pick.pop .mask_attempt << ")\n"; 626 510 627 511 double avgQ_push = double(global.qstat.push.value) / global.qstat.push.count; -
doc/theses/thierry_delisle_PhD/code/utils.hpp
r97392b69 ree06db5c 106 106 } 107 107 108 static inline unsigned rand_bit(unsigned rnum, size_t mask) __attribute__((artificial));109 108 static inline unsigned rand_bit(unsigned rnum, size_t mask) { 110 109 unsigned bit = mask ? rnum % __builtin_popcountl(mask) : 0; … … 144 143 #endif 145 144 } 146 147 struct spinlock_t {148 std::atomic_bool ll = { false };149 150 inline void lock() {151 while( __builtin_expect(ll.exchange(true),false) ) {152 while(ll.load(std::memory_order_relaxed))153 asm volatile("pause");154 }155 }156 157 inline bool try_lock() {158 return false == ll.exchange(true);159 }160 161 inline void unlock() {162 ll.store(false, std::memory_order_release);163 }164 165 inline explicit operator bool() {166 return ll.load(std::memory_order_relaxed);167 }168 };169 170 static inline bool bts(std::atomic_size_t & target, size_t bit ) {171 //*172 int result = 0;173 asm volatile(174 "LOCK btsq %[bit], %[target]\n\t"175 :"=@ccc" (result)176 : [target] "m" (target), [bit] "r" (bit)177 );178 return result != 0;179 /*/180 size_t mask = 1ul << bit;181 size_t ret = target.fetch_or(mask, std::memory_order_relaxed);182 return (ret & mask) != 0;183 //*/184 }185 186 static inline bool btr(std::atomic_size_t & target, size_t bit ) {187 //*188 int result = 0;189 asm volatile(190 "LOCK btrq %[bit], %[target]\n\t"191 :"=@ccc" (result)192 : [target] "m" (target), [bit] "r" (bit)193 );194 return result != 0;195 /*/196 size_t mask = 1ul << bit;197 size_t ret = target.fetch_and(~mask, std::memory_order_relaxed);198 return (ret & mask) != 0;199 //*/200 } -
libcfa/src/Makefile.am
r97392b69 ree06db5c 50 50 thread_headers_nosrc = concurrency/invoke.h 51 51 thread_headers = concurrency/coroutine.hfa concurrency/thread.hfa concurrency/kernel.hfa concurrency/monitor.hfa concurrency/mutex.hfa 52 thread_libsrc = concurrency/CtxSwitch-@ARCHITECTURE@.S concurrency/alarm.cfa concurrency/invoke.c concurrency/io.cfa concurrency/preemption.cfa concurrency/ready_queue.cfa${thread_headers:.hfa=.cfa}52 thread_libsrc = concurrency/CtxSwitch-@ARCHITECTURE@.S concurrency/alarm.cfa concurrency/invoke.c concurrency/io.cfa concurrency/preemption.cfa ${thread_headers:.hfa=.cfa} 53 53 else 54 54 headers = -
libcfa/src/Makefile.in
r97392b69 ree06db5c 166 166 concurrency/CtxSwitch-@ARCHITECTURE@.S concurrency/alarm.cfa \ 167 167 concurrency/invoke.c concurrency/io.cfa \ 168 concurrency/preemption.cfa concurrency/ready_queue.cfa \ 169 concurrency/coroutine.cfa concurrency/thread.cfa \ 170 concurrency/kernel.cfa concurrency/monitor.cfa \ 171 concurrency/mutex.cfa 168 concurrency/preemption.cfa concurrency/coroutine.cfa \ 169 concurrency/thread.cfa concurrency/kernel.cfa \ 170 concurrency/monitor.cfa concurrency/mutex.cfa 172 171 @BUILDLIB_TRUE@am__objects_3 = concurrency/coroutine.lo \ 173 172 @BUILDLIB_TRUE@ concurrency/thread.lo concurrency/kernel.lo \ … … 177 176 @BUILDLIB_TRUE@ concurrency/alarm.lo concurrency/invoke.lo \ 178 177 @BUILDLIB_TRUE@ concurrency/io.lo concurrency/preemption.lo \ 179 @BUILDLIB_TRUE@ concurrency/ready_queue.lo$(am__objects_3)178 @BUILDLIB_TRUE@ $(am__objects_3) 180 179 am_libcfathread_la_OBJECTS = $(am__objects_4) 181 180 libcfathread_la_OBJECTS = $(am_libcfathread_la_OBJECTS) … … 483 482 @BUILDLIB_FALSE@thread_headers = 484 483 @BUILDLIB_TRUE@thread_headers = concurrency/coroutine.hfa concurrency/thread.hfa concurrency/kernel.hfa concurrency/monitor.hfa concurrency/mutex.hfa 485 @BUILDLIB_TRUE@thread_libsrc = concurrency/CtxSwitch-@ARCHITECTURE@.S concurrency/alarm.cfa concurrency/invoke.c concurrency/io.cfa concurrency/preemption.cfa concurrency/ready_queue.cfa${thread_headers:.hfa=.cfa}484 @BUILDLIB_TRUE@thread_libsrc = concurrency/CtxSwitch-@ARCHITECTURE@.S concurrency/alarm.cfa concurrency/invoke.c concurrency/io.cfa concurrency/preemption.cfa ${thread_headers:.hfa=.cfa} 486 485 487 486 #---------------------------------------------------------------------------------------------------------------- … … 621 620 concurrency/$(DEPDIR)/$(am__dirstamp) 622 621 concurrency/preemption.lo: concurrency/$(am__dirstamp) \ 623 concurrency/$(DEPDIR)/$(am__dirstamp)624 concurrency/ready_queue.lo: concurrency/$(am__dirstamp) \625 622 concurrency/$(DEPDIR)/$(am__dirstamp) 626 623 concurrency/coroutine.lo: concurrency/$(am__dirstamp) \ -
libcfa/src/bits/debug.hfa
r97392b69 ree06db5c 52 52 || defined(__CFA_DEBUG_PRINT_IO__) || defined(__CFA_DEBUG_PRINT_IO_CORE__) \ 53 53 || defined(__CFA_DEBUG_PRINT_MONITOR__) || defined(__CFA_DEBUG_PRINT_PREEMPTION__) \ 54 || defined(__CFA_DEBUG_PRINT_RUNTIME_CORE__) || defined(__CFA_DEBUG_PRINT_EXCEPTION__) \ 55 || defined(__CFA_DEBUG_PRINT_READY_QUEUE__) 54 || defined(__CFA_DEBUG_PRINT_RUNTIME_CORE__) || defined(__CFA_DEBUG_PRINT_EXCEPTION__) 56 55 #include <stdio.h> 57 56 #include <unistd.h> -
libcfa/src/bits/defs.hfa
r97392b69 ree06db5c 54 54 return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 ); 55 55 } 56 57 // #define __CFA_NO_BIT_TEST_AND_SET__58 59 #if defined( __i386 )60 static inline bool __atomic_bts(volatile unsigned long int * target, unsigned long int bit ) {61 #if defined(__CFA_NO_BIT_TEST_AND_SET__)62 unsigned long int mask = 1ul << bit;63 unsigned long int ret = __atomic_fetch_or(target, mask, (int)__ATOMIC_RELAXED);64 return (ret & mask) != 0;65 #else66 int result = 0;67 asm volatile(68 "LOCK btsl %[bit], %[target]\n\t"69 : "=@ccc" (result)70 : [target] "m" (*target), [bit] "r" (bit)71 );72 return result != 0;73 #endif74 }75 76 static inline bool __atomic_btr(volatile unsigned long int * target, unsigned long int bit ) {77 #if defined(__CFA_NO_BIT_TEST_AND_SET__)78 unsigned long int mask = 1ul << bit;79 unsigned long int ret = __atomic_fetch_and(target, ~mask, (int)__ATOMIC_RELAXED);80 return (ret & mask) != 0;81 #else82 int result = 0;83 asm volatile(84 "LOCK btrl %[bit], %[target]\n\t"85 :"=@ccc" (result)86 : [target] "m" (*target), [bit] "r" (bit)87 );88 return result != 0;89 #endif90 }91 #elif defined( __x86_64 )92 static inline bool __atomic_bts(volatile unsigned long long int * target, unsigned long long int bit ) {93 #if defined(__CFA_NO_BIT_TEST_AND_SET__)94 unsigned long long int mask = 1ul << bit;95 unsigned long long int ret = __atomic_fetch_or(target, mask, (int)__ATOMIC_RELAXED);96 return (ret & mask) != 0;97 #else98 int result = 0;99 asm volatile(100 "LOCK btsq %[bit], %[target]\n\t"101 : "=@ccc" (result)102 : [target] "m" (*target), [bit] "r" (bit)103 );104 return result != 0;105 #endif106 }107 108 static inline bool __atomic_btr(volatile unsigned long long int * target, unsigned long long int bit ) {109 #if defined(__CFA_NO_BIT_TEST_AND_SET__)110 unsigned long long int mask = 1ul << bit;111 unsigned long long int ret = __atomic_fetch_and(target, ~mask, (int)__ATOMIC_RELAXED);112 return (ret & mask) != 0;113 #else114 int result = 0;115 asm volatile(116 "LOCK btrq %[bit], %[target]\n\t"117 :"=@ccc" (result)118 : [target] "m" (*target), [bit] "r" (bit)119 );120 return result != 0;121 #endif122 }123 #elif defined( __ARM_ARCH )124 #error __atomic_bts and __atomic_btr not implemented for arm125 #else126 #error uknown hardware architecture127 #endif -
libcfa/src/concurrency/invoke.h
r97392b69 ree06db5c 161 161 }; 162 162 163 // Link lists fields164 // instrusive link field for threads165 struct __thread_desc_link {166 struct $thread * next;167 struct $thread * prev;168 volatile unsigned long long ts;169 };170 171 163 struct $thread { 172 164 // Core threading fields … … 200 192 // Link lists fields 201 193 // instrusive link field for threads 202 struct __thread_desc_link link;194 struct $thread * next; 203 195 204 196 struct { … … 226 218 #ifdef __cforall 227 219 extern "Cforall" { 228 229 220 static inline $thread *& get_next( $thread & this ) __attribute__((const)) { 230 return this. link.next;221 return this.next; 231 222 } 232 223 -
libcfa/src/concurrency/io.cfa
r97392b69 ree06db5c 392 392 // This is the tricky case 393 393 // The thread was preempted and now it is on the ready queue 394 395 /* paranoid */ verify( thrd.next != 0p ); // The thread should be the last on the list 394 /* paranoid */ verify( thrd.next == 1p ); // The thread should be the last on the list 396 395 /* paranoid */ verify( this.ready_queue.head == &thrd ); // The thread should be the only thing on the list 397 396 -
libcfa/src/concurrency/kernel.cfa
r97392b69 ree06db5c 120 120 static void __run_thread(processor * this, $thread * dst); 121 121 static $thread * __halt(processor * this); 122 static bool __wake_one(cluster * cltr );122 static bool __wake_one(cluster * cltr, bool was_empty); 123 123 static bool __wake_proc(processor *); 124 124 … … 197 197 self_mon.recursion = 1; 198 198 self_mon_p = &self_mon; 199 link.next = 0p; 200 link.prev = 0p; 199 next = 0p; 201 200 202 201 node.next = 0p; … … 224 223 this.name = name; 225 224 this.cltr = &cltr; 226 id = -1u;227 225 terminated{ 0 }; 228 226 destroyer = 0p; … … 262 260 this.preemption_rate = preemption_rate; 263 261 ready_queue{}; 264 ready_ lock{};262 ready_queue_lock{}; 265 263 266 264 #if !defined(__CFA_NO_STATISTICS__) … … 297 295 __cfadbg_print_safe(runtime_core, "Kernel : core %p starting\n", this); 298 296 299 // register the processor unless it's the main thread which is handled in the boot sequence300 if(this != mainProcessor) {301 this->id = doregister2(this->cltr, this);302 ready_queue_grow( this->cltr );303 }304 305 297 doregister(this->cltr, this); 306 298 … … 326 318 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 327 319 /* paranoid */ verifyf( readyThread->state == Ready || readyThread->preempted != __NO_PREEMPTION, "state : %d, preempted %d\n", readyThread->state, readyThread->preempted); 328 /* paranoid */ verifyf( readyThread-> link.next == 0p, "Expected null got %p", readyThread->link.next );320 /* paranoid */ verifyf( readyThread->next == 0p, "Expected null got %p", readyThread->next ); 329 321 330 322 // We found a thread run it … … 342 334 V( this->terminated ); 343 335 344 // unregister the processor unless it's the main thread which is handled in the boot sequence345 if(this != mainProcessor) {346 ready_queue_shrink( this->cltr );347 unregister2(this->cltr, this);348 }349 else {350 // HACK : the coroutine context switch expects this_thread to be set351 // and it make sense for it to be set in all other cases except here352 // fake it353 kernelTLS.this_thread = mainThread;354 }355 356 336 __cfadbg_print_safe(runtime_core, "Kernel : core %p terminated\n", this); 357 337 358 stats_tls_tally(this->cltr); 338 // HACK : the coroutine context switch expects this_thread to be set 339 // and it make sense for it to be set in all other cases except here 340 // fake it 341 if( this == mainProcessor ) kernelTLS.this_thread = mainThread; 359 342 } 360 343 … … 608 591 // Scheduler routines 609 592 // KERNEL ONLY 610 void __schedule_thread( $thread * thrd ) { 611 /* paranoid */ verify( thrd ); 612 /* paranoid */ verify( thrd->state != Halted ); 593 void __schedule_thread( $thread * thrd ) with( *thrd->curr_cluster ) { 613 594 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 614 595 /* paranoid */ #if defined( __CFA_WITH_VERIFY__ ) 615 /* paranoid */ if( thrd->state == Blocked || thrd->state == Start ) assertf( thrd->preempted == __NO_PREEMPTION,616 "Error inactive thread marked as preempted, state %d, preemption %d\n", thrd->state, thrd->preempted );617 /* paranoid */ if( thrd->preempted != __NO_PREEMPTION ) assertf(thrd->state == Active || thrd->state == Rerun,618 "Error preempted thread marked as not currently running, state %d, preemption %d\n", thrd->state, thrd->preempted );596 /* paranoid */ if( thrd->state == Blocked || thrd->state == Start ) assertf( thrd->preempted == __NO_PREEMPTION, 597 "Error inactive thread marked as preempted, state %d, preemption %d\n", thrd->state, thrd->preempted ); 598 /* paranoid */ if( thrd->preempted != __NO_PREEMPTION ) assertf(thrd->state == Active || thrd->state == Rerun, 599 "Error preempted thread marked as not currently running, state %d, preemption %d\n", thrd->state, thrd->preempted ); 619 600 /* paranoid */ #endif 620 /* paranoid */ verifyf( thrd-> link.next == 0p, "Expected null got %p", thrd->link.next );601 /* paranoid */ verifyf( thrd->next == 0p, "Expected null got %p", thrd->next ); 621 602 622 603 if (thrd->preempted == __NO_PREEMPTION) thrd->state = Ready; 623 604 624 ready_schedule_lock(thrd->curr_cluster, kernelTLS.this_processor); 625 push( thrd->curr_cluster, thrd ); 626 627 __wake_one(thrd->curr_cluster); 628 ready_schedule_unlock(thrd->curr_cluster, kernelTLS.this_processor); 605 lock ( ready_queue_lock __cfaabi_dbg_ctx2 ); 606 bool was_empty = !(ready_queue != 0); 607 append( ready_queue, thrd ); 608 unlock( ready_queue_lock ); 609 610 __wake_one(thrd->curr_cluster, was_empty); 629 611 630 612 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); … … 635 617 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 636 618 637 ready_schedule_lock(this, kernelTLS.this_processor);638 $thread * head = pop( this);639 ready_schedule_unlock(this, kernelTLS.this_processor);619 lock( ready_queue_lock __cfaabi_dbg_ctx2 ); 620 $thread * head = pop_head( ready_queue ); 621 unlock( ready_queue_lock ); 640 622 641 623 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); … … 722 704 // If that is the case, abandon the preemption. 723 705 bool preempted = false; 724 if(thrd-> link.next == 0p) {706 if(thrd->next == 0p) { 725 707 preempted = true; 726 708 thrd->preempted = reason; … … 782 764 pending_preemption = false; 783 765 kernel_thread = pthread_self(); 784 id = -1u;785 766 786 767 runner{ &this }; … … 792 773 mainProcessor = (processor *)&storage_mainProcessor; 793 774 (*mainProcessor){}; 794 795 mainProcessor->id = doregister2(mainCluster, mainProcessor);796 775 797 776 //initialize the global state variables … … 848 827 kernel_stop_preemption(); 849 828 850 unregister2(mainCluster, mainProcessor);851 852 829 // Destroy the main processor and its context in reverse order of construction 853 830 // These were manually constructed so we need manually destroy them 854 831 void ^?{}(processor & this) with( this ){ 855 832 /* paranoid */ verify( this.do_terminate == true ); 856 __cfaabi_dbg_print_safe("Kernel : destroyed main processor context %p\n", &runner);857 833 } 858 834 … … 860 836 861 837 // Final step, destroy the main thread since it is no longer needed 862 863 838 // Since we provided a stack to this taxk it will not destroy anything 864 839 /* paranoid */ verify(mainThread->self_cor.stack.storage == (__stack_t*)(((uintptr_t)&storage_mainThreadCtx)| 0x1)); … … 913 888 914 889 // Wake a thread from the front if there are any 915 static bool __wake_one(cluster * this) { 890 static bool __wake_one(cluster * this, __attribute__((unused)) bool force) { 891 // if we don't want to force check if we know it's false 892 // if( !this->idles.head && !force ) return false; 893 916 894 // First, lock the cluster idle 917 895 lock( this->idle_lock __cfaabi_dbg_ctx2 ); -
libcfa/src/concurrency/kernel.hfa
r97392b69 ree06db5c 60 60 // Cluster from which to get threads 61 61 struct cluster * cltr; 62 unsigned int id;63 62 64 63 // Name of the processor … … 93 92 94 93 // Link lists fields 95 struct __dbg_node_ cltr{96 processor * next;97 processor * prev;94 struct __dbg_node_proc { 95 struct processor * next; 96 struct processor * prev; 98 97 } node; 99 98 … … 122 121 #define CFA_CLUSTER_IO_BUFFLEN_OFFSET 16 123 122 124 125 //-----------------------------------------------------------------------------126 // Cluster Tools127 128 // Cells use by the reader writer lock129 // while not generic it only relies on a opaque pointer130 struct __processor_id;131 132 // Reader-Writer lock protecting the ready-queue133 // while this lock is mostly generic some aspects134 // have been hard-coded to for the ready-queue for135 // simplicity and performance136 struct __clusterRWLock_t {137 // total cachelines allocated138 unsigned int max;139 140 // cachelines currently in use141 volatile unsigned int alloc;142 143 // cachelines ready to itereate over144 // (!= to alloc when thread is in second half of doregister)145 volatile unsigned int ready;146 147 // writer lock148 volatile bool lock;149 150 // data pointer151 __processor_id * data;152 };153 154 void ?{}(__clusterRWLock_t & this);155 void ^?{}(__clusterRWLock_t & this);156 157 // Intrusives lanes which are used by the relaxed ready queue158 struct __attribute__((aligned(128))) __intrusive_lane_t {159 // spin lock protecting the queue160 volatile bool lock;161 162 // anchor for the head and the tail of the queue163 struct __sentinel_t {164 // Link lists fields165 // instrusive link field for threads166 // must be exactly as in $thread167 __thread_desc_link link;168 } before, after;169 170 #if defined(__CFA_WITH_VERIFY__)171 // id of last processor to acquire the lock172 // needed only to check for mutual exclusion violations173 unsigned int last_id;174 175 // number of items on this list176 // needed only to check for deadlocks177 unsigned int count;178 #endif179 180 // Optional statistic counters181 #if !defined(__CFA_NO_SCHED_STATS__)182 struct __attribute__((aligned(64))) {183 // difference between number of push and pops184 ssize_t diff;185 186 // total number of pushes and pops187 size_t push;188 size_t pop ;189 } stat;190 #endif191 };192 193 void ?{}(__intrusive_lane_t & this);194 void ^?{}(__intrusive_lane_t & this);195 196 typedef unsigned long long __cfa_readyQ_mask_t;197 198 // enum {199 // __cfa_ready_queue_mask_size = (64 - sizeof(size_t)) / sizeof(size_t),200 // __cfa_max_ready_queues = __cfa_ready_queue_mask_size * 8 * sizeof(size_t)201 // };202 203 #define __cfa_lane_mask_size ((64 - sizeof(size_t)) / sizeof(__cfa_readyQ_mask_t))204 #define __cfa_max_lanes (__cfa_lane_mask_size * 8 * sizeof(__cfa_readyQ_mask_t))205 206 //TODO adjust cache size to ARCHITECTURE207 // Structure holding the relaxed ready queue208 struct __attribute__((aligned(128))) __ready_queue_t {209 // Data tracking how many/which lanes are used210 // Aligned to 128 for cache locality211 struct {212 // number of non-empty lanes213 volatile size_t count;214 215 // bit mask, set bits indentify which lanes are non-empty216 volatile __cfa_readyQ_mask_t mask[ __cfa_lane_mask_size ];217 } used;218 219 // Data tracking the actual lanes220 // On a seperate cacheline from the used struct since221 // used can change on each push/pop but this data222 // only changes on shrink/grow223 struct __attribute__((aligned(64))) {224 // Arary of lanes225 __intrusive_lane_t * volatile data;226 227 // Number of lanes (empty or not)228 volatile size_t count;229 } lanes;230 231 // Statistics232 #if !defined(__CFA_NO_STATISTICS__)233 __attribute__((aligned(64))) struct {234 struct {235 // Push statistic236 struct {237 // number of attemps at pushing something238 volatile size_t attempt;239 240 // number of successes at pushing241 volatile size_t success;242 } push;243 244 // Pop statistic245 struct {246 // number of reads of the mask247 // picking an empty __cfa_readyQ_mask_t counts here248 // but not as an attempt249 volatile size_t maskrds;250 251 // number of attemps at poping something252 volatile size_t attempt;253 254 // number of successes at poping255 volatile size_t success;256 } pop;257 } pick;258 259 // stats on the "used" struct of the queue260 // tracks average number of queues that are not empty261 // when pushing / poping262 struct {263 volatile size_t value;264 volatile size_t count;265 } used;266 } global_stats;267 268 #endif269 };270 271 void ?{}(__ready_queue_t & this);272 void ^?{}(__ready_queue_t & this);273 274 123 //----------------------------------------------------------------------------- 275 124 // Cluster 276 125 struct cluster { 277 126 // Ready queue locks 278 __ clusterRWLock_t ready_lock;127 __spinlock_t ready_queue_lock; 279 128 280 129 // Ready queue for threads 281 __ ready_queue_tready_queue;130 __queue_t($thread) ready_queue; 282 131 283 132 // Name of the cluster -
libcfa/src/concurrency/kernel_private.hfa
r97392b69 ree06db5c 84 84 //----------------------------------------------------------------------------- 85 85 // Utils 86 #define KERNEL_STORAGE(T,X) __attribute((aligned(__alignof__(T))))static char storage_##X[sizeof(T)]86 #define KERNEL_STORAGE(T,X) static char storage_##X[sizeof(T)] 87 87 88 88 static inline uint32_t __tls_rand() { … … 103 103 void unregister( struct cluster * cltr, struct processor * proc ); 104 104 105 //=======================================================================106 // Cluster lock API107 //=======================================================================108 struct __attribute__((aligned(64))) __processor_id {109 processor * volatile handle;110 volatile bool lock;111 };112 113 // Lock-Free registering/unregistering of threads114 // Register a processor to a given cluster and get its unique id in return115 unsigned doregister2( struct cluster * cltr, struct processor * proc );116 117 // Unregister a processor from a given cluster using its id, getting back the original pointer118 void unregister2( struct cluster * cltr, struct processor * proc );119 120 //=======================================================================121 // Reader-writer lock implementation122 // Concurrent with doregister/unregister,123 // i.e., threads can be added at any point during or between the entry/exit124 125 //-----------------------------------------------------------------------126 // simple spinlock underlying the RWLock127 // Blocking acquire128 static inline void __atomic_acquire(volatile bool * ll) {129 while( __builtin_expect(__atomic_exchange_n(ll, (bool)true, __ATOMIC_SEQ_CST), false) ) {130 while(__atomic_load_n(ll, (int)__ATOMIC_RELAXED))131 asm volatile("pause");132 }133 /* paranoid */ verify(*ll);134 }135 136 // Non-Blocking acquire137 static inline bool __atomic_try_acquire(volatile bool * ll) {138 return !__atomic_exchange_n(ll, (bool)true, __ATOMIC_SEQ_CST);139 }140 141 // Release142 static inline void __atomic_unlock(volatile bool * ll) {143 /* paranoid */ verify(*ll);144 __atomic_store_n(ll, (bool)false, __ATOMIC_RELEASE);145 }146 147 //-----------------------------------------------------------------------148 // Reader side : acquire when using the ready queue to schedule but not149 // creating/destroying queues150 static inline void ready_schedule_lock( struct cluster * cltr, struct processor * proc) with(cltr->ready_lock) {151 unsigned iproc = proc->id;152 /*paranoid*/ verify(data[iproc].handle == proc);153 /*paranoid*/ verify(iproc < ready);154 155 // Step 1 : make sure no writer are in the middle of the critical section156 while(__atomic_load_n(&lock, (int)__ATOMIC_RELAXED))157 asm volatile("pause");158 159 // Fence needed because we don't want to start trying to acquire the lock160 // before we read a false.161 // Not needed on x86162 // std::atomic_thread_fence(std::memory_order_seq_cst);163 164 // Step 2 : acquire our local lock165 __atomic_acquire( &data[iproc].lock );166 /*paranoid*/ verify(data[iproc].lock);167 }168 169 static inline void ready_schedule_unlock( struct cluster * cltr, struct processor * proc) with(cltr->ready_lock) {170 unsigned iproc = proc->id;171 /*paranoid*/ verify(data[iproc].handle == proc);172 /*paranoid*/ verify(iproc < ready);173 /*paranoid*/ verify(data[iproc].lock);174 __atomic_unlock(&data[iproc].lock);175 }176 177 //-----------------------------------------------------------------------178 // Writer side : acquire when changing the ready queue, e.g. adding more179 // queues or removing them.180 uint_fast32_t ready_mutate_lock( struct cluster & cltr );181 182 void ready_mutate_unlock( struct cluster & cltr, uint_fast32_t /* value returned by lock */ );183 184 //=======================================================================185 // Ready-Queue API186 //-----------------------------------------------------------------------187 // push thread onto a ready queue for a cluster188 // returns true if the list was previously empty, false otherwise189 __attribute__((hot)) bool push(struct cluster * cltr, struct $thread * thrd);190 191 //-----------------------------------------------------------------------192 // pop thread from the ready queue of a cluster193 // returns 0p if empty194 __attribute__((hot)) struct $thread * pop(struct cluster * cltr);195 196 //-----------------------------------------------------------------------197 // Increase the width of the ready queue (number of lanes) by 4198 void ready_queue_grow (struct cluster * cltr);199 200 //-----------------------------------------------------------------------201 // Decrease the width of the ready queue (number of lanes) by 4202 void ready_queue_shrink(struct cluster * cltr);203 204 //-----------------------------------------------------------------------205 // Statics call at the end of each thread to register statistics206 #if !defined(__CFA_NO_STATISTICS__)207 void stats_tls_tally(struct cluster * cltr);208 #else209 static inline void stats_tls_tally(struct cluster * cltr) {}210 #endif211 212 105 // Local Variables: // 213 106 // mode: c // -
libcfa/src/concurrency/monitor.cfa
r97392b69 ree06db5c 114 114 115 115 // Some one else has the monitor, wait in line for it 116 /* paranoid */ verify( thrd-> link.next == 0p );116 /* paranoid */ verify( thrd->next == 0p ); 117 117 append( this->entry_queue, thrd ); 118 /* paranoid */ verify( thrd-> link.next == 1p );118 /* paranoid */ verify( thrd->next == 1p ); 119 119 120 120 unlock( this->lock ); … … 199 199 200 200 // Some one else has the monitor, wait in line for it 201 /* paranoid */ verify( thrd-> link.next == 0p );201 /* paranoid */ verify( thrd->next == 0p ); 202 202 append( this->entry_queue, thrd ); 203 /* paranoid */ verify( thrd-> link.next == 1p );203 /* paranoid */ verify( thrd->next == 1p ); 204 204 unlock( this->lock ); 205 205 … … 761 761 $thread * new_owner = pop_head( this->entry_queue ); 762 762 /* paranoid */ verifyf( !this->owner || kernelTLS.this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this ); 763 /* paranoid */ verify( !new_owner || new_owner-> link.next == 0p );763 /* paranoid */ verify( !new_owner || new_owner->next == 0p ); 764 764 __set_owner( this, new_owner ); 765 765 … … 883 883 } 884 884 885 __cfaabi_dbg_print_safe( "Kernel : Runing %i (%p)\n", ready2run, ready2run ? (thread*)node->waiting_thread : (thread*)0p );885 __cfaabi_dbg_print_safe( "Kernel : Runing %i (%p)\n", ready2run, ready2run ? node->waiting_thread : 0p ); 886 886 return ready2run ? node->waiting_thread : 0p; 887 887 } … … 907 907 // For each thread in the entry-queue 908 908 for( $thread ** thrd_it = &entry_queue.head; 909 (*thrd_it)!= 1p;910 thrd_it = &(*thrd_it)-> link.next909 *thrd_it != 1p; 910 thrd_it = &(*thrd_it)->next 911 911 ) { 912 912 // For each acceptable check if it matches -
libcfa/src/concurrency/preemption.cfa
r97392b69 ree06db5c 121 121 // If there are still alarms pending, reset the timer 122 122 if( & (*alarms)`first ) { 123 __cfa dbg_print_buffer_decl(preemption," KERNEL: @%ju(%ju) resetting alarm to %ju.\n", currtime.tv, __kernel_get_time().tv, (alarms->head->alarm - currtime).tv);123 __cfaabi_dbg_print_buffer_decl( " KERNEL: @%ju(%ju) resetting alarm to %ju.\n", currtime.tv, __kernel_get_time().tv, (alarms->head->alarm - currtime).tv); 124 124 Duration delta = (*alarms)`first.alarm - currtime; 125 125 Duration capped = max(delta, 50`us); -
libcfa/src/concurrency/thread.cfa
r97392b69 ree06db5c 35 35 self_mon_p = &self_mon; 36 36 curr_cluster = &cl; 37 link.next = 0p; 38 link.prev = 0p; 37 next = 0p; 39 38 40 39 node.next = 0p; -
libcfa/src/stdhdr/assert.h
r97392b69 ree06db5c 33 33 #define verify(x) assert(x) 34 34 #define verifyf(x, ...) assertf(x, __VA_ARGS__) 35 #define verifyfail(...)36 35 #define __CFA_WITH_VERIFY__ 37 36 #else 38 37 #define verify(x) 39 38 #define verifyf(x, ...) 40 #define verifyfail(...)41 39 #endif 42 40 -
tests/concurrent/examples/datingService.cfa
r97392b69 ree06db5c 35 35 signal_block( Boys[ccode] ); // restart boy to set phone number 36 36 } // if 37 // sout | "Girl:" | PhoneNo | "is dating Boy at" | BoyPhoneNo | "with ccode" | ccode;37 //sout | "Girl:" | PhoneNo | "is dating Boy at" | BoyPhoneNo | "with ccode" | ccode; 38 38 return BoyPhoneNo; 39 39 } // DatingService girl … … 47 47 signal_block( Girls[ccode] ); // restart girl to set phone number 48 48 } // if 49 // sout | " Boy:" | PhoneNo | "is dating Girl" | GirlPhoneNo | "with ccode" | ccode;49 //sout | " Boy:" | PhoneNo | "is dating Girl" | GirlPhoneNo | "with ccode" | ccode; 50 50 return GirlPhoneNo; 51 51 } // DatingService boy -
tests/concurrent/waitfor/when.cfa
r97392b69 ree06db5c 57 57 58 58 void arbiter( global_t & mutex this ) { 59 // There is a race at start where callers can get in before the arbiter.60 // It doesn't really matter here so just restart the loop correctly and move on61 this.last_call = 6;62 63 59 for( int i = 0; i < N; i++ ) { 64 60 when( this.last_call == 6 ) waitfor( call1 : this ) { if( this.last_call != 1) { serr | "Expected last_call to be 1 got" | this.last_call; } }
Note:
See TracChangeset
for help on using the changeset viewer.