Changeset c8a0210 for libcfa/src
- Timestamp:
- Apr 16, 2021, 2:28:09 PM (4 years ago)
- Branches:
- ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, master, new-ast-unique-expr, pthread-emulation, qualifiedEnum
- Children:
- 665edf40
- Parents:
- 857a1c6 (diff), 5f6a172 (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. - Location:
- libcfa/src
- Files:
-
- 27 edited
Legend:
- Unmodified
- Added
- Removed
-
libcfa/src/bits/weakso_locks.cfa
r857a1c6 rc8a0210 25 25 void unlock( blocking_lock & ) {} 26 26 void on_notify( blocking_lock &, struct $thread * ) {} 27 size_t on_wait( blocking_lock & ) { }27 size_t on_wait( blocking_lock & ) { return 0; } 28 28 void on_wakeup( blocking_lock &, size_t ) {} 29 29 size_t wait_count( blocking_lock & ) { return 0; } -
libcfa/src/clock.hfa
r857a1c6 rc8a0210 10 10 // Created On : Thu Apr 12 14:36:06 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Jan 6 12:49:58 202013 // Update Count : 912 // Last Modified On : Wed Apr 14 17:48:25 2021 13 // Update Count : 20 14 14 // 15 15 … … 32 32 33 33 static inline { 34 void reset Clock( Clock & clk, Duration adj ) with( clk ) {34 void reset( Clock & clk, Duration adj ) with( clk ) { 35 35 offset = adj + __timezone`s; // timezone (global) is (UTC - local time) in seconds 36 } // reset Clock36 } // reset 37 37 38 void ?{}( Clock & clk, Duration adj ) { resetClock( clk, adj ); } 38 void ?{}( Clock & clk ) { reset( clk, (Duration){ 0 } ); } 39 void ?{}( Clock & clk, Duration adj ) { reset( clk, adj ); } 39 40 40 Duration getResNsec() { 41 // System-wide clock that measures real, i.e., wall-clock) time. This clock is affected by discontinuous jumps in 42 // the system time. For example, manual changes of the clock, and incremental adjustments performed by adjtime(3) 43 // and NTP (daylight saving (Fall back). 44 Duration resolutionNsec() { 41 45 struct timespec res; 42 46 clock_getres( CLOCK_REALTIME, &res ); 43 47 return ((int64_t)res.tv_sec * TIMEGRAN + res.tv_nsec)`ns; 44 } // getRes48 } // resolutionNsec 45 49 46 Duration getRes() {50 Duration resolution() { 47 51 struct timespec res; 48 52 clock_getres( CLOCK_REALTIME_COARSE, &res ); 49 53 return ((int64_t)res.tv_sec * TIMEGRAN + res.tv_nsec)`ns; 50 } // getRes54 } // resolution 51 55 52 Time getTimeNsec() {// with nanoseconds56 Time timeNsec() { // with nanoseconds 53 57 timespec curr; 54 58 clock_gettime( CLOCK_REALTIME, &curr ); 55 59 return (Time){ curr }; 56 } // getTimeNsec60 } // timeNsec 57 61 58 Time getTime() {// without nanoseconds62 Time time() { // without nanoseconds 59 63 timespec curr; 60 64 clock_gettime( CLOCK_REALTIME_COARSE, &curr ); 61 65 curr.tv_nsec = 0; 62 66 return (Time){ curr }; 63 } // getTime67 } // time 64 68 65 Time getTime( Clock & clk ) with( clk ) {66 return getTime() + offset;67 } // getTime69 Time time( Clock & clk ) with( clk ) { 70 return time() + offset; 71 } // time 68 72 69 73 Time ?()( Clock & clk ) with( clk ) { // alternative syntax 70 return getTime() + offset;71 } // getTime74 return time() + offset; 75 } // ?() 72 76 73 timeval getTime( Clock & clk ) {77 timeval time( Clock & clk ) { 74 78 return (timeval){ clk() }; 75 } // getTime79 } // time 76 80 77 tm getTime( Clock & clk ) with( clk ) {81 tm time( Clock & clk ) with( clk ) { 78 82 tm ret; 79 localtime_r( getTime( clk ).tv_sec, &ret );83 localtime_r( time( clk ).tv_sec, &ret ); 80 84 return ret; 81 } // getTime85 } // time 82 86 83 Time getCPUTime() { 87 // CFA processor CPU-time watch that ticks when the processor (kernel thread) is running. This watch is affected by 88 // discontinuous jumps when the OS is not running the kernal thread. A duration is returned because the value is 89 // relative and cannot be converted to real-time (wall-clock) time. 90 Duration processor() { 84 91 timespec ts; 85 92 clock_gettime( CLOCK_THREAD_CPUTIME_ID, &ts ); 86 return (Time){ ts }; 87 } // getCPUTime 93 return (Duration){ ts }; 94 } // processor 95 96 // Program CPU-time watch measures CPU time consumed by all processors (kernel threads) in the UNIX process. This 97 // watch is affected by discontinuous jumps when the OS is not running the kernel threads. A duration is returned 98 // because the value is relative and cannot be converted to real-time (wall-clock) time. 99 Duration program() { 100 timespec ts; 101 clock_gettime( CLOCK_PROCESS_CPUTIME_ID, &ts ); 102 return (Duration){ ts }; 103 } // program 104 105 // Monotonic stopwatch starting at machine boot and includes system suspension. This watch is unaffected by 106 // discontinuous jumps resulting from manual changes of the clock, and incremental adjustments performed by 107 // adjtime(3) and NTP (Fall back). A duration is returned because the value is relative and cannot be converted to 108 // real-time (wall-clock) time. 109 Duration boot() { 110 timespec ts; 111 clock_gettime( CLOCK_BOOTTIME, &ts ); 112 return (Duration){ ts }; 113 } // boot 88 114 } // distribution 89 115 -
libcfa/src/concurrency/coroutine.cfa
r857a1c6 rc8a0210 46 46 47 47 //----------------------------------------------------------------------------- 48 FORALL_DATA_INSTANCE(CoroutineCancelled, (coroutine_t &), (coroutine_t)) 49 50 forall(T &) 51 void mark_exception(CoroutineCancelled(T) *) {} 48 EHM_VIRTUAL_TABLE(SomeCoroutineCancelled, std_coroutine_cancelled); 52 49 53 50 forall(T &) … … 71 68 72 69 // TODO: Remove explitate vtable set once trac#186 is fixed. 73 CoroutineCancelled(T)except;74 except.virtual_table = & get_exception_vtable(&except);70 SomeCoroutineCancelled except; 71 except.virtual_table = &std_coroutine_cancelled; 75 72 except.the_coroutine = &cor; 76 73 except.the_exception = except; 77 throwResume except; 74 // Why does this need a cast? 75 throwResume (SomeCoroutineCancelled &)except; 78 76 79 77 except->virtual_table->free( except ); -
libcfa/src/concurrency/coroutine.hfa
r857a1c6 rc8a0210 22 22 //----------------------------------------------------------------------------- 23 23 // Exception thrown from resume when a coroutine stack is cancelled. 24 FORALL_DATA_EXCEPTION(CoroutineCancelled, (coroutine_t &), (coroutine_t)) ( 24 EHM_EXCEPTION(SomeCoroutineCancelled)( 25 void * the_coroutine; 26 exception_t * the_exception; 27 ); 28 29 EHM_EXTERN_VTABLE(SomeCoroutineCancelled, std_coroutine_cancelled); 30 31 EHM_FORALL_EXCEPTION(CoroutineCancelled, (coroutine_t &), (coroutine_t)) ( 25 32 coroutine_t * the_coroutine; 26 33 exception_t * the_exception; … … 37 44 // Anything that implements this trait can be resumed. 38 45 // Anything that is resumed is a coroutine. 39 trait is_coroutine(T & | IS_RESUMPTION_EXCEPTION( CoroutineCancelled, (T))) {46 trait is_coroutine(T & | IS_RESUMPTION_EXCEPTION(SomeCoroutineCancelled)) { 40 47 void main(T & this); 41 48 $coroutine * get_coroutine(T & this); -
libcfa/src/concurrency/invoke.h
r857a1c6 rc8a0210 148 148 struct $thread * prev; 149 149 volatile unsigned long long ts; 150 int preferred;151 150 }; 152 151 -
libcfa/src/concurrency/io/call.cfa.in
r857a1c6 rc8a0210 201 201 202 202 sqe->opcode = IORING_OP_{op}; 203 sqe->user_data = ( __u64)(uintptr_t)&future;203 sqe->user_data = (uintptr_t)&future; 204 204 sqe->flags = sflags; 205 205 sqe->ioprio = 0; … … 215 215 asm volatile("": : :"memory"); 216 216 217 verify( sqe->user_data == ( __u64)(uintptr_t)&future );217 verify( sqe->user_data == (uintptr_t)&future ); 218 218 cfa_io_submit( ctx, &idx, 1, 0 != (submit_flags & CFA_IO_LAZY) ); 219 219 #endif … … 238 238 'fd' : 'fd', 239 239 'off' : 'offset', 240 'addr': '( __u64)iov',240 'addr': '(uintptr_t)iov', 241 241 'len' : 'iovcnt', 242 242 }, define = 'CFA_HAVE_PREADV2'), … … 245 245 'fd' : 'fd', 246 246 'off' : 'offset', 247 'addr': '( __u64)iov',247 'addr': '(uintptr_t)iov', 248 248 'len' : 'iovcnt' 249 249 }, define = 'CFA_HAVE_PWRITEV2'), … … 257 257 'addr': 'fd', 258 258 'len': 'op', 259 'off': '( __u64)event'259 'off': '(uintptr_t)event' 260 260 }), 261 261 # CFA_HAVE_IORING_OP_SYNC_FILE_RANGE … … 269 269 Call('SENDMSG', 'ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)', { 270 270 'fd': 'sockfd', 271 'addr': '( __u64)(struct msghdr *)msg',271 'addr': '(uintptr_t)(struct msghdr *)msg', 272 272 'len': '1', 273 273 'msg_flags': 'flags' … … 276 276 Call('RECVMSG', 'ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags)', { 277 277 'fd': 'sockfd', 278 'addr': '( __u64)(struct msghdr *)msg',278 'addr': '(uintptr_t)(struct msghdr *)msg', 279 279 'len': '1', 280 280 'msg_flags': 'flags' … … 283 283 Call('SEND', 'ssize_t send(int sockfd, const void *buf, size_t len, int flags)', { 284 284 'fd': 'sockfd', 285 'addr': '( __u64)buf',285 'addr': '(uintptr_t)buf', 286 286 'len': 'len', 287 287 'msg_flags': 'flags' … … 290 290 Call('RECV', 'ssize_t recv(int sockfd, void *buf, size_t len, int flags)', { 291 291 'fd': 'sockfd', 292 'addr': '( __u64)buf',292 'addr': '(uintptr_t)buf', 293 293 'len': 'len', 294 294 'msg_flags': 'flags' … … 297 297 Call('ACCEPT', 'int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags)', { 298 298 'fd': 'sockfd', 299 'addr': '( __u64)addr',300 'addr2': '( __u64)addrlen',299 'addr': '(uintptr_t)addr', 300 'addr2': '(uintptr_t)addrlen', 301 301 'accept_flags': 'flags' 302 302 }), … … 304 304 Call('CONNECT', 'int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)', { 305 305 'fd': 'sockfd', 306 'addr': '( __u64)addr',306 'addr': '(uintptr_t)addr', 307 307 'off': 'addrlen' 308 308 }), … … 310 310 Call('FALLOCATE', 'int fallocate(int fd, int mode, off_t offset, off_t len)', { 311 311 'fd': 'fd', 312 'addr': '( __u64)len',312 'addr': '(uintptr_t)len', 313 313 'len': 'mode', 314 314 'off': 'offset' … … 323 323 # CFA_HAVE_IORING_OP_MADVISE 324 324 Call('MADVISE', 'int madvise(void *addr, size_t length, int advice)', { 325 'addr': '( __u64)addr',325 'addr': '(uintptr_t)addr', 326 326 'len': 'length', 327 327 'fadvise_advice': 'advice' … … 330 330 Call('OPENAT', 'int openat(int dirfd, const char *pathname, int flags, mode_t mode)', { 331 331 'fd': 'dirfd', 332 'addr': '( __u64)pathname',332 'addr': '(uintptr_t)pathname', 333 333 'len': 'mode', 334 334 'open_flags': 'flags;' … … 339 339 'addr': 'pathname', 340 340 'len': 'sizeof(*how)', 341 'off': '( __u64)how',341 'off': '(uintptr_t)how', 342 342 }, define = 'CFA_HAVE_OPENAT2'), 343 343 # CFA_HAVE_IORING_OP_CLOSE … … 348 348 Call('STATX', 'int statx(int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf)', { 349 349 'fd': 'dirfd', 350 'off': '( __u64)statxbuf',350 'off': '(uintptr_t)statxbuf', 351 351 'addr': 'pathname', 352 352 'len': 'mask', … … 356 356 Call('READ', 'ssize_t read(int fd, void * buf, size_t count)', { 357 357 'fd': 'fd', 358 'addr': '( __u64)buf',358 'addr': '(uintptr_t)buf', 359 359 'len': 'count' 360 360 }), … … 362 362 Call('WRITE', 'ssize_t write(int fd, void * buf, size_t count)', { 363 363 'fd': 'fd', 364 'addr': '( __u64)buf',364 'addr': '(uintptr_t)buf', 365 365 'len': 'count' 366 366 }), -
libcfa/src/concurrency/kernel.cfa
r857a1c6 rc8a0210 113 113 static void __wake_one(cluster * cltr); 114 114 115 static void push (__cluster_idles& idles, processor & proc);116 static void remove(__cluster_idles& idles, processor & proc);117 static [unsigned idle, unsigned total, * processor] query ( & __cluster_idlesidles );115 static void mark_idle (__cluster_proc_list & idles, processor & proc); 116 static void mark_awake(__cluster_proc_list & idles, processor & proc); 117 static [unsigned idle, unsigned total, * processor] query_idles( & __cluster_proc_list idles ); 118 118 119 119 extern void __cfa_io_start( processor * ); … … 189 189 190 190 // Push self to idle stack 191 push(this->cltr->idles, * this);191 mark_idle(this->cltr->procs, * this); 192 192 193 193 // Confirm the ready-queue is empty … … 195 195 if( readyThread ) { 196 196 // A thread was found, cancel the halt 197 remove(this->cltr->idles, * this);197 mark_awake(this->cltr->procs, * this); 198 198 199 199 #if !defined(__CFA_NO_STATISTICS__) … … 225 225 226 226 // We were woken up, remove self from idle 227 remove(this->cltr->idles, * this);227 mark_awake(this->cltr->procs, * this); 228 228 229 229 // DON'T just proceed, start looking again … … 359 359 #if !defined(__CFA_NO_STATISTICS__) 360 360 __tls_stats()->ready.threads.threads++; 361 __push_stat( __tls_stats(), __tls_stats()->ready.threads.threads, false, "Processor", this ); 361 362 #endif 362 363 // This is case 2, the racy case, someone tried to run this thread before it finished blocking … … 376 377 #if !defined(__CFA_NO_STATISTICS__) 377 378 __tls_stats()->ready.threads.threads--; 379 __push_stat( __tls_stats(), __tls_stats()->ready.threads.threads, false, "Processor", this ); 378 380 #endif 379 381 … … 455 457 if( kernelTLS().this_stats ) { 456 458 __tls_stats()->ready.threads.threads++; 459 __push_stat( __tls_stats(), __tls_stats()->ready.threads.threads, false, "Processor", kernelTLS().this_processor ); 457 460 } 458 461 else { 459 462 __atomic_fetch_add(&cl->stats->ready.threads.threads, 1, __ATOMIC_RELAXED); 463 __push_stat( cl->stats, cl->stats->ready.threads.threads, true, "Cluster", cl ); 460 464 } 461 465 #endif … … 470 474 471 475 ready_schedule_lock(); 472 $thread * thrd = pop ( this );476 $thread * thrd = pop_fast( this ); 473 477 ready_schedule_unlock(); 474 478 … … 613 617 unsigned idle; 614 618 unsigned total; 615 [idle, total, p] = query (this->idles);619 [idle, total, p] = query_idles(this->procs); 616 620 617 621 // If no one is sleeping, we are done … … 650 654 } 651 655 652 static void push (__cluster_idles& this, processor & proc) {656 static void mark_idle(__cluster_proc_list & this, processor & proc) { 653 657 /* paranoid */ verify( ! __preemption_enabled() ); 654 658 lock( this ); 655 659 this.idle++; 656 660 /* paranoid */ verify( this.idle <= this.total ); 657 658 insert_first(this. list, proc);661 remove(proc); 662 insert_first(this.idles, proc); 659 663 unlock( this ); 660 664 /* paranoid */ verify( ! __preemption_enabled() ); 661 665 } 662 666 663 static void remove(__cluster_idles& this, processor & proc) {667 static void mark_awake(__cluster_proc_list & this, processor & proc) { 664 668 /* paranoid */ verify( ! __preemption_enabled() ); 665 669 lock( this ); 666 670 this.idle--; 667 671 /* paranoid */ verify( this.idle >= 0 ); 668 669 672 remove(proc); 673 insert_last(this.actives, proc); 670 674 unlock( this ); 671 675 /* paranoid */ verify( ! __preemption_enabled() ); 672 676 } 673 677 674 static [unsigned idle, unsigned total, * processor] query( & __cluster_idles this ) { 678 static [unsigned idle, unsigned total, * processor] query_idles( & __cluster_proc_list this ) { 679 /* paranoid */ verify( ! __preemption_enabled() ); 680 /* paranoid */ verify( ready_schedule_islocked() ); 681 675 682 for() { 676 683 uint64_t l = __atomic_load_n(&this.lock, __ATOMIC_SEQ_CST); … … 678 685 unsigned idle = this.idle; 679 686 unsigned total = this.total; 680 processor * proc = &this. list`first;687 processor * proc = &this.idles`first; 681 688 // Compiler fence is unnecessary, but gcc-8 and older incorrectly reorder code without it 682 689 asm volatile("": : :"memory"); … … 684 691 return [idle, total, proc]; 685 692 } 693 694 /* paranoid */ verify( ready_schedule_islocked() ); 695 /* paranoid */ verify( ! __preemption_enabled() ); 686 696 } 687 697 -
libcfa/src/concurrency/kernel.hfa
r857a1c6 rc8a0210 69 69 struct cluster * cltr; 70 70 71 // Id within the cluster 72 unsigned cltr_id; 71 // Ready Queue state per processor 72 struct { 73 unsigned short its; 74 unsigned short itr; 75 unsigned id; 76 unsigned target; 77 unsigned long long int cutoff; 78 } rdq; 73 79 74 80 // Set to true to notify the processor should terminate … … 140 146 // Cluster Tools 141 147 142 // Intrusives lanes which are used by the re laxed ready queue148 // Intrusives lanes which are used by the ready queue 143 149 struct __attribute__((aligned(128))) __intrusive_lane_t; 144 150 void ?{}(__intrusive_lane_t & this); 145 151 void ^?{}(__intrusive_lane_t & this); 146 152 147 // Counter used for wether or not the lanes are all empty 148 struct __attribute__((aligned(128))) __snzi_node_t; 149 struct __snzi_t { 150 unsigned mask; 151 int root; 152 __snzi_node_t * nodes; 153 }; 154 155 void ?{}( __snzi_t & this, unsigned depth ); 156 void ^?{}( __snzi_t & this ); 153 // Aligned timestamps which are used by the relaxed ready queue 154 struct __attribute__((aligned(128))) __timestamp_t; 155 void ?{}(__timestamp_t & this); 156 void ^?{}(__timestamp_t & this); 157 157 158 158 //TODO adjust cache size to ARCHITECTURE 159 159 // Structure holding the relaxed ready queue 160 160 struct __ready_queue_t { 161 // Data tracking how many/which lanes are used162 // Aligned to 128 for cache locality163 __snzi_t snzi;164 165 161 // Data tracking the actual lanes 166 162 // On a seperate cacheline from the used struct since … … 171 167 __intrusive_lane_t * volatile data; 172 168 169 // Array of times 170 __timestamp_t * volatile tscs; 171 173 172 // Number of lanes (empty or not) 174 173 volatile size_t count; … … 180 179 181 180 // Idle Sleep 182 struct __cluster_ idles{181 struct __cluster_proc_list { 183 182 // Spin lock protecting the queue 184 183 volatile uint64_t lock; … … 191 190 192 191 // List of idle processors 193 dlist(processor, processor) list; 192 dlist(processor, processor) idles; 193 194 // List of active processors 195 dlist(processor, processor) actives; 194 196 }; 195 197 … … 207 209 208 210 // List of idle processors 209 __cluster_ idles idles;211 __cluster_proc_list procs; 210 212 211 213 // List of threads -
libcfa/src/concurrency/kernel/startup.cfa
r857a1c6 rc8a0210 268 268 __print_stats( st, mainProcessor->print_stats, "Processor ", mainProcessor->name, (void*)mainProcessor ); 269 269 } 270 #if defined(CFA_STATS_ARRAY) 271 __flush_stat( st, "Processor", mainProcessor ); 272 #endif 270 273 #endif 271 274 … … 348 351 __print_stats( &local_stats, proc->print_stats, "Processor ", proc->name, (void*)proc ); 349 352 } 353 #if defined(CFA_STATS_ARRAY) 354 __flush_stat( &local_stats, "Processor", proc ); 355 #endif 350 356 #endif 351 357 … … 463 469 this.name = name; 464 470 this.cltr = &_cltr; 471 this.rdq.its = 0; 472 this.rdq.itr = 0; 473 this.rdq.id = -1u; 474 this.rdq.target = -1u; 475 this.rdq.cutoff = -1ull; 465 476 do_terminate = false; 466 477 preemption_alarm = 0p; … … 483 494 #endif 484 495 485 lock( this.cltr->idles ); 486 int target = this.cltr->idles.total += 1u; 487 unlock( this.cltr->idles ); 488 489 id = doregister((__processor_id_t*)&this); 490 496 // Register and Lock the RWlock so no-one pushes/pops while we are changing the queue 497 uint_fast32_t last_size = ready_mutate_register((__processor_id_t*)&this); 498 this.cltr->procs.total += 1u; 499 insert_last(this.cltr->procs.actives, this); 500 501 // Adjust the ready queue size 502 ready_queue_grow( cltr ); 503 504 // Unlock the RWlock 505 ready_mutate_unlock( last_size ); 506 507 __cfadbg_print_safe(runtime_core, "Kernel : core %p created\n", &this); 508 } 509 510 // Not a ctor, it just preps the destruction but should not destroy members 511 static void deinit(processor & this) { 491 512 // Lock the RWlock so no-one pushes/pops while we are changing the queue 492 513 uint_fast32_t last_size = ready_mutate_lock(); 514 this.cltr->procs.total -= 1u; 515 remove(this); 493 516 494 517 // Adjust the ready queue size 495 this.cltr_id = ready_queue_grow( cltr, target ); 496 497 // Unlock the RWlock 498 ready_mutate_unlock( last_size ); 499 500 __cfadbg_print_safe(runtime_core, "Kernel : core %p created\n", &this); 501 } 502 503 // Not a ctor, it just preps the destruction but should not destroy members 504 static void deinit(processor & this) { 505 lock( this.cltr->idles ); 506 int target = this.cltr->idles.total -= 1u; 507 unlock( this.cltr->idles ); 508 509 // Lock the RWlock so no-one pushes/pops while we are changing the queue 510 uint_fast32_t last_size = ready_mutate_lock(); 511 512 // Adjust the ready queue size 513 ready_queue_shrink( this.cltr, target ); 514 515 // Unlock the RWlock 516 ready_mutate_unlock( last_size ); 517 518 // Finally we don't need the read_lock any more 519 unregister((__processor_id_t*)&this); 518 ready_queue_shrink( this.cltr ); 519 520 // Unlock the RWlock and unregister: we don't need the read_lock any more 521 ready_mutate_unregister((__processor_id_t*)&this, last_size ); 520 522 521 523 close(this.idle); … … 560 562 //----------------------------------------------------------------------------- 561 563 // Cluster 562 static void ?{}(__cluster_ idles& this) {564 static void ?{}(__cluster_proc_list & this) { 563 565 this.lock = 0; 564 566 this.idle = 0; 565 567 this.total = 0; 566 (this.list){};567 568 } 568 569 … … 590 591 591 592 // Adjust the ready queue size 592 ready_queue_grow( &this , 0);593 ready_queue_grow( &this ); 593 594 594 595 // Unlock the RWlock … … 605 606 606 607 // Adjust the ready queue size 607 ready_queue_shrink( &this , 0);608 ready_queue_shrink( &this ); 608 609 609 610 // Unlock the RWlock … … 615 616 __print_stats( this.stats, this.print_stats, "Cluster", this.name, (void*)&this ); 616 617 } 618 #if defined(CFA_STATS_ARRAY) 619 __flush_stat( this.stats, "Cluster", &this ); 620 #endif 617 621 free( this.stats ); 618 622 #endif -
libcfa/src/concurrency/kernel_private.hfa
r857a1c6 rc8a0210 83 83 // Cluster lock API 84 84 //======================================================================= 85 // Cells use by the reader writer lock86 // while not generic it only relies on a opaque pointer87 struct __attribute__((aligned(128))) __scheduler_lock_id_t {88 // Spin lock used as the underlying lock89 volatile bool lock;90 91 // Handle pointing to the proc owning this cell92 // Used for allocating cells and debugging93 __processor_id_t * volatile handle;94 95 #ifdef __CFA_WITH_VERIFY__96 // Debug, check if this is owned for reading97 bool owned;98 #endif99 };100 101 static_assert( sizeof(struct __scheduler_lock_id_t) <= __alignof(struct __scheduler_lock_id_t));102 103 85 // Lock-Free registering/unregistering of threads 104 86 // Register a processor to a given cluster and get its unique id in return 105 unsigned doregister( struct __processor_id_t * proc);87 void register_proc_id( struct __processor_id_t * ); 106 88 107 89 // Unregister a processor from a given cluster using its id, getting back the original pointer 108 void unregister( struct __processor_id_t * proc ); 109 110 //----------------------------------------------------------------------- 111 // Cluster idle lock/unlock 112 static inline void lock(__cluster_idles & this) { 113 for() { 114 uint64_t l = this.lock; 115 if( 116 (0 == (l % 2)) 117 && __atomic_compare_exchange_n(&this.lock, &l, l + 1, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) 118 ) return; 119 Pause(); 120 } 121 } 122 123 static inline void unlock(__cluster_idles & this) { 124 /* paranoid */ verify( 1 == (this.lock % 2) ); 125 __atomic_fetch_add( &this.lock, 1, __ATOMIC_SEQ_CST ); 126 } 90 void unregister_proc_id( struct __processor_id_t * proc ); 127 91 128 92 //======================================================================= … … 152 116 __atomic_store_n(ll, (bool)false, __ATOMIC_RELEASE); 153 117 } 118 119 // Cells use by the reader writer lock 120 // while not generic it only relies on a opaque pointer 121 struct __attribute__((aligned(128))) __scheduler_lock_id_t { 122 // Spin lock used as the underlying lock 123 volatile bool lock; 124 125 // Handle pointing to the proc owning this cell 126 // Used for allocating cells and debugging 127 __processor_id_t * volatile handle; 128 129 #ifdef __CFA_WITH_VERIFY__ 130 // Debug, check if this is owned for reading 131 bool owned; 132 #endif 133 }; 134 135 static_assert( sizeof(struct __scheduler_lock_id_t) <= __alignof(struct __scheduler_lock_id_t)); 154 136 155 137 //----------------------------------------------------------------------- … … 247 229 void ready_mutate_unlock( uint_fast32_t /* value returned by lock */ ); 248 230 231 //----------------------------------------------------------------------- 232 // Lock-Free registering/unregistering of threads 233 // Register a processor to a given cluster and get its unique id in return 234 // For convenience, also acquires the lock 235 static inline uint_fast32_t ready_mutate_register( struct __processor_id_t * proc ) { 236 register_proc_id( proc ); 237 return ready_mutate_lock(); 238 } 239 240 // Unregister a processor from a given cluster using its id, getting back the original pointer 241 // assumes the lock is acquired 242 static inline void ready_mutate_unregister( struct __processor_id_t * proc, uint_fast32_t last_s ) { 243 ready_mutate_unlock( last_s ); 244 unregister_proc_id( proc ); 245 } 246 247 //----------------------------------------------------------------------- 248 // Cluster idle lock/unlock 249 static inline void lock(__cluster_proc_list & this) { 250 /* paranoid */ verify( ! __preemption_enabled() ); 251 252 // Start by locking the global RWlock so that we know no-one is 253 // adding/removing processors while we mess with the idle lock 254 ready_schedule_lock(); 255 256 // Simple counting lock, acquired, acquired by incrementing the counter 257 // to an odd number 258 for() { 259 uint64_t l = this.lock; 260 if( 261 (0 == (l % 2)) 262 && __atomic_compare_exchange_n(&this.lock, &l, l + 1, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) 263 ) return; 264 Pause(); 265 } 266 267 /* paranoid */ verify( ! __preemption_enabled() ); 268 } 269 270 static inline void unlock(__cluster_proc_list & this) { 271 /* paranoid */ verify( ! __preemption_enabled() ); 272 273 /* paranoid */ verify( 1 == (this.lock % 2) ); 274 // Simple couting lock, release by incrementing to an even number 275 __atomic_fetch_add( &this.lock, 1, __ATOMIC_SEQ_CST ); 276 277 // Release the global lock, which we acquired when locking 278 ready_schedule_unlock(); 279 280 /* paranoid */ verify( ! __preemption_enabled() ); 281 } 282 249 283 //======================================================================= 250 284 // Ready-Queue API 251 285 //----------------------------------------------------------------------- 252 // pop thread from the ready queue of a cluster253 // returns 0p if empty254 __attribute__((hot)) bool query(struct cluster * cltr);255 256 //-----------------------------------------------------------------------257 286 // push thread onto a ready queue for a cluster 258 287 // returns true if the list was previously empty, false otherwise 259 __attribute__((hot)) boolpush(struct cluster * cltr, struct $thread * thrd);288 __attribute__((hot)) void push(struct cluster * cltr, struct $thread * thrd); 260 289 261 290 //----------------------------------------------------------------------- … … 263 292 // returns 0p if empty 264 293 // May return 0p spuriously 265 __attribute__((hot)) struct $thread * pop (struct cluster * cltr);294 __attribute__((hot)) struct $thread * pop_fast(struct cluster * cltr); 266 295 267 296 //----------------------------------------------------------------------- … … 272 301 273 302 //----------------------------------------------------------------------- 274 // remove thread from the ready queue of a cluster275 // returns bool if it wasn't found276 bool remove_head(struct cluster * cltr, struct $thread * thrd);277 278 //-----------------------------------------------------------------------279 303 // Increase the width of the ready queue (number of lanes) by 4 280 unsigned ready_queue_grow (struct cluster * cltr, int target);304 void ready_queue_grow (struct cluster * cltr); 281 305 282 306 //----------------------------------------------------------------------- 283 307 // Decrease the width of the ready queue (number of lanes) by 4 284 void ready_queue_shrink(struct cluster * cltr , int target);308 void ready_queue_shrink(struct cluster * cltr); 285 309 286 310 -
libcfa/src/concurrency/preemption.cfa
r857a1c6 rc8a0210 712 712 static void * alarm_loop( __attribute__((unused)) void * args ) { 713 713 __processor_id_t id; 714 id.id = doregister(&id);714 register_proc_id(&id); 715 715 __cfaabi_tls.this_proc_id = &id; 716 716 … … 773 773 EXIT: 774 774 __cfaabi_dbg_print_safe( "Kernel : Preemption thread stopping\n" ); 775 unregister(&id);775 register_proc_id(&id); 776 776 777 777 return 0p; -
libcfa/src/concurrency/ready_queue.cfa
r857a1c6 rc8a0210 17 17 // #define __CFA_DEBUG_PRINT_READY_QUEUE__ 18 18 19 // #define USE_SNZI20 19 // #define USE_MPSC 20 21 #define USE_RELAXED_FIFO 22 // #define USE_WORK_STEALING 21 23 22 24 #include "bits/defs.hfa" … … 29 31 #include <unistd.h> 30 32 31 #include "snzi.hfa"32 33 #include "ready_subqueue.hfa" 33 34 … … 40 41 #endif 41 42 42 #define BIAS 4 43 #if defined(USE_RELAXED_FIFO) 44 #define BIAS 4 45 #define READYQ_SHARD_FACTOR 4 46 #define SEQUENTIAL_SHARD 1 47 #elif defined(USE_WORK_STEALING) 48 #define READYQ_SHARD_FACTOR 2 49 #define SEQUENTIAL_SHARD 2 50 #else 51 #error no scheduling strategy selected 52 #endif 53 54 static inline [unsigned, bool] idx_from_r(unsigned r, unsigned preferred); 55 static inline struct $thread * try_pop(struct cluster * cltr, unsigned w); 56 static inline struct $thread * try_pop(struct cluster * cltr, unsigned i, unsigned j); 57 static inline struct $thread * search(struct cluster * cltr); 58 43 59 44 60 // returns the maximum number of processors the RWLock support … … 94 110 //======================================================================= 95 111 // Lock-Free registering/unregistering of threads 96 unsigned doregister( struct __processor_id_t * proc ) with(*__scheduler_lock) {112 void register_proc_id( struct __processor_id_t * proc ) with(*__scheduler_lock) { 97 113 __cfadbg_print_safe(ready_queue, "Kernel : Registering proc %p for RW-Lock\n", proc); 98 114 … … 108 124 /*paranoid*/ verify(0 == (__alignof__(data[i]) % cache_line_size)); 109 125 /*paranoid*/ verify((((uintptr_t)&data[i]) % cache_line_size) == 0); 110 returni;126 proc->id = i; 111 127 } 112 128 } … … 135 151 /*paranoid*/ verify(__alignof__(data[n]) == (2 * cache_line_size)); 136 152 /*paranoid*/ verify((((uintptr_t)&data[n]) % cache_line_size) == 0); 137 returnn;138 } 139 140 void unregister ( struct __processor_id_t * proc ) with(*__scheduler_lock) {153 proc->id = n; 154 } 155 156 void unregister_proc_id( struct __processor_id_t * proc ) with(*__scheduler_lock) { 141 157 unsigned id = proc->id; 142 158 /*paranoid*/ verify(id < ready); … … 193 209 194 210 //======================================================================= 195 // Cforall Re qdy Queue used for scheduling211 // Cforall Ready Queue used for scheduling 196 212 //======================================================================= 197 213 void ?{}(__ready_queue_t & this) with (this) { 198 214 lanes.data = 0p; 215 lanes.tscs = 0p; 199 216 lanes.count = 0; 200 217 } 201 218 202 219 void ^?{}(__ready_queue_t & this) with (this) { 203 verify( 1 == lanes.count ); 204 #ifdef USE_SNZI 205 verify( !query( snzi ) ); 206 #endif 220 verify( SEQUENTIAL_SHARD == lanes.count ); 207 221 free(lanes.data); 222 free(lanes.tscs); 208 223 } 209 224 210 225 //----------------------------------------------------------------------- 211 __attribute__((hot)) bool query(struct cluster * cltr) { 212 #ifdef USE_SNZI 213 return query(cltr->ready_queue.snzi); 214 #endif 215 return true; 216 } 217 218 static inline [unsigned, bool] idx_from_r(unsigned r, unsigned preferred) { 219 unsigned i; 220 bool local; 221 #if defined(BIAS) 226 #if defined(USE_RELAXED_FIFO) 227 //----------------------------------------------------------------------- 228 // get index from random number with or without bias towards queues 229 static inline [unsigned, bool] idx_from_r(unsigned r, unsigned preferred) { 230 unsigned i; 231 bool local; 222 232 unsigned rlow = r % BIAS; 223 233 unsigned rhigh = r / BIAS; … … 225 235 // (BIAS - 1) out of BIAS chances 226 236 // Use perferred queues 227 i = preferred + (rhigh % 4);237 i = preferred + (rhigh % READYQ_SHARD_FACTOR); 228 238 local = true; 229 239 } … … 234 244 local = false; 235 245 } 236 #else 237 i = r; 238 local = false; 239 #endif 240 return [i, local]; 241 } 242 243 //----------------------------------------------------------------------- 244 __attribute__((hot)) bool push(struct cluster * cltr, struct $thread * thrd) with (cltr->ready_queue) { 245 __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr); 246 247 const bool external = (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr); 248 249 // write timestamp 250 thrd->link.ts = rdtscl(); 251 252 bool first = false; 253 __attribute__((unused)) bool local; 254 __attribute__((unused)) int preferred; 255 #if defined(BIAS) 256 preferred = 257 //* 258 external ? -1 : kernelTLS().this_processor->cltr_id; 259 /*/ 260 thrd->link.preferred * 4; 261 //*/ 262 #endif 263 264 // Try to pick a lane and lock it 265 unsigned i; 266 do { 267 // Pick the index of a lane 268 // unsigned r = __tls_rand(); 269 unsigned r = __tls_rand_fwd(); 270 [i, local] = idx_from_r(r, preferred); 271 272 i %= __atomic_load_n( &lanes.count, __ATOMIC_RELAXED ); 273 246 return [i, local]; 247 } 248 249 __attribute__((hot)) void push(struct cluster * cltr, struct $thread * thrd) with (cltr->ready_queue) { 250 __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr); 251 252 const bool external = (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr); 253 /* paranoid */ verify(external || kernelTLS().this_processor->rdq.id < lanes.count ); 254 255 // write timestamp 256 thrd->link.ts = rdtscl(); 257 258 bool local; 259 int preferred = external ? -1 : kernelTLS().this_processor->rdq.id; 260 261 // Try to pick a lane and lock it 262 unsigned i; 263 do { 264 // Pick the index of a lane 265 unsigned r = __tls_rand_fwd(); 266 [i, local] = idx_from_r(r, preferred); 267 268 i %= __atomic_load_n( &lanes.count, __ATOMIC_RELAXED ); 269 270 #if !defined(__CFA_NO_STATISTICS__) 271 if(external) { 272 if(local) __atomic_fetch_add(&cltr->stats->ready.pick.ext.local, 1, __ATOMIC_RELAXED); 273 __atomic_fetch_add(&cltr->stats->ready.pick.ext.attempt, 1, __ATOMIC_RELAXED); 274 } 275 else { 276 if(local) __tls_stats()->ready.pick.push.local++; 277 __tls_stats()->ready.pick.push.attempt++; 278 } 279 #endif 280 281 #if defined(USE_MPSC) 282 // mpsc always succeeds 283 } while( false ); 284 #else 285 // If we can't lock it retry 286 } while( !__atomic_try_acquire( &lanes.data[i].lock ) ); 287 #endif 288 289 // Actually push it 290 push(lanes.data[i], thrd); 291 292 #if !defined(USE_MPSC) 293 // Unlock and return 294 __atomic_unlock( &lanes.data[i].lock ); 295 #endif 296 297 // Mark the current index in the tls rng instance as having an item 298 __tls_rand_advance_bck(); 299 300 __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); 301 302 // Update statistics 274 303 #if !defined(__CFA_NO_STATISTICS__) 275 304 if(external) { 276 if(local) __atomic_fetch_add(&cltr->stats->ready.pick.ext.l ocal, 1, __ATOMIC_RELAXED);277 __atomic_fetch_add(&cltr->stats->ready.pick.ext. attempt, 1, __ATOMIC_RELAXED);305 if(local) __atomic_fetch_add(&cltr->stats->ready.pick.ext.lsuccess, 1, __ATOMIC_RELAXED); 306 __atomic_fetch_add(&cltr->stats->ready.pick.ext.success, 1, __ATOMIC_RELAXED); 278 307 } 279 308 else { 280 if(local) __tls_stats()->ready.pick.push.l ocal++;281 __tls_stats()->ready.pick.push. attempt++;309 if(local) __tls_stats()->ready.pick.push.lsuccess++; 310 __tls_stats()->ready.pick.push.success++; 282 311 } 283 312 #endif 284 285 #if defined(USE_MPSC) 286 // mpsc always succeeds 287 } while( false ); 288 #else 289 // If we can't lock it retry 290 } while( !__atomic_try_acquire( &lanes.data[i].lock ) ); 291 #endif 292 293 // Actually push it 294 #ifdef USE_SNZI 295 bool lane_first = 296 #endif 297 298 push(lanes.data[i], thrd); 299 300 #ifdef USE_SNZI 301 // If this lane used to be empty we need to do more 302 if(lane_first) { 303 // Check if the entire queue used to be empty 304 first = !query(snzi); 305 306 // Update the snzi 307 arrive( snzi, i ); 308 } 309 #endif 310 311 #if !defined(USE_MPSC) 312 // Unlock and return 313 __atomic_unlock( &lanes.data[i].lock ); 314 #endif 315 316 // Mark the current index in the tls rng instance as having an item 317 __tls_rand_advance_bck(); 318 319 __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); 313 } 314 315 // Pop from the ready queue from a given cluster 316 __attribute__((hot)) $thread * pop_fast(struct cluster * cltr) with (cltr->ready_queue) { 317 /* paranoid */ verify( lanes.count > 0 ); 318 /* paranoid */ verify( kernelTLS().this_processor ); 319 /* paranoid */ verify( kernelTLS().this_processor->rdq.id < lanes.count ); 320 321 unsigned count = __atomic_load_n( &lanes.count, __ATOMIC_RELAXED ); 322 int preferred = kernelTLS().this_processor->rdq.id; 323 324 325 // As long as the list is not empty, try finding a lane that isn't empty and pop from it 326 for(25) { 327 // Pick two lists at random 328 unsigned ri = __tls_rand_bck(); 329 unsigned rj = __tls_rand_bck(); 330 331 unsigned i, j; 332 __attribute__((unused)) bool locali, localj; 333 [i, locali] = idx_from_r(ri, preferred); 334 [j, localj] = idx_from_r(rj, preferred); 335 336 #if !defined(__CFA_NO_STATISTICS__) 337 if(locali && localj) { 338 __tls_stats()->ready.pick.pop.local++; 339 } 340 #endif 341 342 i %= count; 343 j %= count; 344 345 // try popping from the 2 picked lists 346 struct $thread * thrd = try_pop(cltr, i, j); 347 if(thrd) { 348 #if !defined(__CFA_NO_STATISTICS__) 349 if( locali || localj ) __tls_stats()->ready.pick.pop.lsuccess++; 350 #endif 351 return thrd; 352 } 353 } 354 355 // All lanes where empty return 0p 356 return 0p; 357 } 358 359 __attribute__((hot)) struct $thread * pop_slow(struct cluster * cltr) { 360 return search(cltr); 361 } 362 #endif 363 #if defined(USE_WORK_STEALING) 364 __attribute__((hot)) void push(struct cluster * cltr, struct $thread * thrd) with (cltr->ready_queue) { 365 __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr); 366 367 const bool external = (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr); 368 /* paranoid */ verify(external || kernelTLS().this_processor->rdq.id < lanes.count ); 369 370 // write timestamp 371 thrd->link.ts = rdtscl(); 372 373 // Try to pick a lane and lock it 374 unsigned i; 375 do { 376 if(unlikely(external)) { 377 i = __tls_rand() % lanes.count; 378 } 379 else { 380 processor * proc = kernelTLS().this_processor; 381 unsigned r = proc->rdq.its++; 382 i = proc->rdq.id + (r % READYQ_SHARD_FACTOR); 383 } 384 385 386 #if defined(USE_MPSC) 387 // mpsc always succeeds 388 } while( false ); 389 #else 390 // If we can't lock it retry 391 } while( !__atomic_try_acquire( &lanes.data[i].lock ) ); 392 #endif 393 394 // Actually push it 395 push(lanes.data[i], thrd); 396 397 #if !defined(USE_MPSC) 398 // Unlock and return 399 __atomic_unlock( &lanes.data[i].lock ); 400 #endif 401 402 __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); 403 } 404 405 // Pop from the ready queue from a given cluster 406 __attribute__((hot)) $thread * pop_fast(struct cluster * cltr) with (cltr->ready_queue) { 407 /* paranoid */ verify( lanes.count > 0 ); 408 /* paranoid */ verify( kernelTLS().this_processor ); 409 /* paranoid */ verify( kernelTLS().this_processor->rdq.id < lanes.count ); 410 411 processor * proc = kernelTLS().this_processor; 412 413 if(proc->rdq.target == -1u) { 414 proc->rdq.target = __tls_rand() % lanes.count; 415 unsigned it1 = proc->rdq.itr; 416 unsigned it2 = proc->rdq.itr + 1; 417 unsigned idx1 = proc->rdq.id + (it1 % READYQ_SHARD_FACTOR); 418 unsigned idx2 = proc->rdq.id + (it1 % READYQ_SHARD_FACTOR); 419 unsigned long long tsc1 = ts(lanes.data[idx1]); 420 unsigned long long tsc2 = ts(lanes.data[idx2]); 421 proc->rdq.cutoff = min(tsc1, tsc2); 422 } 423 else if(lanes.tscs[proc->rdq.target].tv < proc->rdq.cutoff) { 424 $thread * t = try_pop(cltr, proc->rdq.target); 425 proc->rdq.target = -1u; 426 if(t) return t; 427 } 428 429 for(READYQ_SHARD_FACTOR) { 430 unsigned i = proc->rdq.id + (--proc->rdq.itr % READYQ_SHARD_FACTOR); 431 if($thread * t = try_pop(cltr, i)) return t; 432 } 433 return 0p; 434 } 435 436 __attribute__((hot)) struct $thread * pop_slow(struct cluster * cltr) with (cltr->ready_queue) { 437 for(25) { 438 unsigned i = __tls_rand() % lanes.count; 439 $thread * t = try_pop(cltr, i); 440 if(t) return t; 441 } 442 443 return search(cltr); 444 } 445 #endif 446 447 //======================================================================= 448 // Various Ready Queue utilities 449 //======================================================================= 450 // these function work the same or almost the same 451 // whether they are using work-stealing or relaxed fifo scheduling 452 453 //----------------------------------------------------------------------- 454 // try to pop from a lane given by index w 455 static inline struct $thread * try_pop(struct cluster * cltr, unsigned w) with (cltr->ready_queue) { 456 // Get relevant elements locally 457 __intrusive_lane_t & lane = lanes.data[w]; 458 459 // If list looks empty retry 460 if( is_empty(lane) ) return 0p; 461 462 // If we can't get the lock retry 463 if( !__atomic_try_acquire(&lane.lock) ) return 0p; 464 465 // If list is empty, unlock and retry 466 if( is_empty(lane) ) { 467 __atomic_unlock(&lane.lock); 468 return 0p; 469 } 470 471 // Actually pop the list 472 struct $thread * thrd; 473 thrd = pop(lane); 474 475 /* paranoid */ verify(thrd); 476 /* paranoid */ verify(lane.lock); 477 478 // Unlock and return 479 __atomic_unlock(&lane.lock); 320 480 321 481 // Update statistics 322 482 #if !defined(__CFA_NO_STATISTICS__) 323 if(external) { 324 if(local) __atomic_fetch_add(&cltr->stats->ready.pick.ext.lsuccess, 1, __ATOMIC_RELAXED); 325 __atomic_fetch_add(&cltr->stats->ready.pick.ext.success, 1, __ATOMIC_RELAXED); 326 } 327 else { 328 if(local) __tls_stats()->ready.pick.push.lsuccess++; 329 __tls_stats()->ready.pick.push.success++; 330 } 483 __tls_stats()->ready.pick.pop.success++; 331 484 #endif 332 485 333 // return whether or not the list was empty before this push 334 return first; 335 } 336 337 static struct $thread * try_pop(struct cluster * cltr, unsigned i, unsigned j); 338 static struct $thread * try_pop(struct cluster * cltr, unsigned i); 339 340 // Pop from the ready queue from a given cluster 341 __attribute__((hot)) $thread * pop(struct cluster * cltr) with (cltr->ready_queue) { 342 /* paranoid */ verify( lanes.count > 0 ); 343 unsigned count = __atomic_load_n( &lanes.count, __ATOMIC_RELAXED ); 344 int preferred; 345 #if defined(BIAS) 346 // Don't bother trying locally too much 347 preferred = kernelTLS().this_processor->cltr_id; 486 #if defined(USE_WORK_STEALING) 487 lanes.tscs[w].tv = thrd->link.ts; 348 488 #endif 349 489 350 351 // As long as the list is not empty, try finding a lane that isn't empty and pop from it 352 #ifdef USE_SNZI 353 while( query(snzi) ) { 354 #else 355 for(25) { 356 #endif 357 // Pick two lists at random 358 // unsigned ri = __tls_rand(); 359 // unsigned rj = __tls_rand(); 360 unsigned ri = __tls_rand_bck(); 361 unsigned rj = __tls_rand_bck(); 362 363 unsigned i, j; 364 __attribute__((unused)) bool locali, localj; 365 [i, locali] = idx_from_r(ri, preferred); 366 [j, localj] = idx_from_r(rj, preferred); 367 368 #if !defined(__CFA_NO_STATISTICS__) 369 if(locali && localj) { 370 __tls_stats()->ready.pick.pop.local++; 371 } 372 #endif 373 374 i %= count; 375 j %= count; 376 377 // try popping from the 2 picked lists 378 struct $thread * thrd = try_pop(cltr, i, j); 379 if(thrd) { 380 #if defined(BIAS) && !defined(__CFA_NO_STATISTICS__) 381 if( locali || localj ) __tls_stats()->ready.pick.pop.lsuccess++; 382 #endif 383 return thrd; 384 } 385 } 386 387 // All lanes where empty return 0p 388 return 0p; 389 } 390 391 __attribute__((hot)) struct $thread * pop_slow(struct cluster * cltr) with (cltr->ready_queue) { 490 // return the popped thread 491 return thrd; 492 } 493 494 //----------------------------------------------------------------------- 495 // try to pop from any lanes making sure you don't miss any threads push 496 // before the start of the function 497 static inline struct $thread * search(struct cluster * cltr) with (cltr->ready_queue) { 392 498 /* paranoid */ verify( lanes.count > 0 ); 393 499 unsigned count = __atomic_load_n( &lanes.count, __ATOMIC_RELAXED ); … … 405 511 } 406 512 407 408 513 //----------------------------------------------------------------------- 409 // Given 2 indexes, pick the list with the oldest push an try to pop from it 410 static inline struct $thread * try_pop(struct cluster * cltr, unsigned i, unsigned j) with (cltr->ready_queue) { 411 #if !defined(__CFA_NO_STATISTICS__) 412 __tls_stats()->ready.pick.pop.attempt++; 413 #endif 414 415 // Pick the bet list 416 int w = i; 417 if( __builtin_expect(!is_empty(lanes.data[j]), true) ) { 418 w = (ts(lanes.data[i]) < ts(lanes.data[j])) ? i : j; 419 } 420 421 return try_pop(cltr, w); 422 } 423 424 static inline struct $thread * try_pop(struct cluster * cltr, unsigned w) with (cltr->ready_queue) { 425 // Get relevant elements locally 426 __intrusive_lane_t & lane = lanes.data[w]; 427 428 // If list looks empty retry 429 if( is_empty(lane) ) return 0p; 430 431 // If we can't get the lock retry 432 if( !__atomic_try_acquire(&lane.lock) ) return 0p; 433 434 435 // If list is empty, unlock and retry 436 if( is_empty(lane) ) { 437 __atomic_unlock(&lane.lock); 438 return 0p; 439 } 440 441 // Actually pop the list 442 struct $thread * thrd; 443 thrd = pop(lane); 444 445 /* paranoid */ verify(thrd); 446 /* paranoid */ verify(lane.lock); 447 448 #ifdef USE_SNZI 449 // If this was the last element in the lane 450 if(emptied) { 451 depart( snzi, w ); 452 } 453 #endif 454 455 // Unlock and return 456 __atomic_unlock(&lane.lock); 457 458 // Update statistics 459 #if !defined(__CFA_NO_STATISTICS__) 460 __tls_stats()->ready.pick.pop.success++; 461 #endif 462 463 // Update the thread bias 464 thrd->link.preferred = w / 4; 465 466 // return the popped thread 467 return thrd; 468 } 469 //----------------------------------------------------------------------- 470 471 bool remove_head(struct cluster * cltr, struct $thread * thrd) with (cltr->ready_queue) { 472 for(i; lanes.count) { 473 __intrusive_lane_t & lane = lanes.data[i]; 474 475 bool removed = false; 476 477 __atomic_acquire(&lane.lock); 478 if(head(lane)->link.next == thrd) { 479 $thread * pthrd; 480 pthrd = pop(lane); 481 482 /* paranoid */ verify( pthrd == thrd ); 483 484 removed = true; 485 #ifdef USE_SNZI 486 if(emptied) { 487 depart( snzi, i ); 488 } 489 #endif 490 } 491 __atomic_unlock(&lane.lock); 492 493 if( removed ) return true; 494 } 495 return false; 496 } 497 498 //----------------------------------------------------------------------- 499 514 // Check that all the intrusive queues in the data structure are still consistent 500 515 static void check( __ready_queue_t & q ) with (q) { 501 516 #if defined(__CFA_WITH_VERIFY__) && !defined(USE_MPSC) … … 522 537 } 523 538 539 //----------------------------------------------------------------------- 540 // Given 2 indexes, pick the list with the oldest push an try to pop from it 541 static inline struct $thread * try_pop(struct cluster * cltr, unsigned i, unsigned j) with (cltr->ready_queue) { 542 #if !defined(__CFA_NO_STATISTICS__) 543 __tls_stats()->ready.pick.pop.attempt++; 544 #endif 545 546 // Pick the bet list 547 int w = i; 548 if( __builtin_expect(!is_empty(lanes.data[j]), true) ) { 549 w = (ts(lanes.data[i]) < ts(lanes.data[j])) ? i : j; 550 } 551 552 return try_pop(cltr, w); 553 } 554 524 555 // Call this function of the intrusive list was moved using memcpy 525 556 // fixes the list so that the pointers back to anchors aren't left dangling … … 541 572 } 542 573 574 static void assign_list(unsigned & value, dlist(processor, processor) & list, unsigned count) { 575 processor * it = &list`first; 576 for(unsigned i = 0; i < count; i++) { 577 /* paranoid */ verifyf( it, "Unexpected null iterator, at index %u of %u\n", i, count); 578 it->rdq.id = value; 579 it->rdq.target = -1u; 580 value += READYQ_SHARD_FACTOR; 581 it = &(*it)`next; 582 } 583 } 584 585 static void reassign_cltr_id(struct cluster * cltr) { 586 unsigned preferred = 0; 587 assign_list(preferred, cltr->procs.actives, cltr->procs.total - cltr->procs.idle); 588 assign_list(preferred, cltr->procs.idles , cltr->procs.idle ); 589 } 590 591 static void fix_times( struct cluster * cltr ) with( cltr->ready_queue ) { 592 #if defined(USE_WORK_STEALING) 593 lanes.tscs = alloc(lanes.count, lanes.tscs`realloc); 594 for(i; lanes.count) { 595 lanes.tscs[i].tv = ts(lanes.data[i]); 596 } 597 #endif 598 } 599 543 600 // Grow the ready queue 544 unsigned ready_queue_grow(struct cluster * cltr, int target) { 545 unsigned preferred; 601 void ready_queue_grow(struct cluster * cltr) { 546 602 size_t ncount; 603 int target = cltr->procs.total; 547 604 548 605 /* paranoid */ verify( ready_mutate_islocked() ); … … 554 611 // grow the ready queue 555 612 with( cltr->ready_queue ) { 556 #ifdef USE_SNZI557 ^(snzi){};558 #endif559 560 613 // Find new count 561 614 // Make sure we always have atleast 1 list 562 615 if(target >= 2) { 563 ncount = target * 4; 564 preferred = ncount - 4; 616 ncount = target * READYQ_SHARD_FACTOR; 565 617 } else { 566 ncount = 1; 567 preferred = 0; 618 ncount = SEQUENTIAL_SHARD; 568 619 } 569 620 … … 583 634 // Update original 584 635 lanes.count = ncount; 585 586 #ifdef USE_SNZI 587 // Re-create the snzi 588 snzi{ log2( lanes.count / 8 ) }; 589 for( idx; (size_t)lanes.count ) { 590 if( !is_empty(lanes.data[idx]) ) { 591 arrive(snzi, idx); 592 } 593 } 594 #endif 595 } 636 } 637 638 fix_times(cltr); 639 640 reassign_cltr_id(cltr); 596 641 597 642 // Make sure that everything is consistent … … 601 646 602 647 /* paranoid */ verify( ready_mutate_islocked() ); 603 return preferred;604 648 } 605 649 606 650 // Shrink the ready queue 607 void ready_queue_shrink(struct cluster * cltr , int target) {651 void ready_queue_shrink(struct cluster * cltr) { 608 652 /* paranoid */ verify( ready_mutate_islocked() ); 609 653 __cfadbg_print_safe(ready_queue, "Kernel : Shrinking ready queue\n"); … … 612 656 /* paranoid */ check( cltr->ready_queue ); 613 657 658 int target = cltr->procs.total; 659 614 660 with( cltr->ready_queue ) { 615 #ifdef USE_SNZI616 ^(snzi){};617 #endif618 619 661 // Remember old count 620 662 size_t ocount = lanes.count; … … 622 664 // Find new count 623 665 // Make sure we always have atleast 1 list 624 lanes.count = target >= 2 ? target * 4: 1;666 lanes.count = target >= 2 ? target * READYQ_SHARD_FACTOR: SEQUENTIAL_SHARD; 625 667 /* paranoid */ verify( ocount >= lanes.count ); 626 /* paranoid */ verify( lanes.count == target * 4|| target < 2 );668 /* paranoid */ verify( lanes.count == target * READYQ_SHARD_FACTOR || target < 2 ); 627 669 628 670 // for printing count the number of displaced threads … … 667 709 fix(lanes.data[idx]); 668 710 } 669 670 #ifdef USE_SNZI 671 // Re-create the snzi 672 snzi{ log2( lanes.count / 8 ) }; 673 for( idx; (size_t)lanes.count ) { 674 if( !is_empty(lanes.data[idx]) ) { 675 arrive(snzi, idx); 676 } 677 } 678 #endif 679 } 711 } 712 713 fix_times(cltr); 714 715 reassign_cltr_id(cltr); 680 716 681 717 // Make sure that everything is consistent -
libcfa/src/concurrency/ready_subqueue.hfa
r857a1c6 rc8a0210 246 246 #endif 247 247 } 248 249 // Aligned timestamps which are used by the relaxed ready queue 250 struct __attribute__((aligned(128))) __timestamp_t { 251 volatile unsigned long long tv; 252 }; 253 254 void ?{}(__timestamp_t & this) { this.tv = 0; } 255 void ^?{}(__timestamp_t & this) {} -
libcfa/src/concurrency/stats.cfa
r857a1c6 rc8a0210 5 5 #include <inttypes.h> 6 6 #include "bits/debug.hfa" 7 #include "bits/locks.hfa" 7 8 #include "stats.hfa" 8 9 … … 44 45 stats->io.calls.errors.busy = 0; 45 46 stats->io.poller.sleeps = 0; 47 #endif 48 49 #if defined(CFA_STATS_ARRAY) 50 stats->array.values = alloc(CFA_STATS_ARRAY); 51 stats->array.cnt = 0; 46 52 #endif 47 53 } … … 151 157 #endif 152 158 } 159 160 #if defined(CFA_STATS_ARRAY) 161 extern "C" { 162 #include <stdio.h> 163 #include <errno.h> 164 #include <sys/stat.h> 165 #include <fcntl.h> 166 } 167 168 void __flush_stat( struct __stats_t * this, const char * name, void * handle) { 169 int ret = mkdir(".cfadata", 0755); 170 if(ret < 0 && errno != EEXIST) abort("Failed to create directory .cfadata: %d\n", errno); 171 172 char filename[100]; 173 snprintf(filename, 100, ".cfadata/%s%p.data", name, handle); 174 175 int fd = open(filename, O_WRONLY | O_APPEND | O_CREAT, 0644); 176 if(fd < 0) abort("Failed to create file %s: %d\n", filename, errno); 177 178 for(i; this->array.cnt) { 179 char line[100]; 180 size_t n = snprintf(line, 100, "%llu, %lld\n", this->array.values[i].ts, this->array.values[i].value); 181 write(fd, line, n); 182 } 183 184 this->array.cnt = 0; 185 close(fd); 186 } 187 188 static __spinlock_t stats_lock; 189 190 void __push_stat( struct __stats_t * this, int64_t value, bool external, const char * name, void * handle ) { 191 if(external) lock(stats_lock __cfaabi_dbg_ctx2); 192 193 if( this->array.cnt >= CFA_STATS_ARRAY ) __flush_stat( this, name, handle ); 194 195 size_t idx = this->array.cnt; 196 this->array.cnt++; 197 198 if(external) unlock(stats_lock); 199 200 this->array.values[idx].ts = rdtscl(); 201 this->array.values[idx].value = value; 202 } 203 #endif 153 204 #endif -
libcfa/src/concurrency/stats.hfa
r857a1c6 rc8a0210 1 1 #pragma once 2 3 // #define CFA_STATS_ARRAY 10000 2 4 3 5 #include <stdint.h> … … 109 111 #endif 110 112 113 #if defined(CFA_STATS_ARRAY) 114 struct __stats_elem_t { 115 long long int ts; 116 int64_t value; 117 }; 118 #endif 119 111 120 struct __attribute__((aligned(128))) __stats_t { 112 121 __stats_readQ_t ready; … … 114 123 __stats_io_t io; 115 124 #endif 125 126 #if defined(CFA_STATS_ARRAY) 127 struct { 128 __stats_elem_t * values; 129 volatile size_t cnt; 130 } array; 131 #endif 132 116 133 }; 117 134 … … 119 136 void __tally_stats( struct __stats_t *, struct __stats_t * ); 120 137 void __print_stats( struct __stats_t *, int, const char *, const char *, void * ); 138 #if defined(CFA_STATS_ARRAY) 139 void __push_stat ( struct __stats_t *, int64_t value, bool external, const char * name, void * handle); 140 void __flush_stat( struct __stats_t *, const char *, void * ); 141 #else 142 static inline void __push_stat ( struct __stats_t *, int64_t, bool, const char *, void * ) {} 143 static inline void __flush_stat( struct __stats_t *, const char *, void * ) {} 144 #endif 121 145 #endif 122 146 -
libcfa/src/concurrency/thread.cfa
r857a1c6 rc8a0210 39 39 link.next = 0p; 40 40 link.prev = 0p; 41 link.preferred = -1;42 41 #if defined( __CFA_WITH_VERIFY__ ) 43 42 canary = 0x0D15EA5E0D15EA5Ep; … … 62 61 } 63 62 64 FORALL_DATA_INSTANCE(ThreadCancelled, (thread_t &), (thread_t)) 63 EHM_VIRTUAL_TABLE(SomeThreadCancelled, std_thread_cancelled); 65 64 66 65 forall(T &) … … 73 72 forall(T &) 74 73 const char * msg(ThreadCancelled(T) *) { 75 return "ThreadCancelled ";74 return "ThreadCancelled(...)"; 76 75 } 77 76 78 77 forall(T &) 79 78 static void default_thread_cancel_handler(ThreadCancelled(T) & ) { 79 // Improve this error message, can I do formatting? 80 80 abort( "Unhandled thread cancellation.\n" ); 81 81 } 82 82 83 forall(T & | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T))) 83 static void default_thread_cancel_handler(SomeThreadCancelled & ) { 84 // Improve this error message, can I do formatting? 85 abort( "Unhandled thread cancellation.\n" ); 86 } 87 88 forall(T & | is_thread(T) | IS_EXCEPTION(SomeThreadCancelled)) 84 89 void ?{}( thread_dtor_guard_t & this, 85 T & thrd, void(*cancelHandler)( ThreadCancelled(T)&)) {86 90 T & thrd, void(*cancelHandler)(SomeThreadCancelled &)) { 91 $monitor * m = get_monitor(thrd); 87 92 $thread * desc = get_thread(thrd); 88 93 89 94 // Setup the monitor guard 90 95 void (*dtor)(T& mutex this) = ^?{}; 91 bool join = cancelHandler != (void(*)( ThreadCancelled(T)&))0;96 bool join = cancelHandler != (void(*)(SomeThreadCancelled&))0; 92 97 (this.mg){&m, (void(*)())dtor, join}; 93 98 … … 103 108 } 104 109 desc->state = Cancelled; 105 void(*defaultResumptionHandler)( ThreadCancelled(T) &) =110 void(*defaultResumptionHandler)(SomeThreadCancelled &) = 106 111 join ? cancelHandler : default_thread_cancel_handler; 107 112 108 ThreadCancelled(T) except;109 113 // TODO: Remove explitate vtable set once trac#186 is fixed. 110 except.virtual_table = &get_exception_vtable(&except); 114 SomeThreadCancelled except; 115 except.virtual_table = &std_thread_cancelled; 111 116 except.the_thread = &thrd; 112 117 except.the_exception = __cfaehm_cancellation_exception( cancellation ); 113 throwResume except; 118 // Why is this cast required? 119 throwResume (SomeThreadCancelled &)except; 114 120 115 121 except.the_exception->virtual_table->free( except.the_exception ); … … 158 164 159 165 //----------------------------------------------------------------------------- 160 forall(T & | is_thread(T) | IS_RESUMPTION_EXCEPTION( ThreadCancelled, (T)))166 forall(T & | is_thread(T) | IS_RESUMPTION_EXCEPTION(SomeThreadCancelled)) 161 167 T & join( T & this ) { 162 168 thread_dtor_guard_t guard = { this, defaultResumptionHandler }; -
libcfa/src/concurrency/thread.hfa
r857a1c6 rc8a0210 32 32 }; 33 33 34 FORALL_DATA_EXCEPTION(ThreadCancelled, (thread_t &), (thread_t)) ( 34 EHM_EXCEPTION(SomeThreadCancelled) ( 35 void * the_thread; 36 exception_t * the_exception; 37 ); 38 39 EHM_EXTERN_VTABLE(SomeThreadCancelled, std_thread_cancelled); 40 41 EHM_FORALL_EXCEPTION(ThreadCancelled, (thread_t &), (thread_t)) ( 35 42 thread_t * the_thread; 36 43 exception_t * the_exception; … … 79 86 }; 80 87 81 forall( T & | is_thread(T) | IS_EXCEPTION( ThreadCancelled, (T)) )82 void ?{}( thread_dtor_guard_t & this, T & thrd, void(*)( ThreadCancelled(T)&) );88 forall( T & | is_thread(T) | IS_EXCEPTION(SomeThreadCancelled) ) 89 void ?{}( thread_dtor_guard_t & this, T & thrd, void(*)(SomeThreadCancelled &) ); 83 90 void ^?{}( thread_dtor_guard_t & this ); 84 91 … … 125 132 //---------- 126 133 // join 127 forall( T & | is_thread(T) | IS_RESUMPTION_EXCEPTION( ThreadCancelled, (T)) )134 forall( T & | is_thread(T) | IS_RESUMPTION_EXCEPTION(SomeThreadCancelled) ) 128 135 T & join( T & this ); 129 136 -
libcfa/src/exception.c
r857a1c6 rc8a0210 10 10 // Created On : Mon Jun 26 15:13:00 2017 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Tue Oct 27 16:27:00 202013 // Update Count : 3 512 // Last Modified On : Wed Feb 24 13:40:00 2021 13 // Update Count : 36 14 14 // 15 15 … … 26 26 #include "concurrency/invoke.h" 27 27 #include "stdhdr/assert.h" 28 #include "virtual.h" 28 29 29 30 #if defined( __ARM_ARCH ) … … 46 47 const _Unwind_Exception_Class __cfaehm_exception_class = 0x4c50575500414643; 47 48 48 // Base exception vtable is abstract, you should not have base exceptions. 49 struct __cfaehm_base_exception_t_vtable 50 ___cfaehm_base_exception_t_vtable_instance = { 51 .parent = NULL, 52 .size = 0, 53 .copy = NULL, 54 .free = NULL, 55 .msg = NULL 49 // Base Exception type id: 50 struct __cfa__parent_vtable __cfatid_exception_t = { 51 NULL, 56 52 }; 57 53 -
libcfa/src/exception.h
r857a1c6 rc8a0210 10 10 // Created On : Mon Jun 26 15:11:00 2017 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : T ue Oct 27 14:45:00 202013 // Update Count : 1 112 // Last Modified On : Thr Apr 8 15:20:00 2021 13 // Update Count : 12 14 14 // 15 15 … … 29 29 struct __cfaehm_base_exception_t; 30 30 typedef struct __cfaehm_base_exception_t exception_t; 31 struct __cfa__parent_vtable; 31 32 struct __cfaehm_base_exception_t_vtable { 32 const struct __cfa ehm_base_exception_t_vtable * parent;33 const struct __cfa__parent_vtable * __cfavir_typeid; 33 34 size_t size; 34 35 void (*copy)(struct __cfaehm_base_exception_t *this, … … 40 41 struct __cfaehm_base_exception_t_vtable const * virtual_table; 41 42 }; 42 extern struct __cfaehm_base_exception_t_vtable 43 ___cfaehm_base_exception_t_vtable_instance; 43 extern struct __cfa__parent_vtable __cfatid_exception_t; 44 44 45 45 … … 104 104 /* The first field must be a pointer to a virtual table. 105 105 * That virtual table must be a decendent of the base exception virtual table. 106 * The virtual table must point at the prober type-id. 107 * None of these can be enforced in an assertion. 106 108 */ 107 virtualT const & get_exception_vtable(exceptT *);108 // Always returns the virtual table for this type (associated types hack).109 109 }; 110 110 -
libcfa/src/exception.hfa
r857a1c6 rc8a0210 10 10 // Created On : Thu Apr 7 10:25:00 2020 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : T ue Aug 4 16:22:00 202013 // Update Count : 312 // Last Modified On : Thr Apr 8 15:16:00 2021 13 // Update Count : 4 14 14 // 15 15 … … 18 18 // ----------------------------------------------------------------------------------------------- 19 19 20 // TRIVIAL_EXCEPTION_DECLARATION(exception_name); 21 // Declare a trivial exception, one that adds no fields or features. 22 // This will make the exception visible and may go in a .hfa or .cfa file. 23 #define TRIVIAL_EXCEPTION_DECLARATION(...) \ 24 _EXC_DISPATCH(_TRIVIAL_EXCEPTION_DECLARATION, __VA_ARGS__) 20 // EHM_EXCEPTION(exception_name)(fields...); 21 // Create an exception (a virtual structure that inherits from exception_t) 22 // with the given name and fields. 23 #define EHM_EXCEPTION(exception_name) \ 24 _EHM_TYPE_ID_STRUCT(exception_name, ); \ 25 _EHM_TYPE_ID_VALUE(exception_name, ); \ 26 _EHM_VIRTUAL_TABLE_STRUCT(exception_name, , ); \ 27 _EHM_EXCEPTION_STRUCT(exception_name, , ) 25 28 26 // TRIVIAL_EXCEPTION_INSTANCE(exception_name);27 // Create the trival exception. This must be used exactly once and should be used in a .cfa file,28 // as it creates the unique instance of the virtual table. 29 #define TRIVIAL_EXCEPTION_INSTANCE(...) _EXC_DISPATCH(_TRIVIAL_EXCEPTION_INSTANCE, __VA_ARGS__)29 // EHM_EXTERN_VTABLE(exception_name, table_name); 30 // Forward declare a virtual table called table_name for exception_name type. 31 #define EHM_EXTERN_VTABLE(exception_name, table_name) \ 32 _EHM_EXTERN_VTABLE(exception_name, , table_name) 30 33 31 // TRIVIAL_EXCEPTION(exception_name[, parent_name]); 32 // Does both of the above, a short hand if the exception is only used in one .cfa file. 33 // For legacy reasons this is the only one that official supports having a parent other than the 34 // base exception. This feature may be removed or changed. 35 #define TRIVIAL_EXCEPTION(...) \ 36 _EXC_DISPATCH(_TRIVIAL_EXCEPTION_DECLARATION, __VA_ARGS__); \ 37 _EXC_DISPATCH(_TRIVIAL_EXCEPTION_INSTANCE, __VA_ARGS__) 34 // EHM_VIRTUAL_TABLE(exception_name, table_name); 35 // Define a virtual table called table_name for exception_name type. 36 #define EHM_VIRTUAL_TABLE(exception_name, table_name) \ 37 _EHM_DEFINE_COPY(exception_name, ) \ 38 _EHM_DEFINE_MSG(exception_name, ) \ 39 _EHM_VIRTUAL_TABLE(exception_name, , table_name) 38 40 39 // FORALL_TRIVIAL_EXCEPTION(exception_name, (assertions...), (parameters...)); 40 // Forward declare a polymorphic but otherwise trivial exception type. You must provide the entire 41 // assertion list (exactly what would go in the forall clause) and parameters list (only the 42 // parameter names from the assertion list, same order and comma seperated). This should be 43 // visible where ever use the exception. This just generates the polymorphic framework, see 44 // POLY_VTABLE_DECLARATION to allow instantiations. 45 #define FORALL_TRIVIAL_EXCEPTION(exception_name, assertions, parameters) \ 46 _FORALL_TRIVIAL_EXCEPTION(exception_name, __cfaehm_base_exception_t, assertions, parameters, ) 41 // EHM_FORALL_EXCEPTION(exception_name, (assertions), (parameters))(fields...); 42 // As EHM_EXCEPTION but for polymorphic types instead of monomorphic ones. 43 // The assertions list should include all polymorphic parameters and 44 // assertions inside a parentisized list. Parameters should include all the 45 // polymorphic parameter names inside a parentisized list (same order). 46 #define EHM_FORALL_EXCEPTION(exception_name, assertions, parameters) \ 47 _EHM_TYPE_ID_STRUCT(exception_name, forall assertions); \ 48 _EHM_VIRTUAL_TABLE_STRUCT(exception_name, forall assertions, parameters); \ 49 _EHM_EXCEPTION_STRUCT(exception_name, forall assertions, parameters) 47 50 48 // FORALL_TRIVIAL_INSTANCE(exception_name, (assertions...), (parameters...)) 49 // Create the forall trivial exception. The assertion list and parameters must match. 50 // There must be exactly one use of this in a program for each exception type. This just 51 // generates the polymorphic framework, see POLY_VTABLE_INSTANCE to allow instantiations. 52 #define FORALL_TRIVIAL_INSTANCE(exception_name, assertions, parameters) \ 53 _FORALL_CTOR0_INSTANCE(exception_name, assertions, parameters) 51 // EHM_FORALL_EXTERN_VTABLE(exception_name, (arguments), table_name); 52 // As EHM_EXTERN_VTABLE but for polymorphic types instead of monomorphic ones. 53 // Arguments should be the parentisized list of polymorphic arguments. 54 #define EHM_FORALL_EXTERN_VTABLE(exception_name, arguments, table_name) \ 55 _EHM_EXTERN_VTABLE(exception_name, arguments, table_name) 54 56 55 // DATA_EXCEPTION(exception_name)(fields...); 56 // Forward declare an exception that adds fields but no features. The added fields go in the 57 // second argument list. The virtual table instance must be provided later (see VTABLE_INSTANCE). 58 #define DATA_EXCEPTION(...) _EXC_DISPATCH(_DATA_EXCEPTION, __VA_ARGS__) 57 // EHM_FORALL_VIRTUAL_TABLE(exception_name, (arguments), table_name); 58 // As EHM_VIRTUAL_TABLE but for polymorphic types instead of monomorphic ones. 59 // Arguments should be the parentisized list of polymorphic arguments. 60 #define EHM_FORALL_VIRTUAL_TABLE(exception_name, arguments, table_name) \ 61 _EHM_TYPE_ID_VALUE(exception_name, arguments); \ 62 _EHM_DEFINE_COPY(exception_name, arguments) \ 63 _EHM_DEFINE_MSG(exception_name, arguments) \ 64 _EHM_VIRTUAL_TABLE(exception_name, arguments, table_name) 59 65 60 // FORALL_DATA_EXCEPTION(exception_name, (assertions...), (parameters...))(fields...); 61 // Define a polymorphic exception that adds fields but no additional features. The assertion list 62 // and matching parameters must match. Then you can give the list of fields. This should be 63 // visible where ever you use the exception. This just generates the polymorphic framework, see 64 // POLY_VTABLE_DECLARATION to allow instantiations. 65 #define FORALL_DATA_EXCEPTION(exception_name, assertions, parameters) \ 66 _FORALL_DATA_EXCEPTION(exception_name, __cfaehm_base_exception_t, assertions, parameters, ) 66 #define EHM_TYPE_ID(exception_name) _EHM_TYPE_ID_TYPE(exception_name) 67 67 68 // FORALL_DATA_INSTANCE(exception_name, (assertions...), (parameters...)) 69 // Create a polymorphic data exception. The assertion list and parameters must match. This should 70 // appear once in each program. This just generates the polymorphic framework, see 71 // POLY_VTABLE_INSTANCE to allow instantiations. 72 #define FORALL_DATA_INSTANCE(exception_name, assertions, parameters) \ 73 _FORALL_CTOR0_INSTANCE(exception_name, assertions, parameters) 74 75 // VTABLE_DECLARATION(exception_name)([new_features...]); 76 // Declare a virtual table type for an exception with exception_name. You may also add features 77 // (fields on the virtual table) by including them in the second list. 78 #define VTABLE_DECLARATION(...) _EXC_DISPATCH(_VTABLE_DECLARATION, __VA_ARGS__) 79 80 // VTABLE_INSTANCE(exception_name)(msg [, others...]); 81 // Create the instance of the virtual table. There must be exactly one instance of a virtual table 82 // for each exception type. This fills in most of the fields of the virtual table (uses ?=? and 83 // ^?{}) but you must provide the message function and any other fields added in the declaration. 84 #define VTABLE_INSTANCE(...) _EXC_DISPATCH(_VTABLE_INSTANCE, __VA_ARGS__) 85 86 // FORALL_VTABLE_DECLARATION(exception_name, (assertions...), (parameters...))([new_features...]); 87 // Declare a polymorphic virtual table type for an exception with exception_name, the given 88 // assertions and parameters. You may also add features (fields on the virtual table). This just 89 // generates the polymorphic framework, see POLY_VTABLE_DECLARATION to allow instantiations. 90 #define FORALL_VTABLE_DECLARATION(exception_name, assertions, parameters) \ 91 _FORALL_VTABLE_DECLARATION(exception_name, __cfaehm_base_exception_t, assertions, parameters, ) 92 93 // POLY_VTABLE_DECLARATION(exception_name, types...); 94 // Declares that an instantiation for this exception exists for the given types. This should be 95 // visible anywhere you use the instantiation of the exception is used. 96 #define POLY_VTABLE_DECLARATION(exception_name, ...) \ 97 VTABLE_TYPE(exception_name)(__VA_ARGS__) const & get_exception_vtable(exception_name(__VA_ARGS__) *); \ 98 extern VTABLE_TYPE(exception_name)(__VA_ARGS__) VTABLE_NAME(exception_name) 99 100 // POLY_VTABLE_INSTANCE(exception_name, types...)(msg [, others...]); 101 // Creates an instantiation for the given exception for the given types. This should occur only 102 // once in the entire program. You must fill in all features, message and any others given in the 103 // initial declaration. 104 #define POLY_VTABLE_INSTANCE(exception_name, ...) \ 105 _POLY_VTABLE_INSTANCE(exception_name, __cfaehm_base_exception_t, __VA_ARGS__) 106 107 // VTABLE_TYPE(exception_name) | VTABLE_NAME(exception_name) 108 // Get the name of the vtable type or the name of the vtable instance for an exception type. 109 #define VTABLE_TYPE(exception_name) struct _GLUE2(exception_name,_vtable) 110 #define VTABLE_NAME(exception_name) _GLUE3(_,exception_name,_vtable_instance) 111 112 // VTABLE_FIELD(exception_name); 113 // FORALL_VTABLE_FIELD(exception_name, (parameters-or-types)); 114 // The declaration of the virtual table field. Should be the first declaration in a virtual type. 115 #define VTABLE_FIELD(exception_name) VTABLE_TYPE(exception_name) const * virtual_table 116 #define FORALL_VTABLE_FIELD(exception_name, parameters) \ 117 VTABLE_TYPE(exception_name) parameters const * virtual_table 118 119 // VTABLE_INIT(object_reference, exception_name); 120 // Sets a virtual table field on an object to the virtual table instance for the type. 121 #define VTABLE_INIT(this, exception_name) (this).virtual_table = &VTABLE_NAME(exception_name) 122 123 // VTABLE_ASSERTION(exception_name, (parameters...)) 124 // The assertion that there is an instantiation of the vtable for the exception and types. 125 #define VTABLE_ASSERTION(exception_name, parameters) \ 126 { VTABLE_TYPE(exception_name) parameters VTABLE_NAME(exception_name); } 68 #define EHM_MATCH_ALL __cfa__parent_vtable 127 69 128 70 // IS_EXCEPTION(exception_name [, (...parameters)]) … … 135 77 #define IS_TERMINATION_EXCEPTION(...) _IS_EXCEPTION(is_termination_exception, __VA_ARGS__, , ~) 136 78 137 // All internal helper macros begin with an underscore. 138 #define _CLOSE(...) __VA_ARGS__ } 139 #define _GLUE2(left, right) left##right 140 #define _GLUE3(left, middle, right) left##middle##right 141 #define _EXC_DISPATCH(to, ...) to(__VA_ARGS__,__cfaehm_base_exception_t,) 142 #define _UNPACK(...) __VA_ARGS__ 79 // Macros starting with a leading underscore are internal. 143 80 144 #define _TRIVIAL_EXCEPTION_DECLARATION(exception_name, parent_name, ...) \ 145 _VTABLE_DECLARATION(exception_name, parent_name)(); \ 146 struct exception_name { \ 147 VTABLE_FIELD(exception_name); \ 148 }; \ 149 void ?{}(exception_name & this); \ 150 const char * _GLUE2(exception_name,_msg)(exception_name * this) 81 // Create an exception type definition. must be tailing, can be polymorphic. 82 #define _EHM_EXCEPTION_STRUCT(exception_name, forall_clause, parameters) \ 83 forall_clause struct exception_name { \ 84 _EHM_VTABLE_TYPE(exception_name) parameters const * virtual_table; \ 85 _CLOSE 151 86 152 #define _TRIVIAL_EXCEPTION_INSTANCE(exception_name, parent_name, ...) \ 153 void ?{}(exception_name & this) { \ 154 VTABLE_INIT(this, exception_name); \ 155 } \ 156 const char * _GLUE2(exception_name,_msg)(exception_name * this) { \ 157 return #exception_name; \ 158 } \ 159 _VTABLE_INSTANCE(exception_name, parent_name,)(_GLUE2(exception_name,_msg)) 87 // Create a (possibly polymorphic) virtual table forward declaration. 88 #define _EHM_EXTERN_VTABLE(exception_name, arguments, table_name) \ 89 extern const _EHM_VTABLE_TYPE(exception_name) arguments table_name 160 90 161 #define _FORALL_TRIVIAL_EXCEPTION(exception_name, parent_name, assertions, \ 162 parameters, parent_parameters) \ 163 _FORALL_VTABLE_DECLARATION(exception_name, parent_name, assertions, \ 164 parameters, parent_parameters)(); \ 165 forall assertions struct exception_name { \ 166 FORALL_VTABLE_FIELD(exception_name, parameters); \ 167 }; \ 168 _FORALL_CTOR0_DECLARATION(exception_name, assertions, parameters) 169 170 #define _FORALL_CTOR0_DECLARATION(exception_name, assertions, parameters) \ 171 forall(_UNPACK assertions | \ 172 is_exception(exception_name parameters, VTABLE_TYPE(exception_name) parameters)) \ 173 void ?{}(exception_name parameters & this) 174 175 #define _FORALL_CTOR0_INSTANCE(exception_name, assertions, parameters) \ 176 _FORALL_CTOR0_DECLARATION(exception_name, assertions, parameters) { \ 177 (this).virtual_table = &get_exception_vtable(&this); \ 91 // Create a (possibly polymorphic) virtual table definition. 92 #define _EHM_VIRTUAL_TABLE(exception_type, arguments, table_name) \ 93 const _EHM_VTABLE_TYPE(exception_type) arguments table_name @= { \ 94 .__cfavir_typeid : &_EHM_TYPE_ID_NAME(exception_type), \ 95 .size : sizeof(struct exception_type arguments), \ 96 .copy : copy, \ 97 .^?{} : ^?{}, \ 98 .msg : msg, \ 178 99 } 179 100 180 #define _DATA_EXCEPTION(exception_name, parent_name, ...) \ 181 _VTABLE_DECLARATION(exception_name, parent_name)(); \ 182 struct exception_name { \ 183 VTABLE_FIELD(exception_name); \ 184 _CLOSE 101 // Create a (possibly polymorphic) copy function from an assignment operator. 102 #define _EHM_DEFINE_FORALL_COPY(exception_name, forall_clause, parameters) \ 103 forall_clause void copy(exception_name parameters * this, \ 104 exception_name parameters * that) { \ 105 *this = *that; \ 106 } 185 107 186 #define _FORALL_DATA_EXCEPTION(exception_name, parent_name, \ 187 assertions, parameters, parent_parameters) \ 188 _FORALL_VTABLE_DECLARATION(exception_name, parent_name, \ 189 assertions, parameters, parent_parameters)(); \ 190 _FORALL_CTOR0_DECLARATION(exception_name, assertions, parameters); \ 191 forall assertions struct exception_name { \ 192 FORALL_VTABLE_FIELD(exception_name, parameters); \ 193 _CLOSE 108 #define _EHM_DEFINE_COPY(exception_name, arguments) \ 109 void copy(exception_name arguments * this, exception_name arguments * that) { \ 110 *this = *that; \ 111 } 194 112 195 #define _VTABLE_DECLARATION(exception_name, parent_name, ...) \ 196 struct exception_name; \ 197 VTABLE_TYPE(exception_name); \ 198 VTABLE_TYPE(exception_name) const & get_exception_vtable(exception_name *); \ 199 extern VTABLE_TYPE(exception_name) VTABLE_NAME(exception_name); \ 200 VTABLE_TYPE(exception_name) { \ 201 VTABLE_TYPE(parent_name) const * parent; \ 202 size_t size; \ 203 void (*copy)(exception_name * this, exception_name * other); \ 204 void (*^?{})(exception_name & this); \ 205 const char * (*msg)(exception_name * this); \ 206 _CLOSE 113 // Create a (possibly polymorphic) msg function 114 #define _EHM_DEFINE_FORALL_MSG(exception_name, forall_clause, parameters) \ 115 forall_clause const char * msg(exception_name parameters * this) { \ 116 return #exception_name #parameters; \ 117 } 207 118 208 #define _VTABLE_INSTANCE(exception_name, parent_name, ...) \ 209 VTABLE_TYPE(exception_name) const & get_exception_vtable(exception_name *) { \ 210 return VTABLE_NAME(exception_name); \ 211 } \ 212 void _GLUE2(exception_name,_copy)(exception_name * this, exception_name * other) { \ 213 *this = *other; \ 214 } \ 215 VTABLE_TYPE(exception_name) VTABLE_NAME(exception_name) @= { \ 216 &VTABLE_NAME(parent_name), sizeof(exception_name), \ 217 _GLUE2(exception_name,_copy), ^?{}, \ 218 _CLOSE 119 #define _EHM_DEFINE_MSG(exception_name, arguments) \ 120 const char * msg(exception_name arguments * this) { \ 121 return #exception_name #arguments; \ 122 } 219 123 220 #define _FORALL_VTABLE_DECLARATION(exception_name, parent_name, assertions, \ 221 parameters, parent_parameters) \ 222 forall assertions struct exception_name; \ 223 forall assertions VTABLE_TYPE(exception_name) { \ 224 VTABLE_TYPE(parent_name) parent_parameters const * parent; \ 124 // Produces the C compatable name of the virtual table type for a virtual type. 125 #define _EHM_VTABLE_TYPE(type_name) struct _GLUE2(type_name,_vtable) 126 127 // Create the vtable type for exception name. 128 #define _EHM_VIRTUAL_TABLE_STRUCT(exception_name, forall_clause, parameters) \ 129 forall_clause struct exception_name; \ 130 forall_clause _EHM_VTABLE_TYPE(exception_name) { \ 131 _EHM_TYPE_ID_TYPE(exception_name) parameters const * __cfavir_typeid; \ 225 132 size_t size; \ 226 133 void (*copy)(exception_name parameters * this, exception_name parameters * other); \ 227 134 void (*^?{})(exception_name parameters & this); \ 228 135 const char * (*msg)(exception_name parameters * this); \ 229 _CLOSE136 } 230 137 231 #define _POLY_VTABLE_INSTANCE(exception_name, parent_name, ...) \ 232 extern VTABLE_TYPE(exception_name)(__VA_ARGS__) VTABLE_NAME(exception_name); \ 233 VTABLE_TYPE(exception_name)(__VA_ARGS__) const & get_exception_vtable( \ 234 exception_name(__VA_ARGS__) *) { \ 235 return VTABLE_NAME(exception_name); \ 236 } \ 237 void _GLUE2(exception_name,_copy)( \ 238 exception_name(__VA_ARGS__) * this, exception_name(__VA_ARGS__) * other) { \ 239 *this = *other; \ 240 } \ 241 VTABLE_TYPE(exception_name)(__VA_ARGS__) VTABLE_NAME(exception_name) @= { \ 242 &VTABLE_NAME(parent_name), sizeof(exception_name(__VA_ARGS__)), \ 243 _GLUE2(exception_name,_copy), ^?{}, \ 244 _CLOSE 138 // Define the function required to satify the trait for exceptions. 139 #define _EHM_TRAIT_FUNCTION(exception_name, forall_clause, parameters) \ 140 forall_clause inline void mark_exception( \ 141 exception_name parameters const &, \ 142 _EHM_VTABLE_TYPE(exception_name) parameters const &) {} \ 143 144 #define _EHM_TRAIT_FUNCTION2(exception_name, forall_clause, parameters) \ 145 forall_clause _EHM_VTABLE_TYPE(exception_name) parameters const & \ 146 get_exception_vtable(exception_name parameters const & this) 147 148 #define __EHM_TRAIT_FUNCTION(exception_name, forall_clause, parameters) \ 149 forall_clause inline _EHM_VTABLE_TYPE(exception_name) parameters const & \ 150 get_exception_vtable(exception_name parameters const & this) { \ 151 /* This comes before the structure definition, but we know the offset. */ \ 152 /* return (_EHM_VTABLE_TYPE(exception_name) parameters const &)this; */ \ 153 assert(false); \ 154 } 155 156 // Generates a new type-id structure. This is used to mangle the name of the 157 // type-id instance so it also includes polymorphic information. Must be the 158 // direct decendent of exception_t. 159 // The second field is used to recover type information about the exception. 160 #define _EHM_TYPE_ID_STRUCT(exception_name, forall_clause) \ 161 forall_clause _EHM_TYPE_ID_TYPE(exception_name) { \ 162 __cfa__parent_vtable const * parent; \ 163 } 164 165 // Generate a new type-id value. 166 #define _EHM_TYPE_ID_VALUE(exception_name, arguments) \ 167 __attribute__(( section(".gnu.linkonce." "__cfatid_" #exception_name) )) \ 168 _EHM_TYPE_ID_TYPE(exception_name) arguments const \ 169 _EHM_TYPE_ID_NAME(exception_name) = { \ 170 &__cfatid_exception_t, \ 171 } 172 173 // _EHM_TYPE_ID_STRUCT and _EHM_TYPE_ID_VALUE are the two that would need to 174 // be updated to extend the hierarchy if we are still using macros when that 175 // is added. 176 177 // Produce the C compatable name of the type-id type for an exception type. 178 #define _EHM_TYPE_ID_TYPE(exception_name) \ 179 struct _GLUE2(__cfatid_struct_, exception_name) 180 181 // Produce the name of the instance of the type-id for an exception type. 182 #define _EHM_TYPE_ID_NAME(exception_name) _GLUE2(__cfatid_,exception_name) 245 183 246 184 #define _IS_EXCEPTION(kind, exception_name, parameters, ...) \ 247 kind(exception_name parameters, VTABLE_TYPE(exception_name) parameters) 185 kind(exception_name parameters, _EHM_VTABLE_TYPE(exception_name) parameters) 186 187 // Internal helper macros: 188 #define _CLOSE(...) __VA_ARGS__ } 189 #define _GLUE2(left, right) left##right -
libcfa/src/fstream.cfa
r857a1c6 rc8a0210 321 321 322 322 323 EHM_VIRTUAL_TABLE(Open_Failure, Open_Failure_main_table); 323 324 void ?{}( Open_Failure & this, ofstream & ostream ) { 324 VTABLE_INIT(this, Open_Failure);325 this.virtual_table = &Open_Failure_main_table; 325 326 this.ostream = &ostream; 326 327 this.tag = 1; 327 328 } 328 329 void ?{}( Open_Failure & this, ifstream & istream ) { 329 VTABLE_INIT(this, Open_Failure);330 this.virtual_table = &Open_Failure_main_table; 330 331 this.istream = &istream; 331 332 this.tag = 0; 332 333 } 333 const char * Open_Failure_msg(Open_Failure * this) {334 return "Open_Failure";335 }336 VTABLE_INSTANCE(Open_Failure)(Open_Failure_msg);337 334 void throwOpen_Failure( ofstream & ostream ) { 338 335 Open_Failure exc = { ostream }; -
libcfa/src/fstream.hfa
r857a1c6 rc8a0210 133 133 134 134 135 DATA_EXCEPTION(Open_Failure)(135 EHM_EXCEPTION(Open_Failure)( 136 136 union { 137 137 ofstream * ostream; -
libcfa/src/iostream.cfa
r857a1c6 rc8a0210 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Mar 2 14:51:30202113 // Update Count : 1 15112 // Last Modified On : Tue Apr 13 13:05:24 2021 13 // Update Count : 1324 14 14 // 15 15 … … 195 195 int len = snprintf( buf, size, format, ##__VA_ARGS__, val ); \ 196 196 fmt( os, "%s", buf ); \ 197 if ( isfinite( val ) ) { /* if number, always print decimal point */ \197 if ( isfinite( val ) ) { /* if number, print decimal point when no fraction or exponent */ \ 198 198 for ( int i = 0;; i += 1 ) { \ 199 199 if ( i == len ) { fmt( os, "." ); break; } \ 200 if ( buf[i] == '.' ) break;\200 if ( buf[i] == '.' || buf[i] == 'e' || buf[i] == 'E' ) break; /* decimal point or scientific ? */ \ 201 201 } /* for */ \ 202 202 } /* if */ \ … … 525 525 } // distribution 526 526 527 IntegralFMTImpl( signed char, "% *hh ", "% *.*hh " ) 528 IntegralFMTImpl( unsigned char, "% *hh ", "% *.*hh " ) 529 IntegralFMTImpl( signed short int, "% *h ", "% *.*h " ) 530 IntegralFMTImpl( unsigned short int, "% *h ", "% *.*h " ) 531 IntegralFMTImpl( signed int, "% * ", "% *.* " ) 532 IntegralFMTImpl( unsigned int, "% * ", "% *.* " ) 533 IntegralFMTImpl( signed long int, "% *l ", "% *.*l " ) 534 IntegralFMTImpl( unsigned long int, "% *l ", "% *.*l " ) 535 IntegralFMTImpl( signed long long int, "% *ll ", "% *.*ll " ) 536 IntegralFMTImpl( unsigned long long int, "% *ll ", "% *.*ll " ) 537 538 #if 0 539 #if defined( __SIZEOF_INT128__ ) 540 // Default prefix for non-decimal prints is 0b, 0, 0x. 541 #define IntegralFMTImpl128( T, SIGNED, CODE, IFMTNP, IFMTP ) \ 542 forall( ostype & | ostream( ostype ) ) \ 543 static void base10_128( ostype & os, _Ostream_Manip(T) f ) { \ 544 if ( f.val > UINT64_MAX ) { \ 545 unsigned long long int lsig = f.val % P10_UINT64; \ 546 f.val /= P10_UINT64; /* msig */ \ 547 base10_128( os, f ); /* recursion */ \ 548 _Ostream_Manip(unsigned long long int) fmt @= { lsig, 0, 19, 'u', { .all : 0 } }; \ 549 fmt.flags.nobsdp = true; \ 550 /* printf( "fmt1 %c %lld %d\n", fmt.base, fmt.val, fmt.all ); */ \ 551 sepOff( os ); \ 552 (ostype &)(os | fmt); \ 553 } else { \ 554 /* printf( "fmt2 %c %lld %d\n", f.base, (unsigned long long int)f.val, f.all ); */ \ 555 _Ostream_Manip(SIGNED long long int) fmt @= { (SIGNED long long int)f.val, f.wd, f.pc, f.base, { .all : f.all } }; \ 556 (ostype &)(os | fmt); \ 557 } /* if */ \ 558 } /* base10_128 */ \ 559 forall( ostype & | ostream( ostype ) ) { \ 560 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 561 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); \ 562 \ 563 if ( f.base == 'b' | f.base == 'B' | f.base == 'o' | f.base == 'x' | f.base == 'X' ) { \ 564 unsigned long long int msig = (unsigned long long int)(f.val >> 64); \ 565 unsigned long long int lsig = (unsigned long long int)(f.val); \ 566 _Ostream_Manip(SIGNED long long int) fmt @= { msig, f.wd, f.pc, f.base, { .all : f.all } }; \ 567 _Ostream_Manip(unsigned long long int) fmt2 @= { lsig, 0, 0, f.base, { .all : 0 } }; \ 568 if ( msig == 0 ) { \ 569 fmt.val = lsig; \ 570 (ostype &)(os | fmt); \ 571 } else { \ 572 fmt2.flags.pad0 = fmt2.flags.nobsdp = true; \ 573 if ( f.base == 'b' | f.base == 'B' ) { \ 574 if ( fmt.flags.pc && fmt.pc > 64 ) fmt.pc -= 64; else { fmt.flags.pc = false; fmt.pc = 0; } \ 575 if ( fmt.flags.left ) { \ 576 fmt.flags.left = false; \ 577 fmt.wd = 0; \ 578 /* printf( "L %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \ 579 fmt2.flags.left = true; \ 580 int msigd = high1( msig ); \ 581 fmt2.wd = f.wd - (fmt.pc > msigd ? fmt.pc : msigd); \ 582 if ( ! fmt.flags.nobsdp ) fmt2.wd -= 2; /* compensate for 0b base specifier */ \ 583 if ( (int)fmt2.wd < 64 ) fmt2.wd = 64; /* cast deals with negative value */ \ 584 fmt2.flags.pc = true; fmt2.pc = 64; \ 585 } else { \ 586 if ( fmt.wd > 64 ) fmt.wd -= 64; \ 587 else fmt.wd = 1; \ 588 /* printf( "R %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \ 589 fmt2.wd = 64; \ 590 } /* if */ \ 591 /* printf( "C %llo %d %d '%c' %x\n", fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 592 (ostype &)(os | fmt | "" | fmt2); \ 593 } else if ( f.base == 'o' ) { \ 594 if ( fmt.flags.pc && fmt.pc > 22 ) fmt.pc -= 22; else { fmt.flags.pc = false; fmt.pc = 0; } \ 595 fmt.val = (unsigned long long int)fmt.val >> 2; \ 596 fmt2.val = ((msig & 0x3) << 1) + ((lsig & 0x8000000000000000U) != 0); \ 597 if ( fmt.flags.left ) { \ 598 fmt.flags.left = false; \ 599 fmt.wd = 0; \ 600 /* printf( "L %llo %llo %llo %d %d '%c' %x %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all, fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 601 (ostype &)(os | fmt | "" | fmt2); \ 602 sepOff( os ); \ 603 fmt2.flags.left = true; \ 604 int msigd = ceiling_div( high1( fmt.val ), 3 ); \ 605 fmt2.wd = f.wd - (fmt.pc > msigd ? fmt.pc : msigd); \ 606 if ( ! fmt.flags.nobsdp ) fmt2.wd -= 1; /* compensate for 0 base specifier */ \ 607 if ( (int)fmt2.wd < 21 ) fmt2.wd = 21; /* cast deals with negative value */ \ 608 fmt2.flags.pc = true; fmt2.pc = 21; \ 609 } else { \ 610 if ( fmt.wd > 22 ) fmt.wd -= 22; \ 611 else fmt.wd = 1; \ 612 /* printf( "R %llo %llo %llo %d %d '%c' %x %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all, fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 613 (ostype &)(os | fmt | "" | fmt2); \ 614 sepOff( os ); \ 615 fmt2.wd = 21; \ 616 } /* if */ \ 617 fmt2.val = lsig & 0x7fffffffffffffffU; \ 618 /* printf( "\nC %llo %d %d '%c' %x\n", fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 619 (ostype &)(os | fmt2); \ 620 } else { /* f.base == 'x' | f.base == 'X' */ \ 621 if ( fmt.flags.pc && fmt.pc > 16 ) fmt.pc -= 16; else { fmt.flags.pc = false; fmt.pc = 0; } \ 622 if ( fmt.flags.left ) { \ 623 fmt.flags.left = false; \ 624 fmt.wd = 0; \ 625 /* printf( "L %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \ 626 fmt2.flags.left = true; \ 627 int msigd = high1( msig ); \ 628 fmt2.wd = f.wd - (fmt.pc > msigd ? fmt.pc : msigd); \ 629 if ( ! fmt.flags.nobsdp ) fmt2.wd -= 2; /* compensate for 0x base specifier */ \ 630 if ( (int)fmt2.wd < 16 ) fmt2.wd = 16; /* cast deals with negative value */ \ 631 fmt2.flags.pc = true; fmt2.pc = 16; \ 632 } else { \ 633 if ( fmt.wd > 16 ) fmt.wd -= 16; \ 634 else fmt.wd = 1; \ 635 /* printf( "R %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \ 636 fmt2.wd = 16; \ 637 } /* if */ \ 638 /* printf( "C %llo %d %d '%c' %x\n", fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 639 (ostype &)(os | fmt | "" | fmt2); \ 640 } /* if */ \ 641 } /* if */ \ 642 } else { \ 643 if ( CODE == 'd' ) { \ 644 if ( f.val < 0 ) { fmt( os, "-" ); sepOff( os ); f.val = -f.val; f.flags.sign = false; } \ 645 } /* if */ \ 646 base10_128( os, f ); \ 647 } /* if */ \ 648 return os; \ 649 } /* ?|? */ \ 650 void ?|?( ostype & os, _Ostream_Manip(T) f ) { (ostype &)(os | f); ends( os ); } \ 651 } // distribution 652 653 IntegralFMTImpl128( int128, signed, 'd', "% *ll ", "% *.*ll " ) 654 IntegralFMTImpl128( unsigned int128, unsigned, 'u', "% *ll ", "% *.*ll " ) 655 #endif // __SIZEOF_INT128__ 656 #endif // 0 657 658 #if 1 527 IntegralFMTImpl( signed char, " *hh ", " *.*hh " ) 528 IntegralFMTImpl( unsigned char, " *hh ", " *.*hh " ) 529 IntegralFMTImpl( signed short int, " *h ", " *.*h " ) 530 IntegralFMTImpl( unsigned short int, " *h ", " *.*h " ) 531 IntegralFMTImpl( signed int, " * ", " *.* " ) 532 IntegralFMTImpl( unsigned int, " * ", " *.* " ) 533 IntegralFMTImpl( signed long int, " *l ", " *.*l " ) 534 IntegralFMTImpl( unsigned long int, " *l ", " *.*l " ) 535 IntegralFMTImpl( signed long long int, " *ll ", " *.*ll " ) 536 IntegralFMTImpl( unsigned long long int, " *ll ", " *.*ll " ) 537 538 659 539 #if defined( __SIZEOF_INT128__ ) 660 540 // Default prefix for non-decimal prints is 0b, 0, 0x. … … 746 626 IntegralFMTImpl128( unsigned int128 ) 747 627 #endif // __SIZEOF_INT128__ 748 #endif // 0749 628 750 629 // *********************************** floating point *********************************** 751 630 752 #define PrintWithDP2( os, format, val, ... ) \ 631 static const char *suffixes[] = { 632 "y", "z", "a", "f", "p", "n", "u", "m", "", 633 "K", "M", "G", "T", "P", "E", "Z", "Y" 634 }; 635 #define SUFFIXES_START (-24) /* Smallest power for which there is a suffix defined. */ 636 #define SUFFIXES_END (SUFFIXES_START + (int)((sizeof(suffixes) / sizeof(char *) - 1) * 3)) 637 638 #define PrintWithDP2( os, format, ... ) \ 753 639 { \ 754 enum { size = 48 };\755 char buf[size]; \756 int bufbeg = 0, i, len = snprintf( buf, size, format, ##__VA_ARGS__, val );\757 if ( isfinite( val ) && (f.base != 'g' || f.pc != 0) ) { /* if number, print decimal point*/ \758 for ( i = 0; i < len && buf[i] != '.' && buf[i] != 'e' && buf[i] != 'E'; i += 1 ); /* decimal point or scientific ? */\759 if ( i == len && ! f.flags.nobsdp) { \760 if ( ! f.flags.left ) {\761 buf[i] = '.'; buf[i + 1] = '\0';\762 if ( buf[0] == ' ' ) bufbeg = 1; /* decimal point within width */\763 } else {\764 for ( i = 0; i < len && buf[i] != ' '; i += 1 ); /* trailing blank ? */\765 buf[i] = '.'; \766 if ( i == len ) buf[i + 1] = '\0';\640 if ( ! f.flags.eng ) { \ 641 len = snprintf( buf, size, format, ##__VA_ARGS__ ); \ 642 if ( isfinite( f.val ) && ( f.pc != 0 || ! f.flags.nobsdp ) ) { /* if number, print decimal point when no fraction or exponent */ \ 643 for ( i = 0; i < len && buf[i] != '.' && buf[i] != 'e' && buf[i] != 'E'; i += 1 ); /* decimal point or scientific ? */ \ 644 if ( i == len ) { \ 645 if ( ! f.flags.left ) { \ 646 buf[i] = '.'; buf[i + 1] = '\0'; \ 647 if ( buf[0] == ' ' ) bufbeg = 1; /* decimal point within width */ \ 648 } else { \ 649 for ( i = 0; i < len && buf[i] != ' '; i += 1 ); /* trailing blank ? */ \ 650 buf[i] = '.'; \ 651 if ( i == len ) buf[i + 1] = '\0'; \ 652 } /* if */ \ 767 653 } /* if */ \ 768 654 } /* if */ \ 655 } else { \ 656 int exp10, len2; \ 657 eng( f.val, f.pc, exp10 ); /* changes arguments */ \ 658 if ( ! f.flags.left && f.wd > 1 ) { \ 659 /* Exponent size (number of digits, 'e', optional minus sign) */ \ 660 f.wd -= lrint( floor( log10( abs( exp10 ) ) ) ) + 1 + 1 + (exp10 < 0 ? 1 : 0); \ 661 if ( f.wd < 1 ) f.wd = 1; \ 662 } /* if */ \ 663 len = snprintf( buf, size, format, ##__VA_ARGS__ ); \ 664 if ( f.flags.left ) { \ 665 for ( len -= 1; len > 0 && buf[len] == ' '; len -= 1 ); \ 666 len += 1; \ 667 } /* if */ \ 668 if ( ! f.flags.nobsdp || (exp10 < SUFFIXES_START) || (exp10 > SUFFIXES_END) ) { \ 669 len2 = snprintf( &buf[len], size - len, "e%d", exp10 ); \ 670 } else { \ 671 len2 = snprintf( &buf[len], size - len, "%s", suffixes[(exp10 - SUFFIXES_START) / 3] ); \ 672 } /* if */ \ 673 if ( f.flags.left && len + len2 < f.wd ) buf[len + len2] = ' '; \ 769 674 } /* if */ \ 770 675 fmt( os, "%s", &buf[bufbeg] ); \ … … 773 678 #define FloatingPointFMTImpl( T, DFMTNP, DFMTP ) \ 774 679 forall( ostype & | ostream( ostype ) ) { \ 680 static void eng( T &value, int & pc, int & exp10 ) { \ 681 exp10 = lrint( floor( log10( abs( value ) ) ) ); /* round to desired precision */ \ 682 if ( exp10 < 0 ) exp10 -= 2; \ 683 exp10 = floor( exp10, 3 ); \ 684 value *= pow( 10.0, -exp10 ); \ 685 if ( pc <= 3 ) pc = 3; \ 686 } /* eng */ \ 687 \ 775 688 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 689 enum { size = 48 }; \ 690 char buf[size]; \ 691 int bufbeg = 0, i, len; \ 692 \ 776 693 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); \ 777 char fmtstr[sizeof(DFMTP) ];/* sizeof includes '\0' */ \694 char fmtstr[sizeof(DFMTP) + 8]; /* sizeof includes '\0' */ \ 778 695 if ( ! f.flags.pc ) memcpy( &fmtstr, DFMTNP, sizeof(DFMTNP) ); \ 779 696 else memcpy( &fmtstr, DFMTP, sizeof(DFMTP) ); \ … … 789 706 fmtstr[sizeof(DFMTNP)-2] = f.base; /* sizeof includes '\0' */ \ 790 707 /* printf( "%g %d %s\n", f.val, f.wd, &fmtstr[star]); */ \ 791 PrintWithDP2( os, &fmtstr[star], f. val, f.wd) \708 PrintWithDP2( os, &fmtstr[star], f.wd, f.val ) \ 792 709 } else { /* precision */ \ 793 710 fmtstr[sizeof(DFMTP)-2] = f.base; /* sizeof includes '\0' */ \ 794 711 /* printf( "%g %d %d %s\n", f.val, f.wd, f.pc, &fmtstr[star] ); */ \ 795 PrintWithDP2( os, &fmtstr[star], f. val, f.wd, f.pc) \712 PrintWithDP2( os, &fmtstr[star], f.wd, f.pc, f.val ) \ 796 713 } /* if */ \ 797 714 return os; \ … … 801 718 } // distribution 802 719 803 FloatingPointFMTImpl( double, " % * ", "%*.* " )804 FloatingPointFMTImpl( long double, " % *L ", "%*.*L " )720 FloatingPointFMTImpl( double, " * ", " *.* " ) 721 FloatingPointFMTImpl( long double, " *L ", " *.*L " ) 805 722 806 723 // *********************************** character *********************************** -
libcfa/src/iostream.hfa
r857a1c6 rc8a0210 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Mar 2 14:05:08202113 // Update Count : 3 6912 // Last Modified On : Tue Apr 13 13:05:11 2021 13 // Update Count : 384 14 14 // 15 15 … … 158 158 struct _Ostream_Manip { 159 159 T val; // polymorphic base-type 160 unsigned int wd, pc;// width, precision160 int wd, pc; // width, precision 161 161 char base; // numeric base / floating-point style 162 162 union { 163 163 unsigned char all; 164 164 struct { 165 unsigned char eng:1; // engineering notation 165 166 unsigned char neg:1; // val is negative 166 167 unsigned char pc:1; // precision specified … … 222 223 _Ostream_Manip(T) hex( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'a', { .all : 0 } }; } \ 223 224 _Ostream_Manip(T) sci( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'e', { .all : 0 } }; } \ 224 _Ostream_Manip(T) wd( unsigned int w, T val ) { return (_Ostream_Manip(T))@{ val, w, 0, 'f', { .all : 0 } }; } \ 225 _Ostream_Manip(T) eng( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'g', { .flags.eng : true } }; } \ 226 _Ostream_Manip(T) wd( unsigned int w, T val ) { return (_Ostream_Manip(T))@{ val, w, 0, 'g', { .all : 0 } }; } \ 225 227 _Ostream_Manip(T) wd( unsigned int w, unsigned char pc, T val ) { return (_Ostream_Manip(T))@{ val, w, pc, 'f', { .flags.pc : true } }; } \ 226 228 _Ostream_Manip(T) ws( unsigned int w, unsigned char pc, T val ) { return (_Ostream_Manip(T))@{ val, w, pc, 'g', { .flags.pc : true } }; } \ 227 _Ostream_Manip(T) & wd( unsigned int w, _Ostream_Manip(T) & fmt ) { fmt.wd = w; return fmt; } \ 228 _Ostream_Manip(T) & wd( unsigned int w, unsigned char pc, _Ostream_Manip(T) & fmt ) { fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \ 229 _Ostream_Manip(T) & wd( unsigned int w, _Ostream_Manip(T) & fmt ) { if ( fmt.flags.eng ) fmt.base = 'f'; fmt.wd = w; return fmt; } \ 230 _Ostream_Manip(T) & wd( unsigned int w, unsigned char pc, _Ostream_Manip(T) & fmt ) { if ( fmt.flags.eng ) fmt.base = 'f'; fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \ 231 _Ostream_Manip(T) & ws( unsigned int w, unsigned char pc, _Ostream_Manip(T) & fmt ) { fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \ 229 232 _Ostream_Manip(T) & left( _Ostream_Manip(T) & fmt ) { fmt.flags.left = true; return fmt; } \ 230 233 _Ostream_Manip(T) upcase( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'G', { .all : 0 } }; } \ … … 235 238 _Ostream_Manip(T) nodp( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'g', { .flags.nobsdp : true } }; } \ 236 239 _Ostream_Manip(T) & nodp( _Ostream_Manip(T) & fmt ) { fmt.flags.nobsdp = true; return fmt; } \ 240 _Ostream_Manip(T) unit( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'g', { .flags.nobsdp : true } }; } \ 241 _Ostream_Manip(T) & unit( _Ostream_Manip(T) & fmt ) { fmt.flags.nobsdp = true; return fmt; } \ 237 242 } /* distribution */ \ 238 243 forall( ostype & | ostream( ostype ) ) { \ -
libcfa/src/math.hfa
r857a1c6 rc8a0210 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // math --7 // math.hfa -- 8 8 // 9 9 // Author : Peter A. Buhr 10 10 // Created On : Mon Apr 18 23:37:04 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Aug 24 08:56:20 202013 // Update Count : 1 2612 // Last Modified On : Thu Apr 15 11:47:56 2021 13 // Update Count : 132 14 14 // 15 15 … … 100 100 long double _Complex log( long double _Complex x ) { return clogl( x ); } 101 101 102 // O(1) polymorphic integer log2, using clz, which returns the number of leading 0-bits, starting at the most 103 // significant bit (single instruction on x86) 104 int log2( unsigned int n ) { return n == 0 ? -1 : sizeof(n) * __CHAR_BIT__ - 1 - __builtin_clz( n ); } 105 long int log2( unsigned long int n ) { return n == 0 ? -1 : sizeof(n) * __CHAR_BIT__ - 1 - __builtin_clzl( n ); } 106 long long int log2( unsigned long long int n ) { return n == 0 ? -1 : sizeof(n) * __CHAR_BIT__ - 1 - __builtin_clzll( n ); } 102 107 float log2( float x ) { return log2f( x ); } 103 108 // extern "C" { double log2( double ); } -
libcfa/src/time.hfa
r857a1c6 rc8a0210 10 10 // Created On : Wed Mar 14 23:18:57 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Jun 17 16:13:00 202013 // Update Count : 66 312 // Last Modified On : Wed Apr 14 09:30:30 2021 13 // Update Count : 664 14 14 // 15 15 … … 29 29 static inline { 30 30 Duration ?=?( Duration & dur, __attribute__((unused)) zero_t ) { return dur{ 0 }; } 31 32 void ?{}( Duration & dur, timeval t ) with( dur ) { tn = (int64_t)t.tv_sec * TIMEGRAN + t.tv_usec * 1000; } 33 Duration ?=?( Duration & dur, timeval t ) with( dur ) { 34 tn = (int64_t)t.tv_sec * TIMEGRAN + t.tv_usec * (TIMEGRAN / 1_000_000LL); 35 return dur; 36 } // ?=? 37 38 void ?{}( Duration & dur, timespec t ) with( dur ) { tn = (int64_t)t.tv_sec * TIMEGRAN + t.tv_nsec; } 39 Duration ?=?( Duration & dur, timespec t ) with( dur ) { 40 tn = (int64_t)t.tv_sec * TIMEGRAN + t.tv_nsec; 41 return dur; 42 } // ?=? 31 43 32 44 Duration +?( Duration rhs ) with( rhs ) { return (Duration)@{ +tn }; } -
libcfa/src/virtual.c
r857a1c6 rc8a0210 15 15 16 16 #include "virtual.h" 17 #include "assert.h" 17 18 18 19 int __cfa__is_parent( struct __cfa__parent_vtable const * parent, 19 20 struct __cfa__parent_vtable const * child ) { 21 assert( child ); 20 22 do { 21 23 if ( parent == child ) … … 28 30 void * __cfa__virtual_cast( struct __cfa__parent_vtable const * parent, 29 31 struct __cfa__parent_vtable const * const * child ) { 32 assert( child ); 30 33 return (__cfa__is_parent(parent, *child)) ? (void *)child : (void *)0; 31 34 }
Note:
See TracChangeset
for help on using the changeset viewer.