Changeset 8e4aa05
- Timestamp:
- Mar 4, 2021, 7:40:25 PM (5 years ago)
- Branches:
- ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, master, new-ast-unique-expr, pthread-emulation, qualifiedEnum
- Children:
- 77d601f
- Parents:
- 342af53 (diff), a5040fe (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. - Files:
-
- 51 added
- 4 deleted
- 209 edited
- 34 moved
Legend:
- Unmodified
- Added
- Removed
-
.gitignore
r342af53 r8e4aa05 79 79 # generated by npm 80 80 package-lock.json 81 82 # generated by benchmark 83 benchmark/Cargo.toml -
Jenkins/FullBuild
r342af53 r8e4aa05 21 21 gcc_7_x86_old: { trigger_build( 'gcc-7', 'x86', false ) }, 22 22 gcc_6_x86_old: { trigger_build( 'gcc-6', 'x86', false ) }, 23 gcc_9_x64_old: { trigger_build( 'gcc-9', 'x64', false ) }, 24 gcc_8_x64_old: { trigger_build( 'gcc-8', 'x64', false ) }, 25 gcc_7_x64_old: { trigger_build( 'gcc-7', 'x64', false ) }, 26 gcc_6_x64_old: { trigger_build( 'gcc-6', 'x64', false ) }, 27 gcc_5_x64_old: { trigger_build( 'gcc-5', 'x64', false ) }, 23 gcc_9_x64_new: { trigger_build( 'gcc-9', 'x64', true ) }, 24 gcc_8_x64_new: { trigger_build( 'gcc-8', 'x64', true ) }, 25 gcc_7_x64_new: { trigger_build( 'gcc-7', 'x64', true ) }, 26 gcc_6_x64_new: { trigger_build( 'gcc-6', 'x64', true ) }, 27 gcc_5_x64_new: { trigger_build( 'gcc-5', 'x64', true ) }, 28 clang_x64_new: { trigger_build( 'clang', 'x64', true ) }, 28 29 clang_x64_old: { trigger_build( 'clang', 'x64', false ) }, 29 clang_x64_new: { trigger_build( 'clang', 'x64', true ) },30 30 ) 31 31 } … … 66 66 67 67 def trigger_build(String cc, String arch, boolean new_ast) { 68 // Randomly delay the builds by a random amount to avoid hitting the SC server to hard 69 sleep(time: 5 * Math.random(), unit:"MINUTES") 70 71 // Run the build 72 // Don't propagate, it doesn't play nice with our email setup 68 73 def result = build job: 'Cforall/master', \ 69 74 parameters: [ \ -
benchmark/Makefile.am
r342af53 r8e4aa05 502 502 503 503 compile-io$(EXEEXT): 504 $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/io 1.cfa504 $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/io/io.cfa 505 505 506 506 compile-monitor$(EXEEXT): -
benchmark/io/http/filecache.cfa
r342af53 r8e4aa05 4 4 #include <string.h> 5 5 6 #include <fstream.hfa> 6 7 #include <stdlib.hfa> 7 8 … … 182 183 conflicts += put_file( raw[i], fd ); 183 184 } 184 printf("Filled cache from path \"%s\" with %zu files\n", path, fcount);185 sout | "Filled cache from path \"" | path | "\" with" | fcount | "files"; 185 186 if( conflicts > 0 ) { 186 printf("Found %d conflicts (seed: %u)\n", conflicts, options.file_cache.hash_seed);187 sout | "Found" | conflicts | "conflicts (seed: " | options.file_cache.hash_seed | ")"; 187 188 #if defined(REJECT_CONFLICTS) 188 189 abort("Conflicts found in the cache"); … … 191 192 192 193 if(options.file_cache.list) { 193 printf("Listing files and exiting\n");194 sout | "Listing files and exiting"; 194 195 for(i; fcount) { 195 196 int s; char u; 196 197 [s, u] = human_size(raw[i].size); 197 printf("%4d%c - %s\n", s, u, raw[i].file);198 sout | s | u | "-" | raw[i].file; 198 199 free(raw[i].file); 199 200 } … … 208 209 209 210 [int *, int] filefds(int extra) { 211 if(!options.file_cache.path) { 212 int * data = alloc(extra); 213 return [data, 0]; 214 } 215 210 216 if(!file_cache.entries) { 211 217 abort("File cache not filled!\n"); -
benchmark/io/http/main.cfa
r342af53 r8e4aa05 6 6 #include <unistd.h> 7 7 extern "C" { 8 #include <signal.h> 8 9 #include <sys/socket.h> 9 10 #include <netinet/in.h> 10 11 } 11 12 13 #include <fstream.hfa> 12 14 #include <kernel.hfa> 15 #include <iofwd.hfa> 13 16 #include <stats.hfa> 14 17 #include <time.hfa> … … 26 29 27 30 //============================================================================================= 31 // Stats Printer 32 //=============================================================================================' 33 34 thread StatsPrinter {}; 35 36 void ?{}( StatsPrinter & this, cluster & cl ) { 37 ((thread&)this){ "Stats Printer Thread", cl }; 38 } 39 40 void ^?{}( StatsPrinter & mutex this ) {} 41 42 void main(StatsPrinter & this) { 43 LOOP: for() { 44 waitfor( ^?{} : this) { 45 break LOOP; 46 } 47 or else {} 48 49 sleep(10`s); 50 51 print_stats_now( *active_cluster(), CFA_STATS_READY_Q | CFA_STATS_IO ); 52 } 53 } 54 55 //============================================================================================= 28 56 // Globals 29 57 //============================================================================================= 30 struct ServerProc { 31 processor self; 58 struct ServerCluster { 59 cluster self; 60 processor * procs; 61 // io_context * ctxs; 62 StatsPrinter * prnt; 63 32 64 }; 33 65 34 void ?{}( ServerProc & this ) { 35 /* paranoid */ assert( options.clopts.instance != 0p ); 36 (this.self){ "Benchmark Processor", *options.clopts.instance }; 66 void ?{}( ServerCluster & this ) { 67 (this.self){ "Server Cluster", options.clopts.params }; 68 69 this.procs = alloc(options.clopts.nprocs); 70 for(i; options.clopts.nprocs) { 71 (this.procs[i]){ "Benchmark Processor", this.self }; 72 73 #if !defined(__CFA_NO_STATISTICS__) 74 if( options.clopts.procstats ) { 75 print_stats_at_exit( *this.procs, this.self.print_stats ); 76 } 77 if( options.clopts.viewhalts ) { 78 print_halts( *this.procs ); 79 } 80 #endif 81 } 82 83 if(options.stats) { 84 this.prnt = alloc(); 85 (*this.prnt){ this.self }; 86 } else { 87 this.prnt = 0p; 88 } 37 89 38 90 #if !defined(__CFA_NO_STATISTICS__) 39 if( options.clopts.procstats ) { 40 print_stats_at_exit( this.self, options.clopts.instance->print_stats ); 41 } 42 if( options.clopts.viewhalts ) { 43 print_halts( this.self ); 44 } 91 print_stats_at_exit( this.self, CFA_STATS_READY_Q | CFA_STATS_IO ); 45 92 #endif 93 94 options.clopts.instance[options.clopts.cltr_cnt] = &this.self; 95 options.clopts.cltr_cnt++; 96 } 97 98 void ^?{}( ServerCluster & this ) { 99 delete(this.prnt); 100 101 for(i; options.clopts.nprocs) { 102 ^(this.procs[i]){}; 103 } 104 free(this.procs); 105 106 ^(this.self){}; 46 107 } 47 108 … … 53 114 //=============================================================================================' 54 115 int main( int argc, char * argv[] ) { 116 __sighandler_t s = 1p; 117 signal(SIGPIPE, s); 118 55 119 //=================== 56 120 // Parse args 57 const char * path =parse_options(argc, argv);121 parse_options(argc, argv); 58 122 59 123 //=================== 60 124 // Open Files 61 printf("Filling cache from %s\n", path); 62 fill_cache( path ); 125 if( options.file_cache.path ) { 126 sout | "Filling cache from" | options.file_cache.path; 127 fill_cache( options.file_cache.path ); 128 } 63 129 64 130 //=================== 65 131 // Open Socket 66 printf("%ld : Listening on port %d\n", getpid(), options.socket.port);132 sout | getpid() | ": Listening on port" | options.socket.port; 67 133 int server_fd = socket(AF_INET, SOCK_STREAM, 0); 68 134 if(server_fd < 0) { … … 84 150 if(errno == EADDRINUSE) { 85 151 if(waited == 0) { 86 printf("Waiting for port\n");152 sout | "Waiting for port"; 87 153 } else { 88 printf("\r%d", waited);89 f flush(stdout);154 sout | "\r" | waited | nonl; 155 flush( sout ); 90 156 } 91 157 waited ++; … … 106 172 // Run Server Cluster 107 173 { 108 cluster cl = { "Server Cluster", options.clopts.params };109 #if !defined(__CFA_NO_STATISTICS__)110 print_stats_at_exit( cl, CFA_STATS_READY_Q | CFA_STATS_IO );111 #endif112 options.clopts.instance = &cl;113 114 115 174 int pipe_cnt = options.clopts.nworkers * 2; 116 175 int pipe_off; … … 122 181 } 123 182 124 if(options.file_cache.fixed_fds) {125 register_fixed_files(cl, fds, pipe_off);126 }183 // if(options.file_cache.path && options.file_cache.fixed_fds) { 184 // register_fixed_files(cl, fds, pipe_off); 185 // } 127 186 128 187 { 129 Server Proc procs[options.clopts.nprocs];188 ServerCluster cl[options.clopts.nclusters]; 130 189 131 190 init_protocol(); … … 148 207 unpark( workers[i] ); 149 208 } 150 printf("%d workers started on %d processors\n", options.clopts.nworkers, options.clopts.nprocs); 209 sout | options.clopts.nworkers | "workers started on" | options.clopts.nprocs | "processors /" | options.clopts.nclusters | "clusters"; 210 for(i; options.clopts.nclusters) { 211 sout | options.clopts.thrd_cnt[i] | nonl; 212 } 213 sout | nl; 151 214 { 152 215 char buffer[128]; 153 while(!feof(stdin)) { 154 fgets(buffer, 128, stdin); 216 for() { 217 int ret = cfa_read(0, buffer, 128, 0); 218 if(ret == 0) break; 219 if(ret < 0) abort( "main read error: (%d) %s\n", (int)errno, strerror(errno) ); 220 sout | "User wrote '" | "" | nonl; 221 write(sout, buffer, ret - 1); 222 sout | "'"; 155 223 } 156 224 157 printf("Shutting Down\n"); 158 } 159 225 sout | "Shutdown received"; 226 } 227 228 sout | "Notifying connections..." | nonl; flush( sout ); 160 229 for(i; options.clopts.nworkers) { 161 printf("Cancelling %p\n", (void*)workers[i].cancel.target);162 230 workers[i].done = true; 163 cancel(workers[i].cancel);164 }165 166 printf("Shutting down socket\n");231 } 232 sout | "done"; 233 234 sout | "Shutting down socket..." | nonl; flush( sout ); 167 235 int ret = shutdown( server_fd, SHUT_RD ); 168 if( ret < 0 ) { abort( "shutdown error: (%d) %s\n", (int)errno, strerror(errno) ); } 236 if( ret < 0 ) { 237 abort( "shutdown error: (%d) %s\n", (int)errno, strerror(errno) ); 238 } 239 sout | "done"; 169 240 170 241 //=================== 171 242 // Close Socket 172 printf("Closing Socket\n");243 sout | "Closing Socket..." | nonl; flush( sout ); 173 244 ret = close( server_fd ); 174 245 if(ret < 0) { 175 246 abort( "close socket error: (%d) %s\n", (int)errno, strerror(errno) ); 176 247 } 177 } 178 printf("Workers Closed\n"); 179 248 sout | "done"; 249 250 sout | "Stopping connection threads..." | nonl; flush( sout ); 251 } 252 sout | "done"; 253 254 sout | "Stopping protocol threads..." | nonl; flush( sout ); 180 255 deinit_protocol(); 181 } 182 256 sout | "done"; 257 258 sout | "Stopping processors/clusters..." | nonl; flush( sout ); 259 } 260 sout | "done"; 261 262 sout | "Closing splice fds..." | nonl; flush( sout ); 183 263 for(i; pipe_cnt) { 184 264 ret = close( fds[pipe_off + i] ); … … 188 268 } 189 269 free(fds); 190 191 } 270 sout | "done"; 271 272 sout | "Stopping processors..." | nonl; flush( sout ); 273 } 274 sout | "done"; 192 275 193 276 //=================== 194 277 // Close Files 195 printf("Closing Files\n"); 196 close_cache(); 197 } 278 if( options.file_cache.path ) { 279 sout | "Closing open files..." | nonl; flush( sout ); 280 close_cache(); 281 sout | "done"; 282 } 283 } -
benchmark/io/http/options.cfa
r342af53 r8e4aa05 9 9 } 10 10 11 #include <bitmanip.hfa> 12 #include <fstream.hfa> 11 13 #include <kernel.hfa> 12 14 #include <parseargs.hfa> 15 #include <stdlib.hfa> 13 16 17 #include <stdlib.h> 14 18 #include <string.h> 15 19 16 20 Options options @= { 17 21 false, // log 22 false, // stats 18 23 19 24 { // file_cache 25 0, // path 20 26 0, // open_flags; 21 27 42u, // hash_seed; … … 32 38 33 39 { // cluster 40 1, // nclusters; 34 41 1, // nprocs; 35 42 1, // nworkers; 36 0, // flags;43 {}, // params; 37 44 false, // procstats 38 45 false, // viewhalts … … 41 48 }; 42 49 43 const char *parse_options( int argc, char * argv[] ) {44 bool subthrd = false;45 bool eagrsub= false;46 bool fixedfd= false;47 bool sqkpoll = false;48 bool i okpoll= false;49 unsigned sublen = 16; 50 void parse_options( int argc, char * argv[] ) { 51 // bool fixedfd = false; 52 // bool sqkpoll = false; 53 // bool iokpoll = false; 54 unsigned nentries = 16; 55 bool isolate = false; 56 50 57 51 58 static cfa_option opt[] = { 52 { 'p', "port", "Port the server will listen on", options.socket.port},53 { 'c', "cpus", "Number of processors to use", options.clopts.nprocs},54 { 'L', "log", "Enable logs", options.log, parse_settrue},55 {' t', "threads", "Number of worker threads to use", options.clopts.nworkers},56 {' b', "accept-backlog", "Maximum number of pending accepts", options.socket.backlog},57 {' r', "request_len", "Maximum number of bytes in the http request, requests with more data will be answered with Http Code 414", options.socket.buflen},58 {' S', "seed", "seed to use for hashing", options.file_cache.hash_seed},59 {' C', "cache-size", "Size of the cache to use, if set to small, will uses closes power of 2", options.file_cache.size},60 {' l', "list-files", "List the files in the specified path and exit", options.file_cache.list, parse_settrue},61 {' s', "submitthread", "If set, cluster uses polling thread to submit I/O", subthrd, parse_settrue },62 {' e', "eagersubmit", "If set, cluster submits I/O eagerly but still aggregates submits", eagrsub, parse_settrue},63 {'f', "fixed-fds", "If set, files are open eagerly and pre-registered with the cluster", fixedfd, parse_settrue},64 {'k', "kpollsubmit", "If set, cluster uses IORING_SETUP_SQPOLL, implies -f", sqkpoll, parse_settrue },65 {'i', "kpollcomplete", "If set, cluster uses IORING_SETUP_IOPOLL", iokpoll, parse_settrue },66 {' L', "submitlength", "Max number of submitions that can be submitted together", sublen},59 { 'p', "port", "Port the server will listen on", options.socket.port}, 60 { 'c', "cpus", "Number of processors to use", options.clopts.nprocs}, 61 { 't', "threads", "Number of worker threads to use", options.clopts.nworkers}, 62 {'\0', "isolate", "Create one cluster per processor", isolate, parse_settrue}, 63 {'\0', "log", "Enable logs", options.log, parse_settrue}, 64 {'\0', "stats", "Enable statistics", options.stats, parse_settrue}, 65 {'\0', "accept-backlog", "Maximum number of pending accepts", options.socket.backlog}, 66 {'\0', "request_len", "Maximum number of bytes in the http request, requests with more data will be answered with Http Code 414", options.socket.buflen}, 67 {'\0', "seed", "seed to use for hashing", options.file_cache.hash_seed }, 68 {'\0', "cache-size", "Size of the cache to use, if set to small, will uses closes power of 2", options.file_cache.size }, 69 {'\0', "list-files", "List the files in the specified path and exit", options.file_cache.list, parse_settrue }, 70 // { 'f', "fixed-fds", "If set, files are open eagerly and pre-registered with the cluster", fixedfd, parse_settrue}, 71 // { 'k', "kpollsubmit", "If set, cluster uses IORING_SETUP_SQPOLL, implies -f", sqkpoll, parse_settrue }, 72 // { 'i', "kpollcomplete", "If set, cluster uses IORING_SETUP_IOPOLL", iokpoll, parse_settrue }, 73 {'e', "numentries", "Number of I/O entries", nentries }, 67 74 68 75 }; … … 72 79 parse_args( argc, argv, opt, opt_cnt, "[OPTIONS]... [PATH]\ncforall http server", left ); 73 80 74 options.clopts.params.poller_submits = subthrd; 75 options.clopts.params.eager_submits = eagrsub; 76 77 if( fixedfd ) { 78 options.file_cache.fixed_fds = true; 81 if( !is_pow2(nentries) ) { 82 unsigned v = nentries; 83 v--; 84 v |= v >> 1; 85 v |= v >> 2; 86 v |= v >> 4; 87 v |= v >> 8; 88 v |= v >> 16; 89 v++; 90 serr | "Warning: num_entries not a power of 2" | '(' | nentries | ')' | "raising to " | v; 91 nentries = v; 92 } 93 if(isolate) { 94 options.clopts.nclusters = options.clopts.nprocs; 95 options.clopts.nprocs = 1; 96 } 97 options.clopts.params.num_entries = nentries; 98 options.clopts.instance = alloc(options.clopts.nclusters); 99 options.clopts.thrd_cnt = alloc(options.clopts.nclusters); 100 options.clopts.cltr_cnt = 0; 101 for(i; options.clopts.nclusters) { 102 options.clopts.thrd_cnt[i] = 0; 79 103 } 80 104 81 if( sqkpoll ) {82 options.clopts.params.poll_submit = true;83 options.file_cache.fixed_fds = true;84 }85 105 86 if( iokpoll ) { 87 options.clopts.params.poll_complete = true; 88 options.file_cache.open_flags |= O_DIRECT; 89 } 106 // if( fixedfd ) { 107 // options.file_cache.fixed_fds = true; 108 // } 90 109 91 options.clopts.params.num_ready = sublen; 110 // if( sqkpoll ) { 111 // options.file_cache.fixed_fds = true; 112 // } 92 113 93 if( left[0] == 0p ) { return "."; } 114 // if( iokpoll ) { 115 // options.file_cache.open_flags |= O_DIRECT; 116 // } 117 118 if( left[0] == 0p ) { return; } 94 119 95 120 const char * path = left[0]; … … 97 122 98 123 if( left[0] != 0p ) { 99 abort("Too many trailing arguments!\n"); 124 serr | "Too many trailing arguments!" | '\'' | path | '\''; 125 while(left[0] != 0p) { 126 serr | " - " | left[0]; 127 left++; 128 } 129 exit(EXIT_FAILURE); 100 130 } 101 131 102 returnpath;132 options.file_cache.path = path; 103 133 } -
benchmark/io/http/options.hfa
r342af53 r8e4aa05 9 9 struct Options { 10 10 bool log; 11 bool stats; 11 12 12 13 struct { 14 const char * path; 13 15 int open_flags; 14 16 uint32_t hash_seed; … … 25 27 26 28 struct { 29 int nclusters; 27 30 int nprocs; 28 31 int nworkers; … … 30 33 bool procstats; 31 34 bool viewhalts; 32 cluster * instance; 35 cluster ** instance; 36 size_t * thrd_cnt; 37 size_t cltr_cnt; 33 38 } clopts; 34 39 }; … … 36 41 extern Options options; 37 42 38 const char *parse_options( int argc, char * argv[] );43 void parse_options( int argc, char * argv[] ); -
benchmark/io/http/protocol.cfa
r342af53 r8e4aa05 5 5 #include <fcntl.h> 6 6 } 7 8 #include <fstream.hfa> 7 9 #include <iofwd.hfa> 8 10 … … 11 13 extern "C" { 12 14 int snprintf ( char * s, size_t n, const char * format, ... ); 13 #include <linux/io_uring.h>15 // #include <linux/io_uring.h> 14 16 } 15 17 #include <string.h> … … 18 20 #include "options.hfa" 19 21 20 const char * volatile date = 0p; 21 22 const char * http_msgs[] = { 23 "HTTP/1.1 200 OK\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: %zu \n\n", 24 "HTTP/1.1 400 Bad Request\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",25 "HTTP/1.1 404 Not Found\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",26 "HTTP/1.1 413 Payload Too Large\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 27 "HTTP/1.1 414 URI Too Long\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 28 };22 #define PLAINTEXT_1WRITE 23 #define PLAINTEXT_NOCOPY 24 25 struct https_msg_str { 26 char msg[512]; 27 size_t len; 28 }; 29 30 const https_msg_str * volatile http_msgs[KNOWN_CODES] = { 0 }; 29 31 30 32 _Static_assert( KNOWN_CODES == (sizeof(http_msgs ) / sizeof(http_msgs [0]))); 31 33 32 const int http_codes[] = { 34 const int http_codes[KNOWN_CODES] = { 35 200, 33 36 200, 34 37 400, 35 38 404, 39 405, 40 408, 36 41 413, 37 42 414, … … 47 52 while(len > 0) { 48 53 // Call write 49 int ret = cfa_write(fd, it, len, 0, -1`s, 0p, 0p); 50 // int ret = write(fd, it, len); 51 if( ret < 0 ) { if( errno != EAGAIN && errno != EWOULDBLOCK) abort( "'answer error' error: (%d) %s\n", (int)errno, strerror(errno) ); } 54 int ret = cfa_send(fd, it, len, 0, CFA_IO_LAZY); 55 if( ret < 0 ) { 56 if( errno == ECONNRESET || errno == EPIPE ) return -ECONNRESET; 57 if( errno == EAGAIN || errno == EWOULDBLOCK) return -EAGAIN; 58 59 abort( "'answer error' error: (%d) %s\n", (int)errno, strerror(errno) ); 60 } 52 61 53 62 // update it/len … … 61 70 /* paranoid */ assert( code < KNOWN_CODES && code != OK200 ); 62 71 int idx = (int)code; 63 return answer( fd, http_msgs[idx] , strlen( http_msgs[idx] ));72 return answer( fd, http_msgs[idx]->msg, http_msgs[idx]->len ); 64 73 } 65 74 66 75 int answer_header( int fd, size_t size ) { 67 const char * fmt = http_msgs[OK200]; 68 int len = 200; 69 char buffer[len]; 70 len = snprintf(buffer, len, fmt, date, size); 76 char buffer[512]; 77 char * it = buffer; 78 memcpy(it, http_msgs[OK200]->msg, http_msgs[OK200]->len); 79 it += http_msgs[OK200]->len; 80 int len = http_msgs[OK200]->len; 81 len += snprintf(it, 512 - len, "%d \n\n", size); 71 82 return answer( fd, buffer, len ); 72 83 } 73 84 74 int answer_plain( int fd, char buffer[], size_t size ) { 75 int ret = answer_header(fd, size); 85 #if defined(PLAINTEXT_NOCOPY) 86 int answer_plaintext( int fd ) { 87 return answer(fd, http_msgs[OK200_PlainText]->msg, http_msgs[OK200_PlainText]->len + 1); // +1 cause snprintf doesn't count nullterminator 88 } 89 #elif defined(PLAINTEXT_1WRITE) 90 int answer_plaintext( int fd ) { 91 char text[] = "Hello, World!\n"; 92 char buffer[512 + sizeof(text)]; 93 char * it = buffer; 94 memcpy(it, http_msgs[OK200]->msg, http_msgs[OK200]->len); 95 it += http_msgs[OK200]->len; 96 int len = http_msgs[OK200]->len; 97 int r = snprintf(it, 512 - len, "%d \n\n", sizeof(text)); 98 it += r; 99 len += r; 100 memcpy(it, text, sizeof(text)); 101 return answer(fd, buffer, len + sizeof(text)); 102 } 103 #else 104 int answer_plaintext( int fd ) { 105 char text[] = "Hello, World!\n"; 106 int ret = answer_header(fd, sizeof(text)); 76 107 if( ret < 0 ) return ret; 77 return answer(fd, buffer, size); 78 } 108 return answer(fd, text, sizeof(text)); 109 } 110 #endif 79 111 80 112 int answer_empty( int fd ) { … … 83 115 84 116 85 [HttpCode code, bool closed, * const char file, size_t len] http_read(int fd, []char buffer, size_t len , io_cancellation * cancel) {117 [HttpCode code, bool closed, * const char file, size_t len] http_read(int fd, []char buffer, size_t len) { 86 118 char * it = buffer; 87 119 size_t count = len - 1; … … 89 121 READ: 90 122 for() { 91 int ret = cfa_re ad(fd, (void*)it, count, 0, -1`s, cancel, 0p);123 int ret = cfa_recv(fd, (void*)it, count, 0, CFA_IO_LAZY); 92 124 // int ret = read(fd, (void*)it, count); 93 125 if(ret == 0 ) return [OK200, true, 0, 0]; 94 126 if(ret < 0 ) { 95 127 if( errno == EAGAIN || errno == EWOULDBLOCK) continue READ; 96 // if( errno == EINVAL ) return [E400, true, 0, 0]; 128 if( errno == ECONNRESET ) return [E408, true, 0, 0]; 129 if( errno == EPIPE ) return [E408, true, 0, 0]; 97 130 abort( "read error: (%d) %s\n", (int)errno, strerror(errno) ); 98 131 } … … 108 141 } 109 142 110 if( options.log ) printf("%.*s\n", rlen, buffer); 143 if( options.log ) { 144 write(sout, buffer, rlen); 145 sout | nl; 146 } 111 147 112 148 it = buffer; … … 119 155 } 120 156 121 voidsendfile( int pipe[2], int fd, int ans_fd, size_t count ) {157 int sendfile( int pipe[2], int fd, int ans_fd, size_t count ) { 122 158 unsigned sflags = SPLICE_F_MOVE; // | SPLICE_F_MORE; 123 159 off_t offset = 0; 124 160 ssize_t ret; 125 161 SPLICE1: while(count > 0) { 126 ret = cfa_splice(ans_fd, &offset, pipe[1], 0p, count, sflags, 0, -1`s, 0p, 0p); 127 // ret = splice(ans_fd, &offset, pipe[1], 0p, count, sflags); 162 ret = cfa_splice(ans_fd, &offset, pipe[1], 0p, count, sflags, CFA_IO_LAZY); 128 163 if( ret < 0 ) { 129 164 if( errno != EAGAIN && errno != EWOULDBLOCK) continue SPLICE1; 165 if( errno == ECONNRESET ) return -ECONNRESET; 166 if( errno == EPIPE ) return -EPIPE; 130 167 abort( "splice [0] error: (%d) %s\n", (int)errno, strerror(errno) ); 131 168 } … … 135 172 size_t in_pipe = ret; 136 173 SPLICE2: while(in_pipe > 0) { 137 ret = cfa_splice(pipe[0], 0p, fd, 0p, in_pipe, sflags, 0, -1`s, 0p, 0p); 138 // ret = splice(pipe[0], 0p, fd, 0p, in_pipe, sflags); 174 ret = cfa_splice(pipe[0], 0p, fd, 0p, in_pipe, sflags, CFA_IO_LAZY); 139 175 if( ret < 0 ) { 140 176 if( errno != EAGAIN && errno != EWOULDBLOCK) continue SPLICE2; 177 if( errno == ECONNRESET ) return -ECONNRESET; 178 if( errno == EPIPE ) return -EPIPE; 141 179 abort( "splice [1] error: (%d) %s\n", (int)errno, strerror(errno) ); 142 180 } … … 145 183 146 184 } 185 return count; 147 186 } 148 187 … … 153 192 #include <thread.hfa> 154 193 194 const char * original_http_msgs[] = { 195 "HTTP/1.1 200 OK\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: ", 196 "HTTP/1.1 200 OK\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 15\n\nHello, World!\n", 197 "HTTP/1.1 400 Bad Request\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 198 "HTTP/1.1 404 Not Found\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 199 "HTTP/1.1 405 Method Not Allowed\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 200 "HTTP/1.1 408 Request Timeout\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 201 "HTTP/1.1 413 Payload Too Large\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 202 "HTTP/1.1 414 URI Too Long\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 203 }; 204 155 205 struct date_buffer { 156 char buff[100];206 https_msg_str strs[KNOWN_CODES]; 157 207 }; 158 208 … … 163 213 164 214 void ?{}( DateFormater & this ) { 165 ((thread&)this){ "Server Date Thread", *options.clopts.instance };215 ((thread&)this){ "Server Date Thread", *options.clopts.instance[0] }; 166 216 this.idx = 0; 167 memset( this.buffers[0].buff, 0, sizeof(this.buffers[0]) );168 memset( this.buffers[1].buff, 0, sizeof(this.buffers[1]) );217 memset( &this.buffers[0], 0, sizeof(this.buffers[0]) ); 218 memset( &this.buffers[1], 0, sizeof(this.buffers[1]) ); 169 219 } 170 220 … … 176 226 or else {} 177 227 228 229 char buff[100]; 178 230 Time now = getTimeNsec(); 179 180 strftime( this.buffers[this.idx].buff, 100, "%a, %d %b %Y %H:%M:%S %Z", now ); 181 182 char * next = this.buffers[this.idx].buff; 183 __atomic_exchange_n((char * volatile *)&date, next, __ATOMIC_SEQ_CST); 231 strftime( buff, 100, "%a, %d %b %Y %H:%M:%S %Z", now ); 232 sout | "Updated date to '" | buff | "'"; 233 234 for(i; KNOWN_CODES) { 235 size_t len = snprintf( this.buffers[this.idx].strs[i].msg, 512, original_http_msgs[i], buff ); 236 this.buffers[this.idx].strs[i].len = len; 237 } 238 239 for(i; KNOWN_CODES) { 240 https_msg_str * next = &this.buffers[this.idx].strs[i]; 241 __atomic_exchange_n((https_msg_str * volatile *)&http_msgs[i], next, __ATOMIC_SEQ_CST); 242 } 184 243 this.idx = (this.idx + 1) % 2; 244 245 sout | "Date thread sleeping"; 185 246 186 247 sleep(1`s); -
benchmark/io/http/protocol.hfa
r342af53 r8e4aa05 1 1 #pragma once 2 3 struct io_cancellation;4 2 5 3 enum HttpCode { 6 4 OK200 = 0, 5 OK200_PlainText, 7 6 E400, 8 7 E404, 8 E405, 9 E408, 9 10 E413, 10 11 E414, … … 16 17 int answer_error( int fd, HttpCode code ); 17 18 int answer_header( int fd, size_t size ); 18 int answer_plain ( int fd, char buffer [], size_t size);19 int answer_plaintext( int fd ); 19 20 int answer_empty( int fd ); 20 21 21 [HttpCode code, bool closed, * const char file, size_t len] http_read(int fd, []char buffer, size_t len , io_cancellation *);22 [HttpCode code, bool closed, * const char file, size_t len] http_read(int fd, []char buffer, size_t len); 22 23 23 voidsendfile( int pipe[2], int fd, int ans_fd, size_t count );24 int sendfile( int pipe[2], int fd, int ans_fd, size_t count ); -
benchmark/io/http/worker.cfa
r342af53 r8e4aa05 6 6 #include <unistd.h> 7 7 8 #include <fstream.hfa> 8 9 #include <iofwd.hfa> 9 10 … … 16 17 //============================================================================================= 17 18 void ?{}( Worker & this ) { 18 ((thread&)this){ "Server Worker Thread", *options.clopts.instance }; 19 size_t cli = rand() % options.clopts.cltr_cnt; 20 ((thread&)this){ "Server Worker Thread", *options.clopts.instance[cli] }; 21 options.clopts.thrd_cnt[cli]++; 19 22 this.pipe[0] = -1; 20 23 this.pipe[1] = -1; … … 33 36 CONNECTION: 34 37 for() { 35 if( options.log ) printf("=== Accepting connection ===\n"); 36 int fd = cfa_accept4( this.[sockfd, addr, addrlen, flags], 0, -1`s, &this.cancel, 0p ); 37 // int fd = accept4( this.[sockfd, addr, addrlen, flags] ); 38 if( options.log ) sout | "=== Accepting connection ==="; 39 int fd = cfa_accept4( this.[sockfd, addr, addrlen, flags], CFA_IO_LAZY ); 38 40 if(fd < 0) { 39 41 if( errno == ECONNABORTED ) break; 40 if( errno == EINVAL && this.done) break;42 if( this.done && (errno == EINVAL || errno == EBADF) ) break; 41 43 abort( "accept error: (%d) %s\n", (int)errno, strerror(errno) ); 42 44 } 45 if(this.done) break; 43 46 44 if( options.log ) printf("=== New connection %d, waiting for requests ===\n", fd);47 if( options.log ) sout | "=== New connection" | fd | "" | ", waiting for requests ==="; 45 48 REQUEST: 46 49 for() { … … 53 56 size_t len = options.socket.buflen; 54 57 char buffer[len]; 55 if( options.log ) printf("=== Reading request ===\n");56 [code, closed, file, name_size] = http_read(fd, buffer, len , &this.cancel);58 if( options.log ) sout | "=== Reading request ==="; 59 [code, closed, file, name_size] = http_read(fd, buffer, len); 57 60 58 61 // if we are done, break out of the loop 59 if( closed ) { 60 if( options.log ) printf("=== Connection closed ===\n"); 61 close(fd); 62 continue CONNECTION; 63 } 62 if( closed ) break REQUEST; 64 63 65 64 // If this wasn't a request retrun 400 66 65 if( code != OK200 ) { 67 printf("=== Invalid Request : %d ===\n", code_val(code));66 sout | "=== Invalid Request :" | code_val(code) | "==="; 68 67 answer_error(fd, code); 69 68 continue REQUEST; … … 71 70 72 71 if(0 == strncmp(file, "plaintext", min(name_size, sizeof("plaintext") ))) { 73 if( options.log ) printf("=== Request for /plaintext ===\n");72 if( options.log ) sout | "=== Request for /plaintext ==="; 74 73 75 char text[] = "Hello, World!\n"; 74 int ret = answer_plaintext(fd); 75 if( ret == -ECONNRESET ) break REQUEST; 76 76 77 // Send the header 78 answer_plain(fd, text, sizeof(text)); 79 80 if( options.log ) printf("=== Answer sent ===\n"); 77 if( options.log ) sout | "=== Answer sent ==="; 81 78 continue REQUEST; 82 79 } 83 80 84 81 if(0 == strncmp(file, "ping", min(name_size, sizeof("ping") ))) { 85 if( options.log ) printf("=== Request for /ping ===\n");82 if( options.log ) sout | "=== Request for /ping ==="; 86 83 87 84 // Send the header 88 answer_empty(fd); 85 int ret = answer_empty(fd); 86 if( ret == -ECONNRESET ) break REQUEST; 89 87 90 if( options.log ) printf("=== Answer sent ===\n");88 if( options.log ) sout | "=== Answer sent ==="; 91 89 continue REQUEST; 92 90 } 93 91 94 if( options.log ) printf("=== Request for file %.*s ===\n", (int)name_size, file); 92 if( options.log ) { 93 sout | "=== Request for file " | nonl; 94 write(sout, file, name_size); 95 sout | " ==="; 96 } 97 98 if( !options.file_cache.path ) { 99 if( options.log ) { 100 sout | "=== File Not Found (" | nonl; 101 write(sout, file, name_size); 102 sout | ") ==="; 103 } 104 answer_error(fd, E405); 105 continue REQUEST; 106 } 95 107 96 108 // Get the fd from the file cache … … 101 113 // If we can't find the file, return 404 102 114 if( ans_fd < 0 ) { 103 printf("=== File Not Found ===\n"); 115 if( options.log ) { 116 sout | "=== File Not Found (" | nonl; 117 write(sout, file, name_size); 118 sout | ") ==="; 119 } 104 120 answer_error(fd, E404); 105 121 continue REQUEST; … … 107 123 108 124 // Send the header 109 answer_header(fd, count); 125 int ret = answer_header(fd, count); 126 if( ret == -ECONNRESET ) break REQUEST; 110 127 111 128 // Send the desired file 112 sendfile( this.pipe, fd, ans_fd, count); 129 ret = sendfile( this.pipe, fd, ans_fd, count); 130 if( ret == -ECONNRESET ) break REQUEST; 113 131 114 if( options.log ) printf("=== Answer sent ===\n");132 if( options.log ) sout | "=== Answer sent ==="; 115 133 } 134 135 if( options.log ) sout | "=== Connection closed ==="; 136 close(fd); 137 continue CONNECTION; 116 138 } 117 139 } -
benchmark/io/http/worker.hfa
r342af53 r8e4aa05 17 17 socklen_t * addrlen; 18 18 int flags; 19 io_cancellation cancel;20 19 volatile bool done; 21 20 }; -
doc/LaTeXmacros/common.tex
r342af53 r8e4aa05 11 11 %% Created On : Sat Apr 9 10:06:17 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Mon Oct 5 09:34:46 202014 %% Update Count : 46413 %% Last Modified On : Sun Feb 14 15:52:46 2021 14 %% Update Count : 524 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 32 32 \setlist[enumerate]{listparindent=\parindent}% global 33 33 \setlist[enumerate,2]{leftmargin=\parindent,labelsep=*,align=parleft,label=\alph*.}% local 34 \setlist[description]{ itemsep=0pt,listparindent=\parindent,leftmargin=\parindent,labelsep=1.5ex}34 \setlist[description]{topsep=0.5ex,itemsep=0pt,listparindent=\parindent,leftmargin=\parindent,labelsep=1.5ex} 35 35 36 36 % Names used in the document. 37 37 38 38 \usepackage{xspace} 39 \newcommand{\CFAIcon}{\textsf{C}\raisebox{\depth}{\rotatebox{180}{\textsf{A}}}\xspace} % Cforall symbolic name 40 \newcommand{\CFA}{\protect\CFAIcon} % safe for section/caption 41 \newcommand{\CFL}{\textrm{Cforall}\xspace} % Cforall symbolic name 42 \newcommand{\Celeven}{\textrm{C11}\xspace} % C11 symbolic name 43 \newcommand{\CC}{\textrm{C}\kern-.1em\hbox{+\kern-.25em+}\xspace} % C++ symbolic name 44 \newcommand{\CCeleven}{\textrm{C}\kern-.1em\hbox{+\kern-.25em+}11\xspace} % C++11 symbolic name 45 \newcommand{\CCfourteen}{\textrm{C}\kern-.1em\hbox{+\kern-.25em+}14\xspace} % C++14 symbolic name 46 \newcommand{\CCseventeen}{\textrm{C}\kern-.1em\hbox{+\kern-.25em+}17\xspace} % C++17 symbolic name 47 \newcommand{\CCtwenty}{\textrm{C}\kern-.1em\hbox{+\kern-.25em+}20\xspace} % C++20 symbolic name 39 \newcommand{\CFAIcon}{\textsf{C}\raisebox{\depth}{\rotatebox{180}{\textsf{A}}}} % Cforall icon 40 \newcommand{\CFA}{\protect\CFAIcon\xspace} % CFA symbolic name 41 \newcommand{\CFL}{\textrm{Cforall}\xspace} % Cforall non-icon name 42 \newcommand{\Celeven}{\textrm{C11}\xspace} % C11 symbolic name 43 \newcommand{\CCIcon}{\textrm{C}\kern-.1em\hbox{+\kern-.25em+}} % C++ icon 44 \newcommand{\CC}{\protect\CCIcon\xspace} % C++ symbolic name 45 % numbers disallowed in latex variables names => use number names 46 \newcommand{\CCeleven}{\protect\CCIcon{11}\xspace} % C++11 symbolic name 47 \newcommand{\CCfourteen}{\protect\CCIcon{14}\xspace} % C++14 symbolic name 48 \newcommand{\CCseventeen}{\protect\CCIcon{17}\xspace} % C++17 symbolic name 49 \newcommand{\CCtwenty}{\protect\CCIcon{20}\xspace} % C++20 symbolic name 48 50 \newcommand{\Csharp}{C\raisebox{-0.7ex}{\Large$^\sharp$}\xspace} % C# symbolic name 49 51 50 52 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 51 53 54 % remove special-character warning in PDF side-bar names 52 55 \makeatletter 56 \@ifpackageloaded{hyperref}{ 57 \pdfstringdefDisableCommands{ 58 \def\CFA{\CFL} 59 \def\Celeven{C11\xspace} 60 \def\CC{C++\xspace} 61 \def\CCeleven{C++11\xspace} 62 \def\CCfourteen{C++14\xspace} 63 \def\CCseventeen{C++17\xspace} 64 \def\CCtwenty{C++20\xspace} 65 \def\Csharp{C\#\xspace} 66 \def\lstinline{\xspace}% must use {} as delimiters, e.g., \lstinline{...} 67 }{} 68 } 69 53 70 % parindent is relative, i.e., toggled on/off in environments like itemize, so store the value for 54 71 % use rather than use \parident directly. … … 81 98 \vskip 50\p@ 82 99 }} 83 \renewcommand\section{\@startsection{section}{1}{\z@}{-3. 5ex \@plus -1ex \@minus -.2ex}{1.75ex \@plus .2ex}{\normalfont\large\bfseries}}84 \renewcommand\subsection{\@startsection{subsection}{2}{\z@}{- 3.25ex \@plus -1ex \@minus -.2ex}{1.5ex \@plus .2ex}{\normalfont\normalsize\bfseries}}100 \renewcommand\section{\@startsection{section}{1}{\z@}{-3.0ex \@plus -1ex \@minus -.2ex}{1.5ex \@plus .2ex}{\normalfont\large\bfseries}} 101 \renewcommand\subsection{\@startsection{subsection}{2}{\z@}{-2.75ex \@plus -1ex \@minus -.2ex}{1.25ex \@plus .2ex}{\normalfont\normalsize\bfseries}} 85 102 \renewcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}{-2.5ex \@plus -1ex \@minus -.2ex}{1.0ex \@plus .2ex}{\normalfont\normalsize\bfseries}} 86 103 \renewcommand\paragraph{\@startsection{paragraph}{4}{\z@}{-2.0ex \@plus -1ex \@minus -.2ex}{-1em}{\normalfont\normalsize\bfseries}} … … 89 106 \newcommand{\italic}[1]{\emph{\hyperpage{#1}}} 90 107 \newcommand{\Definition}[1]{\textbf{\hyperpage{#1}}} 91 \newcommand{\see}[1]{ \emph{see}~#1}108 \newcommand{\see}[1]{(see #1)} 92 109 93 110 % Define some commands that produce formatted index entries suitable for cross-references. … … 129 146 % The star version does not lowercase the index information, e.g., \newterm*{IBM}. 130 147 \newcommand{\newtermFontInline}{\emph} 131 \newcommand{\newterm}{\ @ifstar\@snewterm\@newterm}148 \newcommand{\newterm}{\protect\@ifstar\@snewterm\@newterm} 132 149 \newcommand{\@newterm}[2][\@empty]{\lowercase{\def\temp{#2}}{\newtermFontInline{#2}}\ifx#1\@empty\index{\temp}\else\index{#1@{\protect#2}}\fi} 133 150 \newcommand{\@snewterm}[2][\@empty]{{\newtermFontInline{#2}}\ifx#1\@empty\index{#2}\else\index{#1@{\protect#2}}\fi} … … 229 246 \usepackage{listings} % format program code 230 247 \usepackage{lstlang} 248 \usepackage{calc} % latex arithmetic 249 231 250 \makeatletter 232 233 251 \newcommand{\LstBasicStyle}[1]{{\lst@basicstyle{#1}}} 234 252 \newcommand{\LstKeywordStyle}[1]{{\lst@basicstyle{\lst@keywordstyle{#1}}}} 235 253 \newcommand{\LstCommentStyle}[1]{{\lst@basicstyle{\lst@commentstyle{#1}}}} 254 \newcommand{\LstStringStyle}[1]{{\lst@basicstyle{\lst@stringstyle{#1}}}} 236 255 237 256 \newlength{\gcolumnposn} % temporary hack because lstlisting does not handle tabs correctly … … 259 278 xleftmargin=\parindentlnth, % indent code to paragraph indentation 260 279 extendedchars=true, % allow ASCII characters in the range 128-255 261 escapechar= §, % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-'262 mathescape= true, % LaTeX math escape in CFA code $...$280 escapechar=\$, % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-' 281 mathescape=false, % LaTeX math escape in CFA code $...$ 263 282 keepspaces=true, % 264 283 showstringspaces=false, % do not show spaces with cup 265 284 showlines=true, % show blank lines at end of code 266 285 aboveskip=4pt, % spacing above/below code block 267 belowskip=3pt, 286 belowskip=0pt, 287 numberstyle=\footnotesize\sf, % numbering style 268 288 % replace/adjust listing characters that look bad in sanserif 269 289 literate={-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.75ex}{0.1ex}}}}1 {^}{\raisebox{0.6ex}{$\scriptscriptstyle\land\,$}}1 … … 274 294 275 295 \ifdefined\CFALatin% extra Latin-1 escape characters 276 \lstnewenvironment{cfa}[1][]{ 296 \lstnewenvironment{cfa}[1][]{% necessary 277 297 \lstset{ 278 298 language=CFA, 279 moredelim=**[is][\color{red}]{®}{®}, % red highlighting ®...® (registered trademark symbol) emacs: C-q M-. 280 moredelim=**[is][\color{blue}]{ß}{ß}, % blue highlighting ß...ß (sharp s symbol) emacs: C-q M-_ 281 moredelim=**[is][\color{OliveGreen}]{¢}{¢}, % green highlighting ¢...¢ (cent symbol) emacs: C-q M-" 282 moredelim=[is][\lstset{keywords={}}]{¶}{¶}, % keyword escape ¶...¶ (pilcrow symbol) emacs: C-q M-^ 283 % replace/adjust listing characters that look bad in sanserif 284 add to literate={`}{\ttfamily\upshape\hspace*{-0.1ex}`}1 299 moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 300 %moredelim=**[is][\color{red}]{®}{®}, % red highlighting ®...® (registered trademark symbol) emacs: C-q M-. 301 %moredelim=**[is][\color{blue}]{ß}{ß}, % blue highlighting ß...ß (sharp s symbol) emacs: C-q M-_ 302 %moredelim=**[is][\color{OliveGreen}]{¢}{¢}, % green highlighting ¢...¢ (cent symbol) emacs: C-q M-" 303 %moredelim=[is][\lstset{keywords={}}]{¶}{¶}, % keyword escape ¶...¶ (pilcrow symbol) emacs: C-q M-^ 285 304 }% lstset 286 \lstset{#1} 305 \lstset{#1}% necessary 287 306 }{} 288 307 % inline code ©...© (copyright symbol) emacs: C-q M-) 289 308 \lstMakeShortInline© % single-character for \lstinline 290 309 \else% regular ASCI characters 291 \lstnewenvironment{cfa}[1][]{ 310 \lstnewenvironment{cfa}[1][]{% necessary 292 311 \lstset{ 293 312 language=CFA, 294 313 escapechar=\$, % LaTeX escape in CFA code 314 mathescape=false, % LaTeX math escape in CFA code $...$ 295 315 moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 296 316 }% lstset 297 \lstset{#1} 317 \lstset{#1}% necessary 298 318 }{} 299 319 % inline code @...@ (at symbol) -
doc/bibliography/pl.bib
r342af53 r8e4aa05 688 688 title = {Asynchronous Exception Propagation in Blocked Tasks}, 689 689 booktitle = {4th International Workshop on Exception Handling (WEH.08)}, 690 o rganization= {16th International Symposium on the Foundations of Software Engineering (FSE 16)},690 optorganization= {16th International Symposium on the Foundations of Software Engineering (FSE 16)}, 691 691 address = {Atlanta, U.S.A}, 692 692 month = nov, … … 1797 1797 } 1798 1798 1799 @article{Delisle 19,1799 @article{Delisle20, 1800 1800 keywords = {concurrency, Cforall}, 1801 1801 contributer = {pabuhr@plg}, 1802 1802 author = {Thierry Delisle and Peter A. Buhr}, 1803 1803 title = {Advanced Control-flow and Concurrency in \textsf{C}$\mathbf{\forall}$}, 1804 year = 20 19,1804 year = 2020, 1805 1805 journal = spe, 1806 pages = {1-33}, 1807 note = {submitted}, 1806 pages = {1-38}, 1807 note = {\href{https://doi-org.proxy.lib.uwaterloo.ca/10.1002/spe.2925}{https://\-doi-org.proxy.lib.uwaterloo.ca/\-10.1002/\-spe.2925}}, 1808 note = {}, 1808 1809 } 1809 1810 … … 7246 7247 7247 7248 @inproceedings{Edelson92, 7248 keywords = {persistence, pointers},7249 keywords = {persistence, smart pointers}, 7249 7250 contributer = {pabuhr@plg}, 7250 7251 author = {Daniel R. Edelson}, … … 7256 7257 year = 1992, 7257 7258 pages = {1-19}, 7259 } 7260 7261 @incollection{smartpointers, 7262 keywords = {smart pointers}, 7263 contributer = {pabuhr@plg}, 7264 author = {Andrei Alexandrescu}, 7265 title = {Smart Pointers}, 7266 booktitle = {Modern C++ Design: Generic Programming and Design Patterns Applied}, 7267 publisher = {Addison-Wesley}, 7268 year = 2001, 7269 chapter = 7, 7270 optpages = {?-?}, 7258 7271 } 7259 7272 … … 8245 8258 } 8246 8259 8260 @misc{vistorpattern, 8261 keywords = {visitor pattern}, 8262 contributer = {pabuhr@plg}, 8263 key = {vistor pattern}, 8264 title = {vistor pattern}, 8265 year = 2020, 8266 note = {WikipediA}, 8267 howpublished= {\href{https://en.wikipedia.org/wiki/Visitor\_pattern} 8268 {https://\-en.wikipedia.org/\-wiki/\-Visitor\_pattern}}, 8269 } 8270 8247 8271 % W 8248 8272 -
doc/papers/concurrency/mail2
r342af53 r8e4aa05 1288 1288 1289 1289 1290 From: "Wiley Online Proofing" <onlineproofing@eproofing.in> 1291 To: pabuhr@uwaterloo.ca 1292 Reply-To: eproofing@wiley.com 1293 Date: 3 Nov 2020 08:25:06 +0000 1294 Subject: Action: Proof of SPE_EV_SPE2925 for Software: Practice And Experience ready for review 1295 1296 Dear Dr. Peter Buhr, 1297 1298 The proof of your Software: Practice And Experience article Advanced control-flow in Cforall is now available for review: 1299 1300 Edit Article https://wiley.eproofing.in/Proof.aspx?token=ab7739d5678447fbbe5036f3bcba2445081500061 1301 1302 To review your article, please complete the following steps, ideally within 48 hours*, so we can publish your article as quickly as possible. 1303 1304 1. Open your proof in the online proofing system using the button above. 1305 2. Check the article for correctness and respond to all queries.For instructions on using the system, please see the "Help" menu in the upper right corner. 1306 3. Submit your changes by clicking the "Submit" button in the proofing system. 1307 1308 Helpful Tips 1309 1310 * Your manuscript has been formatted following the style requirements for the journal. Any requested changes that go against journal style will not be made. 1311 * Your proof will include queries. These must be replied to using the system before the proof can be submitted. 1312 * The only acceptable changes at this stage are corrections to grammatical errors or data accuracy, or to provide higher resolution figure files (if requested by the typesetter). 1313 * Any changes to scientific content or authorship will require editorial review and approval. 1314 * Once your changes are complete, submit the article after which no additional corrections can be requested. 1315 * Most authors complete their corrections within 48 hours. Returning any corrections promptly will accelerate publication of your article. 1316 1317 If you encounter any problems or have questions, please contact the production office at (SPEproofs@wiley.com). For the quickest response, include the journal name and your article ID (found in the subject line) in all correspondence. 1318 1319 Best regards, 1320 Software: Practice And Experience Production Office 1321 1322 * We appreciate that the COVID-19 pandemic may create conditions for you that make it difficult for you to review your proof within standard timeframes. If you have any problems keeping to this schedule, please reach out to me at (SPEproofs@wiley.com) to discuss alternatives. 1323 1324 1325 1290 1326 From: "Pacaanas, Joel -" <jpacaanas@wiley.com> 1291 1327 To: "Peter A. Buhr" <pabuhr@uwaterloo.ca> … … 1345 1381 1346 1382 Since the proof was reset, your added corrections before has also been removed. Please add them back. 1347 1348 1383 Please return your corrections at your earliest convenience. 1349 1384 … … 1384 1419 Best regards, 1385 1420 Joel Pacaanas 1421 1422 1423 1424 Date: Wed, 2 Dec 2020 08:49:52 +0000 1425 From: <cs-author@wiley.com> 1426 To: <pabuhr@uwaterloo.ca> 1427 Subject: Published: Your article is now published in Early View! 1428 1429 Dear Peter Buhr, 1430 1431 Your article Advanced Control-flow and Concurrency in C A in Software: Practice and Experience has the following publication status: Published as Early View 1432 1433 To access your article, please click the following link to register or log in: 1434 1435 https://authorservices.wiley.com/index.html#register 1436 1437 You can also access your published article via this link: http://dx.doi.org/10.1002/spe.2925 1438 1439 If you need any assistance, please click here https://hub.wiley.com/community/support/authorservices to view our Help section. 1440 1441 Sincerely, 1442 Wiley Author Services 1443 1444 1445 Date: Wed, 2 Dec 2020 02:16:23 -0500 1446 From: <no-reply@copyright.com> 1447 To: <pabuhr@uwaterloo.ca> 1448 CC: <SPEproofs@wiley.com> 1449 Subject: Please submit your publication fee(s) SPE2925 1450 1451 John Wiley and Sons 1452 Please submit your selection and payment for publication fee(s). 1453 1454 Dear Peter A. Buhr, 1455 1456 Congratulations, your article in Software: Practice and Experience has published online: 1457 1458 Manuscript DOI: 10.1002/spe.2925 1459 Manuscript ID: SPE2925 1460 Manuscript Title: Advanced control-flow in Cforall 1461 Published by: John Wiley and Sons 1462 1463 Please carefully review your publication options. If you wish your colour 1464 figures to be printed in colour, you must select and pay for that option now 1465 using the RightsLink e-commerce solution from CCC. 1466 1467 Review my options & pay charges 1468 https://oa.copyright.com/apc-payment-ui/overview?id=f46ba36a-2565-4c8d-8865-693bb94d87e5&chargeset=CHARGES 1469 1470 To review and pay your charge(s), please click here 1471 <https://oa.copyright.com/apc-payment-ui/overview?id=f46ba36a-2565-4c8d-8865-693bb94d87e5&chargeset=CHARGES>. You 1472 can also forward this link to another party for processing. 1473 1474 To complete a secure transaction, you will need a RightsLink account 1475 <https://oa.copyright.com/apc-payment-ui/registration?id=f46ba36a-2565-4c8d-8865-693bb94d87e5&chargeset=CHARGES>. If 1476 you do not have one already, you will be prompted to register as you are 1477 checking out your author charges. This is a very quick process; the majority of 1478 your registration form will be pre-populated automatically with information we 1479 have already supplied to RightsLink. 1480 1481 If you have any questions about these charges, please contact CCC Customer 1482 Service <wileysupport@copyright.com> using the information below. Please do not 1483 reply directly to this email as this is an automated email notification sent 1484 from an unmonitored account. 1485 1486 Sincerely, 1487 John Wiley and Sons 1488 1489 Tel.: +1-877-622-5543 / +1-978-646-2777 1490 wileysupport@copyright.com 1491 www.copyright.com 1492 1493 Copyright Clearance Center 1494 RightsLink 1495 1496 This message (including attachments) is confidential, unless marked 1497 otherwise. It is intended for the addressee(s) only. If you are not an intended 1498 recipient, please delete it without further distribution and reply to the 1499 sender that you have received the message in error. 1500 1501 1502 1503 From: "Pacaanas, Joel -" <jpacaanas@wiley.com> 1504 To: "Peter A. Buhr" <pabuhr@uwaterloo.ca> 1505 Subject: RE: Please submit your publication fee(s) SPE2925 1506 Date: Thu, 3 Dec 2020 08:45:10 +0000 1507 1508 Dear Dr Buhr, 1509 1510 Thank you for your email and concern with regard to the RightsLink account. As 1511 you have mentioned that all figures will be printed as black and white, then I 1512 have selected it manually from the system to proceed further. 1513 1514 Best regards, 1515 Joel 1516 1517 Joel Q. Pacaanas 1518 Production Editor 1519 On behalf of Wiley 1520 Manila 1521 We partner with global experts to further innovative research. 1522 1523 E-mail: jpacaanas@wiley.com 1524 Tel: +632 88558618 1525 Fax: +632 5325 0768 1526 1527 -----Original Message----- 1528 From: Peter A. Buhr [mailto:pabuhr@uwaterloo.ca] 1529 Sent: Thursday, December 3, 2020 12:28 AM 1530 To: SPE Proofs <speproofs@wiley.com> 1531 Subject: Re: Please submit your publication fee(s) SPE2925 1532 1533 I am trying to complete the forms to submit my publication fee. 1534 1535 I clicked all the boxs to print in Black and White, so there is no fee. 1536 1537 I then am asked to create RightsLink account, which I did. 1538 1539 However, it requires that I click a box agreeing to: 1540 1541 I consent to have my contact information shared with my publisher and/or 1542 funding organization, as needed, to facilitate APC payment(s), reporting and 1543 customer care. 1544 1545 I do not agree to this sharing and will not click this button. 1546 1547 How would you like to proceed? 1548 1549 1550 1551 From: "Pacaanas, Joel -" <jpacaanas@wiley.com> 1552 To: "Peter A. Buhr" <pabuhr@uwaterloo.ca> 1553 Subject: RE: Please submit your publication fee(s) SPE2925 1554 Date: Fri, 4 Dec 2020 07:55:59 +0000 1555 1556 Dear Peter, 1557 1558 Yes, you are now done with this selection. 1559 1560 Thank you. 1561 1562 Best regards, 1563 Joel 1564 1565 Joel Q. Pacaanas 1566 Production Editor 1567 On behalf of Wiley 1568 Manila 1569 We partner with global experts to further innovative research. 1570 1571 E-mail: jpacaanas@wiley.com 1572 Tel: +632 88558618 1573 Fax: +632 5325 0768 1574 1575 -----Original Message----- 1576 From: Peter A. Buhr [mailto:pabuhr@uwaterloo.ca] 1577 Sent: Thursday, December 3, 2020 10:29 PM 1578 To: Pacaanas, Joel - <jpacaanas@wiley.com> 1579 Subject: Re: Please submit your publication fee(s) SPE2925 1580 1581 Thank you for your email and concern with regard to the RightsLink 1582 account. As you have mentioned that all figures will be printed as black and 1583 white, then I have selected it manually from the system to proceed further. 1584 1585 Just be clear, am I done? Meaning I do not have to go back to that web-page again. -
doc/theses/andrew_beach_MMath/.gitignore
r342af53 r8e4aa05 3 3 4 4 # Final Files: 5 thesis.pdf5 *.pdf 6 6 7 7 # The Makefile here is not generated. -
doc/theses/andrew_beach_MMath/Makefile
r342af53 r8e4aa05 1 1 ### Makefile for Andrew Beach's Masters Thesis 2 2 3 DOC= thesis.pdf3 DOC=uw-ethesis.pdf 4 4 BUILD=out 5 5 TEXSRC=$(wildcard *.tex) … … 7 7 STYSRC=$(wildcard *.sty) 8 8 CLSSRC=$(wildcard *.cls) 9 TEXLIB= .: ${BUILD}:9 TEXLIB= .:../../LaTeXmacros:${BUILD}: 10 10 BIBLIB= .:../../bibliography 11 11 … … 29 29 ${LATEX} ${BASE} 30 30 ${BIBTEX} ${BUILD}/${BASE} 31 ${LATEX} ${BASE} 31 32 ${GLOSSARY} ${BUILD}/${BASE} 32 33 ${LATEX} ${BASE} -
doc/theses/andrew_beach_MMath/existing.tex
r342af53 r8e4aa05 1 \chapter{\CFA{} Existing Features} 2 3 \section{Overloading and extern} 4 Cforall has overloading, allowing multiple definitions of the same name to 5 be defined. 6 7 This also adds name mangling so that the assembly symbols are unique for 8 different overloads. For compatability with names in C there is also a 9 syntax to diable the name mangling. These unmangled names cannot be overloaded 10 but act as the interface between C and \CFA code. 11 12 The syntax for disabling mangling is: 13 \begin{lstlisting} 14 extern "C" { 15 ... 16 } 17 \end{lstlisting} 18 19 To re-enable mangling once it is disabled the syntax is: 20 \begin{lstlisting} 21 extern "Cforall" { 22 ... 23 } 24 \end{lstlisting} 25 26 Both should occur at the declaration level and effect all the declarations 27 in \texttt{...}. Neither care about the state of mangling when they begin 28 and will return to that state after the group is finished. So re-enabling 29 is only used to nest areas of mangled and unmangled declarations. 30 31 \section{References} 32 \CFA adds references to C. These are auto-dereferencing pointers and use the 33 same syntax as pointers except they use ampersand (\codeCFA{\&}) instead of 34 the asterisk (\codeCFA{*}). They can also be constaint or mutable, if they 35 are mutable they may be assigned to by using the address-of operator 36 (\codeCFA\&) which converts them into a pointer. 1 \chapter{\CFA Existing Features} 2 3 \CFA (C-for-all)~\cite{Cforall} is an open-source project extending ISO C with 4 modern safety and productivity features, while still ensuring backwards 5 compatibility with C and its programmers. \CFA is designed to have an 6 orthogonal feature-set based closely on the C programming paradigm 7 (non-object-oriented) and these features can be added incrementally to an 8 existing C code-base allowing programmers to learn \CFA on an as-needed basis. 9 10 Only those \CFA features pertinent to this thesis are discussed. Many of the 11 \CFA syntactic and semantic features used in the thesis should be fairly 12 obvious to the reader. 13 14 \section{Overloading and \lstinline{extern}} 15 \CFA has extensive overloading, allowing multiple definitions of the same name 16 to be defined.~\cite{Moss18} 17 \begin{cfa} 18 char i; int i; double i; $\C[3.75in]{// variable overload}$ 19 int f(); double f(); $\C{// return overload}$ 20 void g( int ); void g( double ); $\C{// parameter overload}\CRT$ 21 \end{cfa} 22 This feature requires name mangling so the assembly symbols are unique for 23 different overloads. For compatibility with names in C, there is also a syntax 24 to disable name mangling. These unmangled names cannot be overloaded but act as 25 the interface between C and \CFA code. The syntax for disabling/enabling 26 mangling is: 27 \begin{cfa} 28 // name mangling 29 int i; // _X1ii_1 30 @extern "C"@ { // no name mangling 31 int j; // j 32 @extern "Cforall"@ { // name mangling 33 int k; // _X1ki_1 34 } 35 // no name mangling 36 } 37 // name mangling 38 \end{cfa} 39 Both forms of @extern@ affect all the declarations within their nested lexical 40 scope and transition back to the previous mangling state when the lexical scope 41 ends. 42 43 \section{Reference Type} 44 \CFA adds a rebindable reference type to C, but more expressive than the \Cpp 45 reference. Multi-level references are allowed and act like auto-dereferenced 46 pointers using the ampersand (@&@) instead of the pointer asterisk (@*@). \CFA 47 references may also be mutable or non-mutable. If mutable, a reference variable 48 may be assigned to using the address-of operator (@&@), which converts the 49 reference to a pointer. 50 \begin{cfa} 51 int i, j; 52 int @&@ ri = i, @&&@ rri = ri; 53 rri = 3; // auto-dereference assign to i 54 @&@ri = @&@j; // rebindable 55 ri = 5; // assign to j 56 \end{cfa} 37 57 38 58 \section{Constructors and Destructors} 39 59 40 60 Both constructors and destructors are operators, which means they are just 41 functions with special names. The special names are used to define them and 42 may be used to call the functions expicately. The \CFA special names are 43 constructed by taking the tokens in the operators and putting \texttt{?} where 44 the arguments would go. So multiplication is \texttt{?*?} while dereference 45 is \texttt{*?}. This also make it easy to tell the difference between 46 pre-fix operations (such as \texttt{++?}) and post-fix operations 47 (\texttt{?++}). 48 49 The special name for contructors is \texttt{?\{\}}, which comes from the 50 initialization syntax in C. The special name for destructors is 51 \texttt{\^{}?\{\}}. % I don't like the \^{} symbol but $^\wedge$ isn't better. 52 53 Any time a type T goes out of scope the destructor matching 54 \codeCFA{void ^?\{\}(T \&);} is called. In theory this is also true of 55 primitive types such as \codeCFA{int}, but in practice those are no-ops and 56 are usually omitted for optimization. 61 functions with special operator names rather than type names in \Cpp. The 62 special operator names may be used to call the functions explicitly (not 63 allowed in \Cpp for constructors). 64 65 In general, operator names in \CFA are constructed by bracketing an operator 66 token with @?@, which indicates where the arguments. For example, infixed 67 multiplication is @?*?@ while prefix dereference is @*?@. This syntax make it 68 easy to tell the difference between prefix operations (such as @++?@) and 69 post-fix operations (@?++@). 70 71 The special name for a constructor is @?{}@, which comes from the 72 initialization syntax in C. The special name for a destructor is @^{}@, where 73 the @^@ has no special meaning. 74 % I don't like the \^{} symbol but $^\wedge$ isn't better. 75 \begin{cfa} 76 struct T { ... }; 77 void ?@{}@(@T &@ this, ...) { ... } // constructor 78 void ?@^{}@(@T &@ this, ...) { ... } // destructor 79 { 80 T s = @{@ ... @}@; // same constructor/initialization braces 81 } // destructor call automatically generated 82 \end{cfa} 83 The first parameter is a reference parameter to the type for the 84 constructor/destructor. Destructors may have multiple parameters. The compiler 85 implicitly matches an overloaded constructor @void ^?{}(T &, ...);@ to an 86 object declaration with associated initialization, and generates a construction 87 call after the object is allocated. When an object goes out of scope, the 88 matching overloaded destructor @void ^?{}(T &);@ is called. Without explicit 89 definition, \CFA creates a default and copy constructor, destructor and 90 assignment (like \Cpp). It is possible to define constructors/destructors for 91 basic and existing types. 57 92 58 93 \section{Polymorphism} 59 \CFA uses polymorphism to create functions and types that are defined over 60 different types. \CFA polymorphic declarations serve the same role as \CPP 61 templates or Java generics. 62 63 Polymorphic declaractions start with a forall clause that goes before the 64 standard (monomorphic) declaration. These declarations have the same syntax 65 except that you may use the names introduced by the forall clause in them. 66 67 Forall clauses are written \codeCFA{forall( ... )} where \codeCFA{...} becomes 68 the list of polymorphic variables (local type names) and assertions, which 69 repersent required operations on those types. 70 71 \begin{lstlisting} 72 forall(dtype T | { void do_once(T &); }) 73 void do_twice(T & value) { 74 do_once(value); 75 do_once(value); 76 } 77 \end{lstlisting} 78 79 A polymorphic function can be used in the same way normal functions are. 80 The polymorphics variables are filled in with concrete types and the 81 assertions are checked. An assertion checked by seeing if that name of that 82 type (with all the variables replaced with the concrete types) is defined at 83 the the call site. 84 85 As an example, even if no function named \codeCFA{do_once} is not defined 86 near the definition of \codeCFA{do_twice} the following code will work. 87 \begin{lstlisting} 94 \CFA uses parametric polymorphism to create functions and types that are 95 defined over multiple types. \CFA polymorphic declarations serve the same role 96 as \Cpp templates or Java generics. The ``parametric'' means the polymorphism is 97 accomplished by passing argument operations to associate \emph{parameters} at 98 the call site, and these parameters are used in the function to differentiate 99 among the types the function operates on. 100 101 Polymorphic declarations start with a universal @forall@ clause that goes 102 before the standard (monomorphic) declaration. These declarations have the same 103 syntax except they may use the universal type names introduced by the @forall@ 104 clause. For example, the following is a polymorphic identity function that 105 works on any type @T@: 106 \begin{cfa} 107 @forall( T )@ @T@ identity( @T@ val ) { return val; } 108 int forty_two = identity( 42 ); // T bound to int, forty_two == 42 109 \end{cfa} 110 111 To allow a polymorphic function to be separately compiled, the type @T@ must be 112 constrained by the operations used on @T@ in the function body. The @forall@ 113 clauses is augmented with a list of polymorphic variables (local type names) 114 and assertions (constraints), which represent the required operations on those 115 types used in a function, \eg: 116 \begin{cfa} 117 forall( T @| { void do_once(T); }@) // assertion 118 void do_twice(T value) { 119 do_once(value); 120 do_once(value); 121 } 122 void do_once(int i) { ... } // provide assertion 123 int i; 124 do_twice(i); // implicitly pass assertion do_once to do_twice 125 \end{cfa} 126 Any object with a type fulfilling the assertion may be passed as an argument to 127 a @do_twice@ call. 128 129 A polymorphic function can be used in the same way as a normal function. The 130 polymorphic variables are filled in with concrete types and the assertions are 131 checked. An assertion is checked by verifying each assertion operation (with 132 all the variables replaced with the concrete types from the arguments) is 133 defined at a call site. 134 135 Note, a function named @do_once@ is not required in the scope of @do_twice@ to 136 compile it, unlike \Cpp template expansion. Furthermore, call-site inferencing 137 allows local replacement of the most specific parametric functions needs for a 138 call. 139 \begin{cfa} 140 void do_once(double y) { ... } // global 88 141 int quadruple(int x) { 89 void do_once(int & y) { 90 y = y * 2; 91 } 92 do_twice(x); 93 return x; 94 } 95 \end{lstlisting} 96 This is not the recommended way to implement a quadruple function but it 97 does work. The complier will deduce that \codeCFA{do_twice}'s T is an 98 integer from the argument. It will then look for a definition matching the 99 assertion which is the \codeCFA{do_once} defined within the function. That 100 function will be passed in as a function pointer to \codeCFA{do_twice} and 101 called within it. 102 103 To avoid typing out long lists of assertions again and again there are also 104 traits which collect assertions into convenent packages that can then be used 105 in assertion lists instead of all of their components. 106 \begin{lstlisting} 107 trait done_once(dtype T) { 108 void do_once(T &); 109 } 110 \end{lstlisting} 111 112 After this the forall list in the previous example could instead be written 113 with the trait instead of the assertion itself. 114 \begin{lstlisting} 115 forall(dtype T | done_once(T)) 116 \end{lstlisting} 117 118 Traits can have arbitrary number of assertions in them and are usually used to 119 create short hands for, and give descriptive names to, commond groupings of 120 assertions. 121 122 Polymorphic structures and unions may also be defined by putting a forall 123 clause before the declaration. The type variables work the same way except 124 are now used in field declaractions instead of parameters and local variables. 125 126 \begin{lstlisting} 142 void do_once(int y) { y = y * 2; } // local 143 do_twice(x); // using local "do_once" 144 return x; 145 } 146 \end{cfa} 147 Specifically, the complier deduces that @do_twice@'s T is an integer from the 148 argument @x@. It then looks for the most specific definition matching the 149 assertion, which is the nested integral @do_once@ defined within the 150 function. The matched assertion function is then passed as a function pointer 151 to @do_twice@ and called within it. 152 153 To avoid typing long lists of assertions, constraints can be collect into 154 convenient packages called a @trait@, which can then be used in an assertion 155 instead of the individual constraints. 156 \begin{cfa} 157 trait done_once(T) { 158 void do_once(T); 159 } 160 \end{cfa} 161 and the @forall@ list in the previous example is replaced with the trait. 162 \begin{cfa} 163 forall(dtype T | @done_once(T)@) 164 \end{cfa} 165 In general, a trait can contain an arbitrary number of assertions, both 166 functions and variables, and are usually used to create a shorthand for, and 167 give descriptive names to, common groupings of assertions describing a certain 168 functionality, like @sumable@, @listable@, \etc. 169 170 Polymorphic structures and unions are defined by qualifying the aggregate type 171 with @forall@. The type variables work the same except they are used in field 172 declarations instead of parameters, returns, and local variable declarations. 173 \begin{cfa} 127 174 forall(dtype T) 128 175 struct node { 129 node(T) * next; 130 T * data; 131 } 132 \end{lstlisting} 133 134 The \codeCFA{node(T)} is a use of a polymorphic structure. Polymorphic types 135 must be provided their polymorphic parameters. 136 137 There are many other features of polymorphism that have not given here but 138 these are the ones used by the exception system. 176 node(T) * next; // generic linked node 177 T * data; 178 } 179 \end{cfa} 180 The generic type @node(T)@ is an example of a polymorphic-type usage. Like \Cpp 181 templates usage, a polymorphic-type usage must specify a type parameter. 182 183 There are many other polymorphism features in \CFA but these are the ones used 184 by the exception system. 139 185 140 186 \section{Concurrency} 141 142 \CFA has a number of concurrency features, \codeCFA{thread}s, 143 \codeCFA{monitor}s and \codeCFA{mutex} parameters, \codeCFA{coroutine}s and 144 \codeCFA{generator}s. The two features that interact with the exception system 145 are \codeCFA{thread}s and \codeCFA{coroutine}s; they and their supporting 146 constructs will be described here. 147 148 \subsection{Coroutines} 149 Coroutines are routines that do not have to finish execution to hand control 150 back to their caller, instead they may suspend their execution at any time 151 and resume it later. 152 Coroutines are not true concurrency but share some similarities and many of 153 the same underpinnings and so are included as part of the \CFA threading 154 library. 155 156 In \CFA coroutines are created using the \codeCFA{coroutine} keyword which 157 works just like \codeCFA{struct} except that the created structure will be 158 modified by the compiler to satify the \codeCFA{is_coroutine} trait. 159 160 These structures act as the interface between callers and the coroutine, 161 the fields are used to pass information in and out. Here is a simple example 162 where the single field is used to pass the next number in a sequence out. 163 \begin{lstlisting} 187 \CFA has a number of concurrency features: @thread@, @monitor@, @mutex@ 188 parameters, @coroutine@ and @generator@. The two features that interact with 189 the exception system are @thread@ and @coroutine@; they and their supporting 190 constructs are described here. 191 192 \subsection{Coroutine} 193 A coroutine is a type with associated functions, where the functions are not 194 required to finish execution when control is handed back to the caller. Instead 195 they may suspend execution at any time and be resumed later at the point of 196 last suspension. (Generators are stackless and coroutines are stackful.) These 197 types are not concurrent but share some similarities along with common 198 underpinnings, so they are combined with the \CFA threading library. Further 199 discussion in this section only refers to the coroutine because generators are 200 similar. 201 202 In \CFA, a coroutine is created using the @coroutine@ keyword, which is an 203 aggregate type like @struct,@ except the structure is implicitly modified by 204 the compiler to satisfy the @is_coroutine@ trait; hence, a coroutine is 205 restricted by the type system to types that provide this special trait. The 206 coroutine structure acts as the interface between callers and the coroutine, 207 and its fields are used to pass information in and out of coroutine interface 208 functions. 209 210 Here is a simple example where a single field is used to pass (communicate) the 211 next number in a sequence. 212 \begin{cfa} 164 213 coroutine CountUp { 165 unsigned int next; 166 } 167 \end{lstlisting} 168 169 The routine part of the coroutine is a main function for the coroutine. It 170 takes a reference to a coroutine object and returns nothing. In this function, 171 and any functions called by this function, the suspend statement may be used 172 to return execution to the coroutine's caller. When control returns to the 173 function it continue from that same suspend statement instead of at the top 174 of the function. 175 \begin{lstlisting} 176 void main(CountUp & this) { 177 unsigned int next = 0; 178 while (true) { 179 this.next = next; 180 suspend; 181 next = next + 1; 182 } 183 } 184 \end{lstlisting} 185 186 Control is passed to the coroutine with the resume function. This includes the 187 first time when the coroutine is starting up. The resume function takes a 188 reference to the coroutine structure and returns the same reference. The 189 return value is for easy access to communication variables. For example the 190 next value from a count-up can be generated and collected in a single 191 expression: \codeCFA{resume(count).next}. 214 unsigned int next; // communication variable 215 } 216 CountUp countup; 217 \end{cfa} 218 Each coroutine has @main@ function, which takes a reference to a coroutine 219 object and returns @void@. 220 \begin{cfa}[numbers=left] 221 void main(@CountUp & this@) { // argument matches trait is_coroutine 222 unsigned int up = 0; // retained between calls 223 while (true) { 224 next = up; // make "up" available outside function 225 @suspend;@$\label{suspend}$ 226 up += 1; 227 } 228 } 229 \end{cfa} 230 In this function, or functions called by this function (helper functions), the 231 @suspend@ statement is used to return execution to the coroutine's caller 232 without terminating the coroutine. 233 234 A coroutine is resumed by calling the @resume@ function, \eg @resume(countup)@. 235 The first resume calls the @main@ function at the top. Thereafter, resume calls 236 continue a coroutine in the last suspended function after the @suspend@ 237 statement, in this case @main@ line~\ref{suspend}. The @resume@ function takes 238 a reference to the coroutine structure and returns the same reference. The 239 return value allows easy access to communication variables defined in the 240 coroutine object. For example, the @next@ value for coroutine object @countup@ 241 is both generated and collected in the single expression: 242 @resume(countup).next@. 192 243 193 244 \subsection{Monitors and Mutex} 194 195 True concurrency does not garrenty ordering. To get some of that ordering back 196 \CFA uses monitors and mutex (mutual exclution) parameters. A monitor is 197 another special declaration that contains a lock and is compatable with mutex 198 parameters. 199 200 Function parameters can have the \codeCFA{mutex} qualifiers on reference 201 arguments, for example \codeCFA{void example(a_monitor & mutex arg);}. When the 202 function is called it will acquire the lock on all of the mutex parameters. 203 204 This means that all functions that mutex on a type are part of a critical 205 section and only one will ever run at a time. 245 Concurrency does not guarantee ordering; without ordering results are 246 non-deterministic. To claw back ordering, \CFA uses monitors and @mutex@ 247 (mutual exclusion) parameters. A monitor is another kind of aggregate, where 248 the compiler implicitly inserts a lock and instances are compatible with 249 @mutex@ parameters. 250 251 A function that requires deterministic (ordered) execution, acquires mutual 252 exclusion on a monitor object by qualifying an object reference parameter with 253 @mutex@. 254 \begin{cfa} 255 void example(MonitorA & @mutex@ argA, MonitorB & @mutex@ argB); 256 \end{cfa} 257 When the function is called, it implicitly acquires the monitor lock for all of 258 the mutex parameters without deadlock. This semantics means all functions with 259 the same mutex type(s) are part of a critical section for objects of that type 260 and only one runs at a time. 206 261 207 262 \subsection{Threads} 208 While coroutines allow new things to be done with a single execution path 209 threads actually introduce new paths of execution that continue independently. 210 Now for threads to work together their must be some communication between them 211 and that means the timing of certain operations does have to be known. There 212 or various means of syncronization and mutual exclution provided by \CFA but 213 for exceptions only the basic two -- fork and join -- are needed. 214 215 Threads are created like coroutines except the keyword is changed: 216 \begin{lstlisting} 263 Functions, generators, and coroutines are sequential so there is only a single 264 (but potentially sophisticated) execution path in a program. Threads introduce 265 multiple execution paths that continue independently. 266 267 For threads to work safely with objects requires mutual exclusion using 268 monitors and mutex parameters. For threads to work safely with other threads, 269 also requires mutual exclusion in the form of a communication rendezvous, which 270 also supports internal synchronization as for mutex objects. For exceptions 271 only the basic two basic operations are important: thread fork and join. 272 273 Threads are created like coroutines with an associated @main@ function: 274 \begin{cfa} 217 275 thread StringWorker { 218 219 276 const char * input; 277 int result; 220 278 }; 221 222 279 void main(StringWorker & this) { 223 const char * localCopy = this.input; 224 // ... do some work, perhaps hashing the string ... 225 this.result = result; 226 } 227 \end{lstlisting} 228 The main function will start executing after the fork operation and continue 229 executing until it is finished. If another thread joins with this one it will 230 wait until main has completed execution. In other words everything the thread 231 does is between fork and join. 232 233 From the outside this is the creation and destruction of the thread object. 234 Fork happens after the constructor is run and join happens before the 235 destructor runs. Join also happens during the \codeCFA{join} function which 236 can be used to join a thread earlier. If it is used the destructor does not 237 join as that has already been completed. 280 const char * localCopy = this.input; 281 // ... do some work, perhaps hashing the string ... 282 this.result = result; 283 } 284 { 285 StringWorker stringworker; // fork thread running in "main" 286 } // implicitly join with thread $\(\Rightarrow\)$ wait for completion 287 \end{cfa} 288 The thread main is where a new thread starts execution after a fork operation 289 and then the thread continues executing until it is finished. If another thread 290 joins with an executing thread, it waits until the executing main completes 291 execution. In other words, everything a thread does is between a fork and join. 292 293 From the outside, this behaviour is accomplished through creation and 294 destruction of a thread object. Implicitly, fork happens after a thread 295 object's constructor is run and join happens before the destructor runs. Join 296 can also be specified explicitly using the @join@ function to wait for a 297 thread's completion independently from its deallocation (\ie destructor 298 call). If @join@ is called explicitly, the destructor does not implicitly join. -
doc/theses/andrew_beach_MMath/features.tex
r342af53 r8e4aa05 1 \chapter{Features} 2 3 This chapter covers the design and user interface of the \CFA exception 4 handling mechanism. 5 6 \section{Virtual Casts} 7 8 Virtual casts and virtual types are not truly part of the exception system but 9 they did not exist in \CFA and are useful in exceptions. So a minimal version 10 of they virtual system was designed and implemented. 11 12 Virtual types are organizied in simple hierarchies. Each virtual type may have 13 a parent and can have any number of children. A type's descendants are its 14 children and its children's descendants. A type may not be its own descendant. 15 16 Each virtual type has an associated virtual table type. A virtual table is a 17 structure that has fields for all the virtual members of a type. A virtual 18 type has all the virtual members of its parent and can add more. It may also 19 update the values of the virtual members. 20 21 Except for virtual casts, this is only used internally in the exception 22 system. There is no general purpose interface for the other features. A 23 a virtual cast has the following syntax: 24 25 \begin{lstlisting} 1 \chapter{Exception Features} 2 3 This chapter covers the design and user interface of the \CFA 4 exception-handling mechanism. 5 6 \section{Virtuals} 7 Virtual types and casts are not part of the exception system nor are they 8 required for an exception system. But an object-oriented style hierarchy is a 9 great way of organizing exceptions so a minimal virtual system has been added 10 to \CFA. 11 12 The pattern of a simple hierarchy was borrowed from object-oriented 13 programming was chosen for several reasons. 14 The first is that it allows new exceptions to be added in user code 15 and in libraries independently of each other. Another is it allows for 16 different levels of exception grouping (all exceptions, all IO exceptions or 17 a particular IO exception). Also it also provides a simple way of passing 18 data back and forth across the throw. 19 20 Virtual types and casts are not required for a basic exception-system but are 21 useful for advanced exception features. However, \CFA is not object-oriented so 22 there is no obvious concept of virtuals. Hence, to create advanced exception 23 features for this work, I needed to design and implement a virtual-like 24 system for \CFA. 25 26 % NOTE: Maybe we should but less of the rational here. 27 Object-oriented languages often organized exceptions into a simple hierarchy, 28 \eg Java. 29 \begin{center} 30 \setlength{\unitlength}{4000sp}% 31 \begin{picture}(1605,612)(2011,-1951) 32 \put(2100,-1411){\vector(1, 0){225}} 33 \put(3450,-1411){\vector(1, 0){225}} 34 \put(3550,-1411){\line(0,-1){225}} 35 \put(3550,-1636){\vector(1, 0){150}} 36 \put(3550,-1636){\line(0,-1){225}} 37 \put(3550,-1861){\vector(1, 0){150}} 38 \put(2025,-1490){\makebox(0,0)[rb]{\LstBasicStyle{exception}}} 39 \put(2400,-1460){\makebox(0,0)[lb]{\LstBasicStyle{arithmetic}}} 40 \put(3750,-1460){\makebox(0,0)[lb]{\LstBasicStyle{underflow}}} 41 \put(3750,-1690){\makebox(0,0)[lb]{\LstBasicStyle{overflow}}} 42 \put(3750,-1920){\makebox(0,0)[lb]{\LstBasicStyle{zerodivide}}} 43 \end{picture}% 44 \end{center} 45 The hierarchy provides the ability to handle an exception at different degrees 46 of specificity (left to right). Hence, it is possible to catch a more general 47 exception-type in higher-level code where the implementation details are 48 unknown, which reduces tight coupling to the lower-level implementation. 49 Otherwise, low-level code changes require higher-level code changes, \eg, 50 changing from raising @underflow@ to @overflow@ at the low level means changing 51 the matching catch at the high level versus catching the general @arithmetic@ 52 exception. In detail, each virtual type may have a parent and can have any 53 number of children. A type's descendants are its children and its children's 54 descendants. A type may not be its own descendant. 55 56 The exception hierarchy allows a handler (@catch@ clause) to match multiple 57 exceptions, \eg a base-type handler catches both base and derived 58 exception-types. 59 \begin{cfa} 60 try { 61 ... 62 } catch(arithmetic &) { 63 ... // handle arithmetic, underflow, overflow, zerodivide 64 } 65 \end{cfa} 66 Most exception mechanisms perform a linear search of the handlers and select 67 the first matching handler, so the order of handers is now important because 68 matching is many to one. 69 70 Each virtual type needs an associated virtual table. A virtual table is a 71 structure with fields for all the virtual members of a type. A virtual type has 72 all the virtual members of its parent and can add more. It may also update the 73 values of the virtual members and often does. 74 75 While much of the virtual infrastructure is created, it is currently only used 76 internally for exception handling. The only user-level feature is the virtual 77 cast, which is the same as the \Cpp \lstinline[language=C++]|dynamic_cast|. 78 \label{p:VirtualCast} 79 \begin{cfa} 26 80 (virtual TYPE)EXPRESSION 27 \end{lstlisting} 28 29 This has the same precidence as a traditional C-cast and can be used in the 30 same places. This will convert the result of EXPRESSION to the type TYPE. Both 31 the type of EXPRESSION and TYPE must be pointers to virtual types. 32 33 The cast is checked and will either return the original value or null, based 34 on the result of the check. The check is does the object pointed at have a 35 type that is a descendant of the target type. If it is the result is the 36 pointer, otherwise the result is null. 37 38 \section{Exceptions} 81 \end{cfa} 82 Note, the syntax and semantics matches a C-cast, rather than the function-like 83 \Cpp syntax for special casts. Both the type of @EXPRESSION@ and @TYPE@ must be 84 a pointer to a virtual type. 85 The cast dynamically checks if the @EXPRESSION@ type is the same or a subtype 86 of @TYPE@, and if true, returns a pointer to the 87 @EXPRESSION@ object, otherwise it returns @0p@ (null pointer). 88 89 \section{Exception} 39 90 % Leaving until later, hopefully it can talk about actual syntax instead 40 91 % of my many strange macros. Syntax aside I will also have to talk about the 41 92 % features all exceptions support. 42 93 43 \section{Termination} 44 45 Termination exception throws are likely the most framilar kind, as they are 46 used in several popular programming languages. A termination will throw an 47 exception, search the stack for a handler, unwind the stack to where the 48 handler is defined, execute the handler and then continue execution after 49 the handler. They are used when execution cannot continue here. 50 51 Termination has two pieces of syntax it uses. The first is the throw: 52 \begin{lstlisting} 94 Exceptions are defined by the trait system; there are a series of traits, and 95 if a type satisfies them, then it can be used as an exception. The following 96 is the base trait all exceptions need to match. 97 \begin{cfa} 98 trait is_exception(exceptT &, virtualT &) { 99 virtualT const & get_exception_vtable(exceptT *); 100 }; 101 \end{cfa} 102 The trait is defined over two types, the exception type and the virtual table 103 type. This should be one-to-one, each exception type has only one virtual 104 table type and vice versa. The only assertion in the trait is 105 @get_exception_vtable@, which takes a pointer of the exception type and 106 returns a reference to the virtual table type instance. 107 108 The function @get_exception_vtable@ is actually a constant function. 109 Recardless of the value passed in (including the null pointer) it should 110 return a reference to the virtual table instance for that type. 111 The reason it is a function instead of a constant is that it make type 112 annotations easier to write as you can use the exception type instead of the 113 virtual table type; which usually has a mangled name. 114 % Also \CFA's trait system handles functions better than constants and doing 115 % it this way reduce the amount of boiler plate we need. 116 117 % I did have a note about how it is the programmer's responsibility to make 118 % sure the function is implemented correctly. But this is true of every 119 % similar system I know of (except Agda's I guess) so I took it out. 120 121 There are two more traits for exceptions @is_termination_exception@ and 122 @is_resumption_exception@. They are defined as follows: 123 124 \begin{cfa} 125 trait is_termination_exception( 126 exceptT &, virtualT & | is_exception(exceptT, virtualT)) { 127 void defaultTerminationHandler(exceptT &); 128 }; 129 130 trait is_resumption_exception( 131 exceptT &, virtualT & | is_exception(exceptT, virtualT)) { 132 void defaultResumptionHandler(exceptT &); 133 }; 134 \end{cfa} 135 136 In other words they make sure that a given type and virtual type is an 137 exception and defines one of the two default handlers. These default handlers 138 are used in the main exception handling operations \see{Exception Handling} 139 and their use will be detailed there. 140 141 However all three of these traits can be trickly to use directly. 142 There is a bit of repetition required but 143 the largest issue is that the virtual table type is mangled and not in a user 144 facing way. So there are three macros that can be used to wrap these traits 145 when you need to refer to the names: 146 @IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@ and @IS_RESUMPTION_EXCEPTION@. 147 148 All take one or two arguments. The first argument is the name of the 149 exception type. Its unmangled and mangled form are passed to the trait. 150 The second (optional) argument is a parenthesized list of polymorphic 151 arguments. This argument should only with polymorphic exceptions and the 152 list will be passed to both types. 153 In the current set-up the base name and the polymorphic arguments have to 154 match so these macros can be used without losing flexability. 155 156 For example consider a function that is polymorphic over types that have a 157 defined arithmetic exception: 158 \begin{cfa} 159 forall(Num | IS_EXCEPTION(Arithmetic, (Num))) 160 void some_math_function(Num & left, Num & right); 161 \end{cfa} 162 163 \section{Exception Handling} 164 \CFA provides two kinds of exception handling, termination and resumption. 165 These twin operations are the core of the exception handling mechanism and 166 are the reason for the features of exceptions. 167 This section will cover the general patterns shared by the two operations and 168 then go on to cover the details each individual operation. 169 170 Both operations follow the same set of steps to do their operation. They both 171 start with the user preforming a throw on an exception. 172 Then there is the search for a handler, if one is found than the exception 173 is caught and the handler is run. After that control returns to normal 174 execution. 175 176 If the search fails a default handler is run and then control 177 returns to normal execution immediately. That is where the default handlers 178 @defaultTermiationHandler@ and @defaultResumptionHandler@ are used. 179 180 \subsection{Termination} 181 \label{s:Termination} 182 183 Termination handling is more familiar kind and used in most programming 184 languages with exception handling. 185 It is dynamic, non-local goto. If a throw is successful then the stack will 186 be unwound and control will (usually) continue in a different function on 187 the call stack. They are commonly used when an error has occured and recovery 188 is impossible in the current function. 189 190 % (usually) Control can continue in the current function but then a different 191 % control flow construct should be used. 192 193 A termination throw is started with the @throw@ statement: 194 \begin{cfa} 53 195 throw EXPRESSION; 54 \end{lstlisting} 55 56 The expression must evaluate to a reference to a termination exception. A 57 termination exception is any exception with a 58 \codeCFA{void defaultTerminationHandler(T &);} (the default handler) defined 59 on it. The handler is taken from the call sight with \CFA's trait system and 60 passed into the exception system along with the exception itself. 61 62 The exception passed into the system is then copied into managed memory. 63 This is to ensure it remains in scope during unwinding. It is the user's 64 responsibility to make sure the original exception is freed when it goes out 65 of scope. Being allocated on the stack is sufficient for this. 66 67 Then the exception system will search the stack starting from the throw and 68 proceding towards the base of the stack, from callee to caller. As it goes 69 it will check any termination handlers it finds: 70 71 \begin{lstlisting} 72 try { 73 TRY_BLOCK 74 } catch (EXCEPTION_TYPE * NAME) { 75 HANDLER 76 } 77 \end{lstlisting} 78 79 This shows a try statement with a single termination handler. The statements 80 in TRY\_BLOCK will be executed when control reaches this statement. While 81 those statements are being executed if a termination exception is thrown and 82 it is not handled by a try statement further up the stack the EHM will check 83 all of the terminations handlers attached to the try block, top to bottom. 84 85 At each handler the EHM will check to see if the thrown exception is a 86 descendant of EXCEPTION\_TYPE. If it is the pointer to the exception is 87 bound to NAME and the statements in HANDLER are executed. If control reaches 88 the end of the handler then it exits the block, the exception is freed and 89 control continues after the try statement. 90 91 The default handler is only used if no handler for the exception is found 92 after the entire stack is searched. When that happens the default handler 93 is called with a reference to the exception as its only argument. If the 94 handler returns control continues from after the throw statement. 95 96 \paragraph{Conditional Catches} 97 98 Catch clauses may also be written as: 99 \begin{lstlisting} 196 \end{cfa} 197 The expression must return a reference to a termination exception, where the 198 termination exception is any type that satifies @is_termination_exception@ 199 at the call site. 200 Through \CFA's trait system the functions in the traits are passed into the 201 throw code. A new @defaultTerminationHandler@ can be defined in any scope to 202 change the throw's behavior (see below). 203 204 The throw will copy the provided exception into managed memory. It is the 205 user's responcibility to ensure the original exception is cleaned up if the 206 stack is unwound (allocating it on the stack should be sufficient). 207 208 Then the exception system searches the stack using the copied exception. 209 It starts starts from the throw and proceeds to the base of the stack, 210 from callee to caller. 211 At each stack frame, a check is made for resumption handlers defined by the 212 @catch@ clauses of a @try@ statement. 213 \begin{cfa} 214 try { 215 GUARDED_BLOCK 216 } catch (EXCEPTION_TYPE$\(_1\)$ * NAME$\(_1\)$) { 217 HANDLER_BLOCK$\(_1\)$ 218 } catch (EXCEPTION_TYPE$\(_2\)$ * NAME$\(_2\)$) { 219 HANDLER_BLOCK$\(_2\)$ 220 } 221 \end{cfa} 222 When viewed on its own a try statement will simply exceute the statements in 223 @GUARDED_BLOCK@ and when those are finished the try statement finishes. 224 225 However, while the guarded statements are being executed, including any 226 functions they invoke, all the handlers following the try block are now 227 or any functions invoked from those 228 statements, throws an exception, and the exception 229 is not handled by a try statement further up the stack, the termination 230 handlers are searched for a matching exception type from top to bottom. 231 232 Exception matching checks the representation of the thrown exception-type is 233 the same or a descendant type of the exception types in the handler clauses. If 234 it is the same of a descendent of @EXCEPTION_TYPE@$_i$ then @NAME@$_i$ is 235 bound to a pointer to the exception and the statements in @HANDLER_BLOCK@$_i$ 236 are executed. If control reaches the end of the handler, the exception is 237 freed and control continues after the try statement. 238 239 If no handler is found during the search then the default handler is run. 240 Through \CFA's trait system the best match at the throw sight will be used. 241 This function is run and is passed the copied exception. After the default 242 handler is run control continues after the throw statement. 243 244 There is a global @defaultTerminationHandler@ that cancels the current stack 245 with the copied exception. However it is generic over all exception types so 246 new default handlers can be defined for different exception types and so 247 different exception types can have different default handlers. 248 249 \subsection{Resumption} 250 \label{s:Resumption} 251 252 Resumption exception handling is a less common form than termination but is 253 just as old~\cite{Goodenough75} and is in some sense simpler. 254 It is a dynamic, non-local function call. If the throw is successful a 255 closure will be taken from up the stack and executed, after which the throwing 256 function will continue executing. 257 These are most often used when an error occured and if the error is repaired 258 then the function can continue. 259 260 A resumption raise is started with the @throwResume@ statement: 261 \begin{cfa} 262 throwResume EXPRESSION; 263 \end{cfa} 264 The semantics of the @throwResume@ statement are like the @throw@, but the 265 expression has return a reference a type that satifies the trait 266 @is_resumption_exception@. The assertions from this trait are available to 267 the exception system while handling the exception. 268 269 At runtime, no copies are made. As the stack is not unwound the exception and 270 any values on the stack will remain in scope while the resumption is handled. 271 272 Then the exception system searches the stack using the provided exception. 273 It starts starts from the throw and proceeds to the base of the stack, 274 from callee to caller. 275 At each stack frame, a check is made for resumption handlers defined by the 276 @catchResume@ clauses of a @try@ statement. 277 \begin{cfa} 278 try { 279 GUARDED_BLOCK 280 } catchResume (EXCEPTION_TYPE$\(_1\)$ * NAME$\(_1\)$) { 281 HANDLER_BLOCK$\(_1\)$ 282 } catchResume (EXCEPTION_TYPE$\(_2\)$ * NAME$\(_2\)$) { 283 HANDLER_BLOCK$\(_2\)$ 284 } 285 \end{cfa} 286 If the handlers are not involved in a search this will simply execute the 287 @GUARDED_BLOCK@ and then continue to the next statement. 288 Its purpose is to add handlers onto the stack. 289 (Note, termination and resumption handlers may be intermixed in a @try@ 290 statement but the kind of throw must be the same as the handler for it to be 291 considered as a possible match.) 292 293 If a search for a resumption handler reaches a try block it will check each 294 @catchResume@ clause, top-to-bottom. 295 At each handler if the thrown exception is or is a child type of 296 @EXCEPTION_TYPE@$_i$ then the a pointer to the exception is bound to 297 @NAME@$_i$ and then @HANDLER_BLOCK@$_i$ is executed. After the block is 298 finished control will return to the @throwResume@ statement. 299 300 Like termination, if no resumption handler is found, the default handler 301 visible at the throw statement is called. It will use the best match at the 302 call sight according to \CFA's overloading rules. The default handler is 303 passed the exception given to the throw. When the default handler finishes 304 execution continues after the throw statement. 305 306 There is a global @defaultResumptionHandler@ is polymorphic over all 307 termination exceptions and preforms a termination throw on the exception. 308 The @defaultTerminationHandler@ for that throw is matched at the original 309 throw statement (the resumption @throwResume@) and it can be customized by 310 introducing a new or better match as well. 311 312 % \subsubsection? 313 314 A key difference between resumption and termination is that resumption does 315 not unwind the stack. A side effect that is that when a handler is matched 316 and run it's try block (the guarded statements) and every try statement 317 searched before it are still on the stack. This can lead to the recursive 318 resumption problem. 319 320 The recursive resumption problem is any situation where a resumption handler 321 ends up being called while it is running. 322 Consider a trivial case: 323 \begin{cfa} 324 try { 325 throwResume (E &){}; 326 } catchResume(E *) { 327 throwResume (E &){}; 328 } 329 \end{cfa} 330 When this code is executed the guarded @throwResume@ will throw, start a 331 search and match the handler in the @catchResume@ clause. This will be 332 call and placed on the stack on top of the try-block. The second throw then 333 throws and will seach the same try block and put call another instance of the 334 same handler leading to an infinite loop. 335 336 This situation is trivial and easy to avoid, but much more complex cycles 337 can form with multiple handlers and different exception types. 338 339 To prevent all of these cases we mask sections of the stack, or equvilantly 340 the try statements on the stack, so that the resumption seach skips over 341 them and continues with the next unmasked section of the stack. 342 343 A section of the stack is marked when it is searched to see if it contains 344 a handler for an exception and unmarked when that exception has been handled 345 or the search was completed without finding a handler. 346 347 % This might need a diagram. But it is an important part of the justification 348 % of the design of the traversal order. 349 \begin{verbatim} 350 throwResume2 ----------. 351 | | 352 generated from handler | 353 | | 354 handler | 355 | | 356 throwResume1 -----. : 357 | | : 358 try | : search skip 359 | | : 360 catchResume <----' : 361 | | 362 \end{verbatim} 363 364 The rules can be remembered as thinking about what would be searched in 365 termination. So when a throw happens in a handler; a termination handler 366 skips everything from the original throw to the original catch because that 367 part of the stack has been unwound, a resumption handler skips the same 368 section of stack because it has been masked. 369 A throw in a default handler will preform the same search as the original 370 throw because; for termination nothing has been unwound, for resumption 371 the mask will be the same. 372 373 The symmetry with termination is why this pattern was picked. Other patterns, 374 such as marking just the handlers that caught, also work but lack the 375 symmetry whih means there is more to remember. 376 377 \section{Conditional Catch} 378 Both termination and resumption handler clauses can be given an additional 379 condition to further control which exceptions they handle: 380 \begin{cfa} 100 381 catch (EXCEPTION_TYPE * NAME ; CONDITION) 101 \end{lstlisting} 102 This has the same behaviour as a regular catch clause except that if the 103 exception matches the given type the condition is also run. If the result is 104 true only then is this considered a matching handler. If the result is false 105 then the handler does not match and the search continues with the next clause 106 in the try block. 107 108 The condition considers all names in scope at the beginning of the try block 109 to be in scope along with the name introduce in the catch clause itself. 110 111 \paragraph{Re-Throwing} 112 113 You can also rethrow the most recent termination exception with 114 \codeCFA{throw;}. % This is terrible and you should never do it. 115 This can be done in a handler or any function that could be called from a 116 handler. 117 118 This will start another termination throw reusing the exception, meaning it 119 does not copy the exception or allocated any more memory for it. However the 120 default handler is still at the original through and could refer to data that 121 was on the unwound section of the stack. So instead a new default handler that 122 does a program level abort is used. 123 124 \section{Resumption} 125 126 Resumption exceptions are less popular then termination but in many 127 regards are simpler and easier to understand. A resumption throws an exception, 128 searches for a handler on the stack, executes that handler on top of the stack 129 and then continues execution from the throw. These are used when a problem 130 needs to be fixed before execution continues. 131 132 A resumption is thrown with a throw resume statement: 133 \begin{lstlisting} 134 throwResume EXPRESSION; 135 \end{lstlisting} 136 The result of EXPRESSION must be a resumption exception type. A resumption 137 exception type is any type that satifies the assertion 138 \codeCFA{void defaultResumptionHandler(T &);} (the default handler). When the 139 statement is executed the expression is evaluated and the result is thrown. 140 141 Handlers are declared using clauses in try statements: 142 \begin{lstlisting} 143 try { 144 TRY_BLOCK 145 } catchResume (EXCEPTION_TYPE * NAME) { 146 HANDLER 147 } 148 \end{lstlisting} 149 This is a simple example with the try block and a single resumption handler. 150 Multiple resumption handlers can be put in a try statement and they can be 151 mixed with termination handlers. 152 153 When a resumption begins it will start searching the stack starting from 154 the throw statement and working its way to the callers. In each try statement 155 handlers will be tried top to bottom. Each handler is checked by seeing if 156 the thrown exception is a descendant of EXCEPTION\_TYPE. If not the search 157 continues. Otherwise NAME is bound to a pointer to the exception and the 158 HANDLER statements are executed. After they are finished executing control 159 continues from the throw statement. 160 161 If no approprate handler is found then the default handler is called. The 162 throw statement acts as a regular function call passing the exception to 163 the default handler and after the handler finishes executing control continues 164 from the throw statement. 165 166 The exception system also tracks the position of a search on the stack. If 167 another resumption exception is thrown while a resumption handler is running 168 it will first check handlers pushed to the stack by the handler and any 169 functions it called, then it will continue from the try statement that the 170 handler is a part of; except for the default handler where it continues from 171 the throw the default handler was passed to. 172 173 This makes the search pattern for resumption reflect the one for termination, 174 which is what most users expect. 175 176 % This might need a diagram. But it is an important part of the justifaction 177 % of the design of the traversal order. 178 It also avoids the recursive resumption problem. If the entire stack is 179 searched loops of resumption can form. Consider a handler that handles an 180 exception of type A by resuming an exception of type B and on the same stack, 181 later in the search path, is a second handler that handles B by resuming A. 182 183 Assuming no other handlers on the stack handle A or B then in either traversal 184 system an A resumed from the top of the stack will be handled by the first 185 handler. A B resumed from the top or from the first handler it will be handled 186 by the second hander. The only difference is when A is thrown from the second 187 handler. The entire stack search will call the first handler again, creating a 188 loop. Starting from the position in the stack though will break this loop. 189 190 \paragraph{Conditional Catches} 191 192 Resumption supports conditional catch clauses like termination does. They 193 use the same syntax except the keyword is changed: 194 \begin{lstlisting} 195 catchResume (EXCEPTION_TYPE * NAME ; CONDITION) 196 \end{lstlisting} 197 198 It also has the same behaviour, after the exception type has been matched 199 with the EXCEPTION\_TYPE the CONDITION is evaluated with NAME in scope. If 200 the result is true then the hander is run, otherwise the search continues 201 just as if there had been a type mismatch. 202 203 \paragraph{Re-Throwing} 204 205 You may also re-throw resumptions with a \codeCFA{throwResume;} statement. 206 This can only be done from inside of a \codeCFA{catchResume} block. 207 208 Outside of any side effects of any code already run in the handler this will 209 have the same effect as if the exception had not been caught in the first 210 place. 382 \end{cfa} 383 First, the same semantics is used to match the exception type. Second, if the 384 exception matches, @CONDITION@ is executed. The condition expression may 385 reference all names in scope at the beginning of the try block and @NAME@ 386 introduced in the handler clause. If the condition is true, then the handler 387 matches. Otherwise, the exception search continues as if the exception type 388 did not match. 389 \begin{cfa} 390 try { 391 f1 = open( ... ); 392 f2 = open( ... ); 393 ... 394 } catch( IOFailure * f ; fd( f ) == f1 ) { 395 // only handle IO failure for f1 396 } 397 \end{cfa} 398 Note, catching @IOFailure@, checking for @f1@ in the handler, and reraising the 399 exception if not @f1@ is different because the reraise does not examine any of 400 remaining handlers in the current try statement. 401 402 \section{Rethrowing} 403 \colour{red}{From Andrew: I recomend we talk about why the language doesn't 404 have rethrows/reraises instead.} 405 406 \label{s:Rethrowing} 407 Within the handler block or functions called from the handler block, it is 408 possible to reraise the most recently caught exception with @throw@ or 409 @throwResume@, respectively. 410 \begin{cfa} 411 try { 412 ... 413 } catch( ... ) { 414 ... throw; 415 } catchResume( ... ) { 416 ... throwResume; 417 } 418 \end{cfa} 419 The only difference between a raise and a reraise is that reraise does not 420 create a new exception; instead it continues using the current exception, \ie 421 no allocation and copy. However the default handler is still set to the one 422 visible at the raise point, and hence, for termination could refer to data that 423 is part of an unwound stack frame. To prevent this problem, a new default 424 handler is generated that does a program-level abort. 211 425 212 426 \section{Finally Clauses} 213 214 A \codeCFA{finally} clause may be placed at the end of a try statement after 215 all the handler clauses. In the simply case, with no handlers, it looks like 216 this: 217 218 \begin{lstlisting} 219 try { 220 TRY_BLOCK 221 } finally { 222 FINAL_STATEMENTS 223 } 224 \end{lstlisting} 225 226 Any number of termination handlers and resumption handlers may proceed the 227 finally clause. 228 229 The FINAL\_STATEMENTS, the finally block, are executed whenever the try 230 statement is removed from the stack. This includes: the TRY\_BLOCK finishes 231 executing, a termination exception finishes executing and the stack unwinds. 232 233 Execution of the finally block should finish by letting control run off 234 the end of the block. This is because after the finally block is complete 235 control will continue to where ever it would if the finally clause was not 236 present. 237 238 Because of this local control flow out of the finally block is forbidden. 239 The compiler rejects uses of \codeCFA{break}, \codeCFA{continue}, 240 \codeCFA{fallthru} and \codeCFA{return} that would cause control to leave 241 the finally block. Other ways to leave the finally block - such as a long 242 jump or termination - are much harder to check, at best requiring additional 243 runtime overhead, and so are merely discouraged. 427 Finally clauses are used to preform unconditional clean-up when leaving a 428 scope. They are placed at the end of a try statement: 429 \begin{cfa} 430 try { 431 GUARDED_BLOCK 432 } ... // any number or kind of handler clauses 433 ... finally { 434 FINALLY_BLOCK 435 } 436 \end{cfa} 437 The @FINALLY_BLOCK@ is executed when the try statement is removed from the 438 stack, including when the @GUARDED_BLOCK@ finishes, any termination handler 439 finishes or during an unwind. 440 The only time the block is not executed is if the program is exited before 441 the stack is unwound. 442 443 Execution of the finally block should always finish, meaning control runs off 444 the end of the block. This requirement ensures always continues as if the 445 finally clause is not present, \ie finally is for cleanup not changing control 446 flow. Because of this requirement, local control flow out of the finally block 447 is forbidden. The compiler precludes any @break@, @continue@, @fallthru@ or 448 @return@ that causes control to leave the finally block. Other ways to leave 449 the finally block, such as a long jump or termination are much harder to check, 450 and at best requiring additional run-time overhead, and so are mearly 451 discouraged. 452 453 Not all languages with exceptions have finally clauses. Notably \Cpp does 454 without it as descructors serve a similar role. Although destructors and 455 finally clauses can be used in many of the same areas they have their own 456 use cases like top-level functions and lambda functions with closures. 457 Destructors take a bit more work to set up but are much easier to reuse while 458 finally clauses are good for once offs and can include local information. 244 459 245 460 \section{Cancellation} 246 247 Cancellation can be thought of as a stack-level abort or as an uncatchable 248 termination. It unwinds the entirety of the current exception and if possible 249 passes an exception to a different stack as a message. 250 251 There is no special statement for starting a cancellation, instead you call 252 the standard libary function \codeCFA{cancel\_stack} which takes an exception. 253 Unlike in a throw this exception is not used in control flow but is just there 254 to pass information about why the cancellation happened. 255 256 The handler is decided entirely by which stack is being cancelled. There are 257 three handlers that apply to three different groups of stacks: 258 \begin{itemize} 259 \item Main Stack: 260 The main stack is the one on which the program main is called at the beginning 261 of your program. It is also the only stack you have without the libcfathreads. 262 263 Because of this there is no other stack ``above" (or possibly at all) for main 264 to notify when a cancellation occurs. So after the stack is unwound we do a 265 program level abort. 266 267 \item Thread Stack: 268 Thread stacks are those created \codeCFA{thread} or otherwise satify the 269 \codeCFA{is\_thread} trait. 270 271 Threads only have two structural points of communication that must happen, 272 start and join. As the thread must be running to preform a cancellation it 273 will be after start and before join, so join is one cancellation uses. 274 275 After the stack is unwound the thread will halt as if had completed normally 276 and wait for another thread to join with it. The other thread, when it joins, 277 checks for a cancellation. If so it will throw the resumption exception 278 \codeCFA{ThreadCancelled}. 279 280 There is a difference here in how explicate joins (with the \codeCFA{join} 281 function) and implicate joins (from a destructor call). Explicate joins will 282 take the default handler (\codeCFA{defaultResumptionHandler}) from the context 283 and use like a regular through does if the exception is not caught. The 284 implicate join does a program abort instead. 285 286 This is for safety. One of the big problems in exceptions is you cannot handle 287 two terminations or cancellations on the same stack as either can destroy the 288 context required for the other. This can happen with join but as the 289 destructors will always be run when the stack is being unwound and one 290 termination/cancellation is already active. Also since they are implicite they 291 are easier to forget about. 292 293 \item Coroutine Stack: 294 Coroutine stacks are those created with \codeCFA{coroutine} or otherwise 295 satify the \codeCFA{is\_coroutine} trait. 296 297 A coroutine knows of two other coroutines, its starter and its last resumer. 298 The last resumer is ``closer" so that is the one notified. 299 300 After the stack is unwound control goes to the last resumer. 301 Resume will resume throw a \codeCFA{CoroutineCancelled} exception, which is 302 polymorphic over the coroutine type and has a pointer to the coroutine being 303 cancelled and the cancelling exception. The resume function also has an 304 assertion that the \codeCFA{defaultResumptionHandler} for the exception. So it 305 will use the default handler like a regular throw. 306 307 \end{itemize} 461 Cancellation is a stack-level abort, which can be thought of as as an 462 uncatchable termination. It unwinds the entirety of the current stack, and if 463 possible forwards the cancellation exception to a different stack. 464 465 Cancellation is not an exception operation like termination or resumption. 466 There is no special statement for starting a cancellation; instead the standard 467 library function @cancel_stack@ is called passing an exception. Unlike a 468 throw, this exception is not used in matching only to pass information about 469 the cause of the cancellation. 470 (This also means matching cannot fail so there is no default handler either.) 471 472 After @cancel_stack@ is called the exception is copied into the exception 473 handling mechanism's memory. Then the entirety of the current stack is 474 unwound. After that it depends one which stack is being cancelled. 475 \begin{description} 476 \item[Main Stack:] 477 The main stack is the one used by the program main at the start of execution, 478 and is the only stack in a sequential program. Even in a concurrent program 479 the main stack is only dependent on the environment that started the program. 480 Hence, when the main stack is cancelled there is nowhere else in the program 481 to notify. After the stack is unwound, there is a program-level abort. 482 483 \item[Thread Stack:] 484 A thread stack is created for a @thread@ object or object that satisfies the 485 @is_thread@ trait. A thread only has two points of communication that must 486 happen: start and join. As the thread must be running to perform a 487 cancellation, it must occur after start and before join, so join is used 488 for communication here. 489 After the stack is unwound, the thread halts and waits for 490 another thread to join with it. The joining thread checks for a cancellation, 491 and if present, resumes exception @ThreadCancelled@. 492 493 There is a subtle difference between the explicit join (@join@ function) and 494 implicit join (from a destructor call). The explicit join takes the default 495 handler (@defaultResumptionHandler@) from its calling context, which is used if 496 the exception is not caught. The implicit join does a program abort instead. 497 498 This semantics is for safety. If an unwind is triggered while another unwind 499 is underway only one of them can proceed as they both want to ``consume'' the 500 stack. Letting both try to proceed leads to very undefined behaviour. 501 Both termination and cancellation involve unwinding and, since the default 502 @defaultResumptionHandler@ preforms a termination that could more easily 503 happen in an implicate join inside a destructor. So there is an error message 504 and an abort instead. 505 \todo{Perhaps have a more general disucssion of unwind collisions before 506 this point.} 507 508 The recommended way to avoid the abort is to handle the intial resumption 509 from the implicate join. If required you may put an explicate join inside a 510 finally clause to disable the check and use the local 511 @defaultResumptionHandler@ instead. 512 513 \item[Coroutine Stack:] A coroutine stack is created for a @coroutine@ object 514 or object that satisfies the @is_coroutine@ trait. A coroutine only knows of 515 two other coroutines, its starter and its last resumer. Of the two the last 516 resumer has the tightest coupling to the coroutine it activated and the most 517 up-to-date information. 518 519 Hence, cancellation of the active coroutine is forwarded to the last resumer 520 after the stack is unwound. When the resumer restarts, it resumes exception 521 @CoroutineCancelled@, which is polymorphic over the coroutine type and has a 522 pointer to the cancelled coroutine. 523 524 The resume function also has an assertion that the @defaultResumptionHandler@ 525 for the exception. So it will use the default handler like a regular throw. 526 \end{description} -
doc/theses/andrew_beach_MMath/future.tex
r342af53 r8e4aa05 1 1 \chapter{Future Work} 2 2 3 \section{Language Improvements} 4 \CFA is a developing programming language. As such, there are partially or 5 unimplemented features of the language (including several broken components) 6 that I had to workaround while building an exception handling system largely in 7 the \CFA language (some C components). The following are a few of these 8 issues, and once implemented/fixed, how this would affect the exception system. 9 \begin{itemize} 10 \item 11 The implementation of termination is not portable because it includes 12 hand-crafted assembly statements. These sections must be ported by hand to 13 support more hardware architectures, such as the ARM processor. 14 \item 15 Due to a type-system problem, the catch clause cannot bind the exception to a 16 reference instead of a pointer. Since \CFA has a very general reference 17 capability, programmers will want to use it. Once fixed, this capability should 18 result in little or no change in the exception system. 19 \item 20 Termination handlers cannot use local control-flow transfers, \eg by @break@, 21 @return@, \etc. The reason is that current code generation hoists a handler 22 into a nested function for convenience (versus assemble-code generation at the 23 @try@ statement). Hence, when the handler runs, its code is not in the lexical 24 scope of the @try@ statement, where the local control-flow transfers are 25 meaningful. 26 \item 27 There is no detection of colliding unwinds. It is possible for clean-up code 28 run during an unwind to trigger another unwind that escapes the clean-up code 29 itself; such as a termination exception caught further down the stack or a 30 cancellation. There do exist ways to handle this but currently they are not 31 even detected and the first unwind will simply be forgotten, often leaving 32 it in a bad state. 33 \item 34 Also the exception system did not have a lot of time to be tried and tested. 35 So just letting people use the exception system more will reveal new 36 quality of life upgrades that can be made with time. 37 \end{itemize} 38 3 39 \section{Complete Virtual System} 4 The virtual system should be completed. It was n ever supposed to be part of5 this project and so minimal work was done on it. A draft of what the complete 6 system might look like was created but it was never finalized or implemented. 7 A future project in \CFA would be to complete that work and to update the 8 parts of the exception system that usethe current version.40 The virtual system should be completed. It was not supposed to be part of this 41 project, but was thrust upon it to do exception inheritance; hence, only 42 minimal work was done. A draft for a complete virtual system is available but 43 it is not finalized. A future \CFA project is to complete that work and then 44 update the exception system that uses the current version. 9 45 10 For instance a full virtual system would probably allow for several 11 improvements to the exception traits. Although they do currently work they 12 could be made easier to use by making the virtual table type implitate in the 13 trait (which would remove the need for those wrapper marcos) or allowing 14 for assertions that give the layout of a virtual table for safety. 46 There are several improvements to the virtual system that would improve the 47 exception traits. The most important one is an assertion to check one virtual 48 type is a child of another. This check precisely captures many of the 49 correctness requirements. 15 50 16 \section{Additional Throws} 17 Several other kinds of throws, beyond the termination throw (\codeCFA{throw}), 18 the resumption throw (\codeCFA{throwResume}) and the re-throws, were considered. 19 None were as useful as the core throws but they would likely be worth 20 revising. 51 The full virtual system might also include other improvement like associated 52 types to allow traits to refer to types not listed in their header. This 53 feature allows exception traits to not refer to the virtual-table type 54 explicitly, removing the need for the current interface macros. 21 55 22 The first ones are throws for asynchronous exceptions, throwing exceptions 23 from one stack to another. These act like signals allowing for communication 24 between the stacks. This is usually used with resumption as it allows the 25 target stack to continue execution normally after the exception has been 26 handled. 56 \section{Additional Raises} 57 Several other kinds of exception raises were considered beyond termination 58 (@throw@), resumption (@throwResume@), and reraise. 27 59 28 This would much more coordination between the concurrency system and the 29 exception system to handle. Most of the interesting design decisions around 30 applying asynchronous exceptions appear to be around masking (controlling 31 which exceptions may be thrown at a stack). It would likely require more of 32 the virtual system and would also effect how default handlers are set. 60 The first is a non-local/concurrent raise providing asynchronous exceptions, 61 \ie raising an exception on another stack. This semantics acts like signals 62 allowing for out-of-band communication among coroutines and threads. This kind 63 of raise is often restricted to resumption to allow the target stack to 64 continue execution normally after the exception has been handled. That is, 65 allowing one coroutine/thread to unwind the stack of another via termination is 66 bad software engineering. 33 67 34 The other throws were designed to mimic bidirectional algebraic effects. 35 Algebraic effects are used in some functional languages and allow a function 68 Non-local/concurrent requires more coordination between the concurrency system 69 and the exception system. Many of the interesting design decisions centre 70 around masking (controlling which exceptions may be thrown at a stack). It 71 would likely require more of the virtual system and would also effect how 72 default handlers are set. 73 74 Other raises were considered to mimic bidirectional algebraic effects. 75 Algebraic effects are used in some functional languages allowing one function 36 76 to have another function on the stack resolve an effect (which is defined with 37 a function-like interface). 38 These can be mimiced with resumptions and the the new throws were designed 39 to try and mimic bidirectional algebraic effects, where control can go back 40 and forth between the function effect caller and handler while the effect 41 is underway. 77 a functional-like interface). This semantics can be mimicked with resumptions 78 and new raises were discussed to mimic bidirectional algebraic-effects, where 79 control can go back and forth between the function-effect caller and handler 80 while the effect is underway. 42 81 % resume-top & resume-reply 82 These raises would be like the resumption raise except using different search 83 patterns to find the handler. 43 84 44 These throws would likely be just like the resumption throw except they would 45 use different search patterns to find the handler to reply to. 85 \section{Zero-Cost Try} 86 \CFA does not have zero-cost try-statements because the compiler generates C 87 code rather than assembler code \see{\VPageref{p:zero-cost}}. When the compiler 88 does create its own assembly (or LLVM byte-code), then zero-cost try-statements 89 are possible. The downside of zero-cost try-statements is the LSDA complexity, 90 its size (program bloat), and the high cost of raising an exception. 46 91 47 \section{Zero-Cost Exceptions} 48 \CFA does not have zero-cost exceptions because it does not generate assembly 49 but instead generates C code. See the implementation section. When the 50 compiler does start to create its own assembly (or LLVM byte code) then 51 zero-cost exceptions could be implemented. 92 Alternatively, some research could be done into the simpler alternative method 93 with a non-zero-cost try-statement but much lower cost exception raise. For 94 example, programs are starting to use exception in the normal control path, so 95 more exceptions are thrown. In these cases, the cost balance switches towards 96 low-cost raise. Unfortunately, while exceptions remain exceptional, the 97 libunwind model will probably remain the most effective option. 52 98 53 Now in zero-cost exceptions the only part that is zero-cost are the try 54 blocks. Some research could be done into the alternative methods for systems 55 that expect a lot more exceptions to be thrown, allowing some overhead in 56 entering and leaving try blocks to make throws faster. But while exceptions 57 remain exceptional the libunwind model will probably remain the most effective 58 option. 99 Zero-cost resumptions is still an open problem. First, because libunwind does 100 not support a successful-exiting stack-search without doing an unwind. 101 Workarounds are possible but awkward. Ideally an extension to libunwind could 102 be made, but that would either require separate maintenance or gain enough 103 support to have it folded into the standard. 59 104 60 Zero-cost resumptions have more problems to solve. First because libunwind 61 does not support a successful exiting stack search without doing an unwind. 62 There are several ways to hack that functionality in. Ideally an extension to 63 libunwind could be made, but that would either require seperate maintenance 64 or gain enough support to have it folded into the standard. 105 Also new techniques to skip previously searched parts of the stack need to be 106 developed to handle the recursive resume problem and support advanced algebraic 107 effects. 65 108 66 Also new techniques to skip previously searched parts of the stack will have 67 to be developed. 109 \section{Signal Exceptions} 110 Goodenough~\cite{Goodenough75} suggests three types of exceptions: escape, 111 notify and signal. Escape are termination exceptions, notify are resumption 112 exceptions, leaving signal unimplemented. 68 113 69 \section{Support for More Platforms} 70 Termination is not portable because it is implemented with inline assembly. 71 Those sections will have to be rewritten to support different architectures 114 A signal exception allows either behaviour, \ie after an exception is handled, 115 the handler has the option of returning to the raise or after the @try@ 116 statement. Currently, \CFA fixes the semantics of the handler return 117 syntactically by the @catch@ or @catchResume@ clause. 72 118 73 \section{Quality-of-Life Improvements} 74 Finally come various improvements to the usability of \CFA. Most of these 75 would just require time. Time that would not lead to interesting research so 76 it has been left aside for now. A few examples are included here but there 77 are more: 119 Signal exception should be reexamined and possibly be supported in \CFA. A very 120 direct translation is to have a new raise and catch pair, and a new statement 121 (or statements) would indicate if the handler returns to the raise or continues 122 where it is; but there may be other options. 78 123 79 \begin{itemize} 80 \item Allowing exception handler to bind the exception to a reference instead 81 of a pointer. This should actually result in no change in behaviour so there 82 is no reason not to allow it. It is however a small improvement; giving a bit 83 of flexibility to the user in what style they want to use. 84 \item Enabling local control flow (by \codeCFA{break}, \codeCFA{return} and 85 similar statements) out of a termination handler. The current set-up makes 86 this very difficult but the catch function that runs the handler after it has 87 been matched could be inlined into the function's body, which would make this 88 much easier. (To do the same for try blocks would probably wait for zero-cost 89 exceptions, which would allow the try block to be inlined as well.) 90 \item Enabling local control flow out of a resumption handler. This would be 91 a weighty operation, causing a stack unwind like a termination, so there might 92 be a different statement or a statement modifier to make sure the user does 93 this purposefully. 124 For instance, resumption could be extended to cover this use by allowing local 125 control flow out of it. This approach would require an unwind as part of the 126 transition as there are stack frames that have to be removed. This approach 127 means there is no notify raise, but because \CFA does not have exception 128 signatures, a termination can be thrown from within any resumption handler so 129 there is already a way to do mimic this in existing \CFA. 94 130 95 However this would require the more complex system as they cannot be inlined 96 into the original function as they can be run at a different place on the 97 stack. So instead the unwinding will have to carry with it information on 98 which one of these points to continue at and possibly also the return value 99 for the function if a \codeCFA{return} statement was used. 100 \end{itemize} 131 % Maybe talk about the escape; and escape CONTROL_STMT; statements or how 132 % if we could choose if _Unwind_Resume proceeded to the clean-up stage this 133 % would be much easier to implement. -
doc/theses/andrew_beach_MMath/implement.tex
r342af53 r8e4aa05 2 2 % Goes over how all the features are implemented. 3 3 4 The implementation work for this thesis covers two components: the virtual 5 system and exceptions. Each component is discussed in detail. 6 4 7 \section{Virtual System} 8 \label{s:VirtualSystem} 5 9 % Virtual table rules. Virtual tables, the pointer to them and the cast. 6 The \CFA virtual system only has one public facing feature: virtual casts. 7 However there is a lot of structure to support that and provide some other 8 features for the standard library. 9 10 All of this is accessed through a field inserted at the beginning of every 11 virtual type. Currently it is called \codeC{virtual_table} but it is not 12 ment to be accessed by the user. This field is a pointer to the type's 13 virtual table instance. It is assigned once during the object's construction 14 and left alone after that. 15 16 \subsection{Virtual Table Construction} 17 For each virtual type a virtual table is constructed. This is both a new type 18 and an instance of that type. Other instances of the type could be created 19 but the system doesn't use them. So this section will go over the creation of 20 the type and the instance. 21 22 Creating the single instance is actually very important. The address of the 23 table acts as the unique identifier for the virtual type. Similarly the first 24 field in every virtual table is the parent's id; a pointer to the parent 25 virtual table instance. 26 27 The remaining fields contain the type's virtual members. First come the ones 28 present on the parent type, in the same order as they were the parent, and 29 then any that this type introduces. The types of the ones inherited from the 30 parent may have a slightly modified type, in that references to the 31 dispatched type are replaced with the current virtual type. These are always 32 taken by pointer or reference. 33 34 The structure itself is created where the virtual type is created. The name 35 of the type is created by mangling the name of the base type. The name of the 36 instance is also generated by name mangling. 37 38 The fields are initialized automatically. 10 While the \CFA virtual system currently has only one public feature, virtual 11 cast \see{\VPageref{p:VirtualCast}}, substantial structure is required to 12 support it, and provide features for exception handling and the standard 13 library. 14 15 \subsection{Virtual Type} 16 Virtual types only have one change to their structure, the addition of a 17 pointer to the virtual table. This is always the first field so that 18 if it is cast to a supertype the field's location is still known. 19 20 This field is set as part of all new generated constructors. 21 \todo{They only come as part exceptions and don't work.} 22 After the object is created the field is constant. 23 24 However it can be read from, internally it is just a regular field called 25 @virtual_table@. Dereferencing it gives the virtual table and access to the 26 type's virtual members. 27 28 \subsection{Virtual Table} 29 Every time a virtual type is defined the new virtual table type must also be 30 defined. 31 32 The unique instance is important because the address of the virtual table 33 instance is used as the identifier for the virtual type. So a pointer to the 34 virtual table and the ID for the virtual type are interchangable. 35 \todo{Unique instances might be going so we will have to talk about the new 36 system instead.} 37 38 The first step in putting it all together is to create the virtual table type. 39 The virtual table type is just a structure and can be described in terms of 40 its fields. The first field is always the parent type ID (or a pointer to 41 the parent virtual table) or 0 (the null pointer). 42 Next are other fields on the parent virtual table are repeated. 43 Finally are the fields used to store any new virtual members of the new 44 The virtual type 45 46 The virtual system is accessed through a private constant field inserted at the 47 beginning of every virtual type, called the virtual-table pointer. This field 48 points at a type's virtual table and is assigned during the object's 49 construction. The address of a virtual table acts as the unique identifier for 50 the virtual type, and the first field of a virtual table is a pointer to the 51 parent virtual-table or @0p@. The remaining fields are duplicated from the 52 parent tables in this type's inheritance chain, followed by any fields this type 53 introduces. Parent fields are duplicated so they can be changed (all virtual 54 members are overridable), so that references to the dispatched type 55 are replaced with the current virtual type. 56 % These are always taken by pointer or reference. 57 58 % Simple ascii diragram: 59 \begin{verbatim} 60 parent_pointer \ 61 parent_field0 | 62 ... | Same layout as parent. 63 parent_fieldN / 64 child_field0 65 ... 66 child_fieldN 67 \end{verbatim} 68 \todo{Refine the diagram} 69 70 % For each virtual type, a virtual table is constructed. This is both a new type 71 % and an instance of that type. Other instances of the type could be created 72 % but the system doesn't use them. So this section will go over the creation of 73 % the type and the instance. 74 75 A virtual table is created when the virtual type is created. The name of the 76 type is created by mangling the name of the base type. The name of the instance 77 is also generated by name mangling. The fields are initialized automatically. 39 78 The parent field is initialized by getting the type of the parent field and 40 79 using that to calculate the mangled name of the parent's virtual table type. 41 80 There are two special fields that are included like normal fields but have 42 special initialization rules: the \codeC{size} field is the type's size and is 43 initialized with a sizeof expression, the \codeC{align} field is the type's 44 alignment and uses an alignof expression. The remaining fields are resolved 45 to a name matching the field's name and type using the normal visibility 46 and overload resolution rules of the type system. 47 48 These operations are split up into several groups depending on where they 49 take place which can vary for monomorphic and polymorphic types. The first 50 devision is between the declarations and the definitions. Declarations, such 51 as a function signature or a structure's name, must always be visible but may 52 be repeated so they go in headers. Definitions, such as function bodies and a 53 structure's layout, don't have to be visible on use but must occur exactly 54 once and go into source files. 55 81 special initialization rules: the @size@ field is the type's size and is 82 initialized with a @sizeof@ expression, the @align@ field is the type's 83 alignment and uses an @alignof@ expression. The remaining fields are resolved 84 to a name matching the field's name and type using the normal visibility and 85 overload resolution rules of the type system. 86 87 These operations are split up into several groups depending on where they take 88 place which varies for monomorphic and polymorphic types. The first devision is 89 between the declarations and the definitions. Declarations, such as a function 90 signature or a aggregate's name, must always be visible but may be repeated in 91 the form of forward declarations in headers. Definitions, such as function 92 bodies and a aggregate's layout, can be separately compiled but must occur 93 exactly once in a source file. 94 95 \begin{sloppypar} 56 96 The declarations include the virtual type definition and forward declarations 57 97 of the virtual table instance, constructor, message function and 58 \codeCFA{get_exception_vtable}. The definition includes the storage and 59 initialization of the virtual table instance and the bodies of the three 60 functions. 98 @get_exception_vtable@. The definition includes the storage and initialization 99 of the virtual table instance and the bodies of the three functions. 100 \end{sloppypar} 61 101 62 102 Monomorphic instances put all of these two groups in one place each. 63 64 Polymorphic instances also split out the core declarations and definitions 65 from the per-instance information. The virtual table type and most of the 66 functions are polymorphic so they are all part of the core. The virtual table 67 instance and the \codeCFA{get_exception_vtable} function. 68 69 Coroutines and threads need instances of \codeCFA{CoroutineCancelled} and 70 \codeCFA{ThreadCancelled} respectively to use all of their functionality. 71 When a new data type is declared with \codeCFA{coroutine} or \codeCFA{thread} 72 the forward declaration for the instance is created as well. The definition 73 of the virtual table is created at the definition of the main function. 103 Polymorphic instances also split out the core declarations and definitions from 104 the per-instance information. The virtual table type and most of the functions 105 are polymorphic so they are all part of the core. The virtual table instance 106 and the @get_exception_vtable@ function. 107 108 \begin{sloppypar} 109 Coroutines and threads need instances of @CoroutineCancelled@ and 110 @ThreadCancelled@ respectively to use all of their functionality. When a new 111 data type is declared with @coroutine@ or @thread@ the forward declaration for 112 the instance is created as well. The definition of the virtual table is created 113 at the definition of the main function. 114 \end{sloppypar} 74 115 75 116 \subsection{Virtual Cast} 76 Virtual casts are implemented as a function call that does the check and a 77 old C-style cast to do the type conversion. The C-cast is just to make sure 78 the generated code is correct so the rest of the section is about that 79 function. 80 81 The function is \codeC{__cfa__virtual_cast} and it is implemented in the 82 standard library. It takes a pointer to the target type's virtual table and 83 the object pointer being cast. The function is very simple, getting the 84 object's virtual table pointer and then checking to see if it or any of 85 its ancestors, by using the parent pointers, are the same as the target type 86 virtual table pointer. It does this in a simple loop. 87 88 For the generated code a forward decaration of the virtual works as follows. 89 There is a forward declaration of \codeC{__cfa__virtual_cast} in every cfa 90 file so it can just be used. The object argument is the expression being cast 91 so that is just placed in the argument list. 92 93 To build the target type parameter the compiler will create a mapping from 94 concrete type-name -- so for polymorphic types the parameters are filled in 95 -- to virtual table address. Every virtual table declaraction is added to the 96 this table; repeats are ignored unless they have conflicting definitions. 97 This does mean the declaractions have to be in scope, but they should usually 98 be introduced as part of the type definition. 117 Virtual casts are implemented as a function call that does the subtype check 118 and a C coercion-cast to do the type conversion. 119 % The C-cast is just to make sure the generated code is correct so the rest of 120 % the section is about that function. 121 The function is 122 \begin{cfa} 123 void * __cfa__virtual_cast( 124 struct __cfa__parent_vtable const * parent, 125 struct __cfa__parent_vtable const * const * child ); 126 \end{cfa} 127 and it is implemented in the standard library. The structure reperents the 128 head of a vtable which is the pointer to the parent virtual table. The 129 @parent@ points directly at the parent type virtual table while the @child@ 130 points at the object of the (possibe) child type. 131 132 In terms of the virtual cast expression, @parent@ comes from looking up the 133 type being cast to and @child@ is the result of the expression being cast. 134 Because the complier outputs C code, some type C type casts are also used. 135 The last bit of glue is an map that saves every virtual type the compiler 136 sees. This is used to check the type used in a virtual cast is a virtual 137 type and to get its virtual table. 138 (It also checks for conflicting definitions.) 139 140 Inside the function it is a simple conditional. If the type repersented by 141 @parent@ is or is an ancestor of the type repersented by @*child@ (it 142 requires one more level of derefence to pass through the object) then @child@ 143 is returned, otherwise the null pointer is returned. 144 145 The check itself is preformed is a simple linear search. If the child 146 virtual table or any of its ancestors (which are retreved through the first 147 field of every virtual table) are the same as the parent virtual table then 148 the cast succeeds. 99 149 100 150 \section{Exceptions} … … 106 156 % resumption doesn't as well. 107 157 108 Many modern languages work with an interal stack that function push and pop 109 their local data to. Stack unwinding removes large sections of the stack, 110 often across functions. 111 112 At a very basic level this can be done with \codeC{setjmp} \& \codeC{longjmp} 113 which simply move the top of the stack, discarding everything on the stack 114 above a certain point. However this ignores all the clean-up code that should 115 be run when certain sections of the stack are removed (for \CFA these are from 116 destructors and finally clauses) and also requires that the point to which the 117 stack is being unwound is known ahead of time. libunwind is used to address 118 both of these problems. 119 120 Libunwind, provided in \texttt{unwind.h} on most platorms, is a C library 121 that provides \CPP style stack unwinding. Its operation is divided into two 122 phases. The search phase -- phase 1 -- is used to scan the stack and decide 123 where the unwinding will stop, this allows for a dynamic target. The clean-up 124 phase -- phase 2 -- does the actual unwinding and also runs any clean-up code 125 as it goes. 126 127 To use the libunwind each function must have a personality function and an 128 LSDA (Language Specific Data Area). Libunwind actually does very little, it 129 simply moves down the stack from function to function. Most of the actions are 130 implemented by the personality function which libunwind calls on every 131 function. Since this is shared across many functions or even every function in 132 a language it will need a bit more information. This is provided by the LSDA 133 which has the unique information for each function. 134 135 Theoretically the LSDA can contain anything but conventionally it is a table 136 with entries reperenting areas of the function and what has to be done there 137 during unwinding. These areas are described in terms of where the instruction 138 pointer is. If the current value of the instruction pointer is between two 139 values reperenting the beginning and end of a region then execution is 140 currently being executed. These are used to mark out try blocks and the 141 scopes of objects with destructors to run. 142 143 GCC will generate an LSDA and attach its personality function with the 144 \texttt{-fexceptions} flag. However this only handles the cleanup attribute. 145 This attribute is used on a variable and specifies a function that should be 146 run when the variable goes out of scope. The function is passed a pointer to 147 the object as well so it can be used to mimic destructors. It however cannot 148 be used to mimic try statements. 149 150 \subsection{Implementing Personality Functions} 151 Personality functions have a complex interface specified by libunwind. 152 This section will cover some of the important parts of that interface. 153 154 \begin{lstlisting} 155 typedef _Unwind_Reason_Code (*_Unwind_Personality_Fn)( 156 int version, 157 _Unwind_Action action, 158 _Unwind_Exception_Class exception_class, 159 _Unwind_Exception * exception, 160 struct _Unwind_Context * context); 158 % Many modern languages work with an interal stack that function push and pop 159 % their local data to. Stack unwinding removes large sections of the stack, 160 % often across functions. 161 162 Stack unwinding is the process of removing stack frames (activations) from the 163 stack. On function entry and return, unwinding is handled directly by the code 164 embedded in the function. Usually, the stack-frame size is known statically 165 based on parameter and local variable declarations. For dynamically-sized 166 local variables, a runtime computation is necessary to know the frame 167 size. Finally, a function's frame-size may change during execution as local 168 variables (static or dynamic sized) go in and out of scope. 169 Allocating/deallocating stack space is usually an $O(1)$ operation achieved by 170 bumping the hardware stack-pointer up or down as needed. 171 172 Unwinding across multiple stack frames is more complex because individual stack 173 management code associated with each frame is bypassed. That is, the location 174 of a function's frame-management code is largely unknown and dispersed 175 throughout the function, hence the current frame size managed by that code is 176 also unknown. Hence, code unwinding across frames does not have direct 177 knowledge about what is on the stack, and hence, how much of the stack needs to 178 be removed. 179 180 % At a very basic level this can be done with @setjmp@ \& @longjmp@ which simply 181 % move the top of the stack, discarding everything on the stack above a certain 182 % point. However this ignores all the cleanup code that should be run when 183 % certain sections of the stack are removed (for \CFA these are from destructors 184 % and finally clauses) and also requires that the point to which the stack is 185 % being unwound is known ahead of time. libunwind is used to address both of 186 % these problems. 187 188 The traditional unwinding mechanism for C is implemented by saving a snap-shot 189 of a function's state with @setjmp@ and restoring that snap-shot with 190 @longjmp@. This approach bypasses the need to know stack details by simply 191 reseting to a snap-shot of an arbitrary but existing function frame on the 192 stack. It is up to the programmer to ensure the snap-shot is valid when it is 193 reset, making this unwinding approach fragile with potential errors that are 194 difficult to debug because the stack becomes corrupted. 195 196 However, many languages define cleanup actions that must be taken when objects 197 are deallocated from the stack or blocks end, such as running a variable's 198 destructor or a @try@ statement's @finally@ clause. Handling these mechanisms 199 requires walking the stack and checking each stack frame for these potential 200 actions. 201 202 For exceptions, it must be possible to walk the stack frames in search of @try@ 203 statements to match and execute a handler. For termination exceptions, it must 204 also be possible to unwind all stack frames from the throw to the matching 205 catch, and each of these frames must be checked for cleanup actions. Stack 206 walking is where most of the complexity and expense of exception handling 207 appears. 208 209 One of the most popular tools for stack management is libunwind, a low-level 210 library that provides tools for stack walking, handler execution, and 211 unwinding. What follows is an overview of all the relevant features of 212 libunwind needed for this work, and how \CFA uses them to implement exception 213 handling. 214 215 \subsection{libunwind Usage} 216 Libunwind, accessed through @unwind.h@ on most platforms, is a C library that 217 provides \CC-style stack-unwinding. Its operation is divided into two phases: 218 search and cleanup. The dynamic target search -- phase 1 -- is used to scan the 219 stack and decide where unwinding should stop (but no unwinding occurs). The 220 cleanup -- phase 2 -- does the unwinding and also runs any cleanup code. 221 222 To use libunwind, each function must have a personality function and a Language 223 Specific Data Area (LSDA). The LSDA has the unique information for each 224 function to tell the personality function where a function is executing, its 225 current stack frame, and what handlers should be checked. Theoretically, the 226 LSDA can contain any information but conventionally it is a table with entries 227 representing regions of the function and what has to be done there during 228 unwinding. These regions are bracketed by the instruction pointer. If the 229 instruction pointer is within a region's start/end, then execution is currently 230 executing in that region. Regions are used to mark out the scopes of objects 231 with destructors and try blocks. 232 233 % Libunwind actually does very little, it simply moves down the stack from 234 % function to function. Most of the actions are implemented by the personality 235 % function which libunwind calls on every function. Since this is shared across 236 % many functions or even every function in a language it will need a bit more 237 % information. 238 239 The GCC compilation flag @-fexceptions@ causes the generation of an LSDA and 240 attaches its personality function. However, this 241 flag only handles the cleanup attribute: 242 \todo{Peter: What is attached? Andrew: It uses the .cfi\_personality directive 243 and that's all I know.} 244 \begin{cfa} 245 void clean_up( int * var ) { ... } 246 int avar __attribute__(( cleanup(clean_up) )); 247 \end{cfa} 248 which is used on a variable and specifies a function, in this case @clean_up@, 249 run when the variable goes out of scope. 250 The function is passed a pointer to the object being removed from the stack 251 so it can be used to mimic destructors. 252 However, this feature cannot be used to mimic @try@ statements as it cannot 253 control the unwinding. 254 255 \subsection{Personality Functions} 256 Personality functions have a complex interface specified by libunwind. This 257 section covers some of the important parts of the interface. 258 259 A personality function can preform different actions depending on how it is 260 called. 261 \begin{lstlisting}[language=C,{moredelim=**[is][\color{red}]{@}{@}}] 262 typedef _Unwind_Reason_Code (*@_Unwind_Personality_Fn@) ( 263 _Unwind_Action @action@, 264 _Unwind_Exception_Class @exception_class@, 265 _Unwind_Exception * @exception@, 266 struct _Unwind_Context * @context@ 267 ); 161 268 \end{lstlisting} 162 163 The return value, the reason code, is an enumeration of possible messages 269 The @action@ argument is a bitmask of possible actions: 270 \begin{enumerate} 271 \item 272 @_UA_SEARCH_PHASE@ specifies a search phase and tells the personality function 273 to check for handlers. If there is a handler in a stack frame, as defined by 274 the language, the personality function returns @_URC_HANDLER_FOUND@; otherwise 275 it return @_URC_CONTINUE_UNWIND@. 276 277 \item 278 @_UA_CLEANUP_PHASE@ specifies a cleanup phase, where the entire frame is 279 unwound and all cleanup code is run. The personality function does whatever 280 cleanup the language defines (such as running destructors/finalizers) and then 281 generally returns @_URC_CONTINUE_UNWIND@. 282 283 \item 284 \begin{sloppypar} 285 @_UA_HANDLER_FRAME@ specifies a cleanup phase on a function frame that found a 286 handler. The personality function must prepare to return to normal code 287 execution and return @_URC_INSTALL_CONTEXT@. 288 \end{sloppypar} 289 290 \item 291 @_UA_FORCE_UNWIND@ specifies a forced unwind call. Forced unwind only performs 292 the cleanup phase and uses a different means to decide when to stop 293 \see{\VRef{s:ForcedUnwind}}. 294 \end{enumerate} 295 296 The @exception_class@ argument is a copy of the 297 \lstinline[language=C]|exception|'s @exception_class@ field. 298 299 The \lstinline[language=C]|exception| argument is a pointer to the user 300 provided storage object. It has two public fields, the exception class, which 301 is actually just a number, identifying the exception handling mechanism that 302 created it, and the cleanup function. The cleanup function is called if 303 required by the exception. 304 305 The @context@ argument is a pointer to an opaque type passed to helper 306 functions called inside the personality function. 307 308 The return value, @_Unwind_Reason_Code@, is an enumeration of possible messages 164 309 that can be passed several places in libunwind. It includes a number of 165 310 messages for special cases (some of which should never be used by the 166 311 personality function) and error codes but unless otherwise noted the 167 personality function should always return \codeC{_URC_CONTINUE_UNWIND}. 168 169 The \codeC{version} argument is the verson of the implementation that is 170 calling the personality function. At this point it appears to always be 1 and 171 it will likely stay that way until a new version of the API is updated. 172 173 The \codeC{action} argument is set of flags that tell the personality 174 function when it is being called and what it must do on this invocation. 175 The flags are as follows: 176 \begin{itemize} 177 \item\codeC{_UA_SEARCH_PHASE}: This flag is set whenever the personality 178 function is called during the search phase. The personality function should 179 decide if unwinding will stop in this function or not. If it does then the 180 personality function should return \codeC{_URC_HANDLER_FOUND}. 181 \item\codeC{_UA_CLEANUP_PHASE}: This flag is set whenever the personality 182 function is called during the cleanup phase. If no other flags are set this 183 means the entire frame will be unwound and all cleanup code should be run. 184 \item\codeC{_UA_HANDLER_FRAME}: This flag is set during the cleanup phase 185 on the function frame that found the handler. The personality function must 186 prepare to return to normal code execution and return 187 \codeC{_URC_INSTALL_CONTEXT}. 188 \item\codeC{_UA_FORCE_UNWIND}: This flag is set if the personality function 189 is called through a forced unwind call. Forced unwind only performs the 190 cleanup phase and uses a different means to decide when to stop. See its 191 section below. 192 \end{itemize} 193 194 The \codeC{exception_class} argument is a copy of the \codeC{exception}'s 195 \codeC{exception_class} field. 196 197 The \codeC{exception} argument is a pointer to the user provided storage 198 object. It has two public fields, the exception class which is actually just 199 a number that identifies the exception handling mechanism that created it and 200 the other is the clean-up function. The clean-up function is called if the 201 exception needs to 202 203 The \codeC{context} argument is a pointer to an opaque type. This is passed 204 to the many helper functions that can be called inside the personality 205 function. 312 personality function should always return @_URC_CONTINUE_UNWIND@. 206 313 207 314 \subsection{Raise Exception} 208 This could be considered the central function of libunwind. It preforms the 209 two staged unwinding the library is built around and most of the rest of the 210 interface of libunwind is here to support it. It's signature is as follows: 211 212 \begin{lstlisting} 315 Raising an exception is the central function of libunwind and it performs a 316 two-staged unwinding. 317 \begin{cfa} 213 318 _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception *); 319 \end{cfa} 320 First, the function begins the search phase, calling the personality function 321 of the most recent stack frame. It continues to call personality functions 322 traversing the stack from newest to oldest until a function finds a handler or 323 the end of the stack is reached. In the latter case, raise exception returns 324 @_URC_END_OF_STACK@. 325 326 Second, when a handler is matched, raise exception continues onto the cleanup 327 phase. 328 Once again, it calls the personality functions of each stack frame from newest 329 to oldest. This pass stops at the stack frame containing the matching handler. 330 If that personality function has not install a handler, it is an error. 331 332 If an error is encountered, raise exception returns either 333 @_URC_FATAL_PHASE1_ERROR@ or @_URC_FATAL_PHASE2_ERROR@ depending on when the 334 error occurred. 335 336 \subsection{Forced Unwind} 337 \label{s:ForcedUnwind} 338 Forced Unwind is the other central function in libunwind. 339 \begin{cfa} 340 _Unwind_Reason_Code _Unwind_ForcedUnwind( _Unwind_Exception *, 341 _Unwind_Stop_Fn, void *); 342 \end{cfa} 343 It also unwinds the stack but it does not use the search phase. Instead another 344 function, the stop function, is used to stop searching. The exception is the 345 same as the one passed to raise exception. The extra arguments are the stop 346 function and the stop parameter. The stop function has a similar interface as a 347 personality function, except it is also passed the stop parameter. 348 \begin{lstlisting}[language=C,{moredelim=**[is][\color{red}]{@}{@}}] 349 typedef _Unwind_Reason_Code (*@_Unwind_Stop_Fn@)( 350 _Unwind_Action @action@, 351 _Unwind_Exception_Class @exception_class@, 352 _Unwind_Exception * @exception@, 353 struct _Unwind_Context * @context@, 354 void * @stop_parameter@); 214 355 \end{lstlisting} 215 356 216 When called the function begins the search phase, calling the personality217 function of the most recent stack frame. It will continue to call personality218 functions traversing the stack new-to-old until a function finds a handler or219 the end of the stack is reached. In the latter case raise exception will220 return with \codeC{_URC_END_OF_STACK}.221 222 Once a handler has been found raise exception continues onto the the cleanup223 phase. Once again it will call the personality functins of each stack frame224 from newest to oldest. This pass will stop at the stack frame that found the225 handler last time, if that personality function does not install the handler226 it is an error.227 228 If an error is encountered raise exception will return either229 \codeC{_URC_FATAL_PHASE1_ERROR} or \codeC{_URC_FATAL_PHASE2_ERROR} depending230 on when the error occured.231 232 \subsection{Forced Unwind}233 This is the second big function in libunwind. It also unwinds a stack but it234 does not use the search phase. Instead another function, the stop function,235 is used to decide when to stop.236 237 \begin{lstlisting}238 _Unwind_Reason_Code _Unwind_ForcedUnwind(239 _Unwind_Exception *, _Unwind_Stop_Fn, void *);240 \end{lstlisting}241 242 The exception is the same as the one passed to raise exception. The extra243 arguments are the stop function and the stop parameter. The stop function has244 a similar interface as a personality function, except it is also passed the245 stop parameter.246 247 \begin{lstlisting}248 typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)(249 int version,250 _Unwind_Action action,251 _Unwind_Exception_Class exception_class,252 _Unwind_Exception * exception,253 struct _Unwind_Context * context,254 void * stop_parameter);255 \end{lstlisting}256 257 357 The stop function is called at every stack frame before the personality 258 function is called and then once more once after all frames of the stack have 259 been unwound. 260 261 Each time it is called the stop function should return \codeC{_URC_NO_REASON} 262 or transfer control directly to other code outside of libunwind. The 263 framework does not provide any assistance here. 264 265 Its arguments are the same as the paired personality function. 266 The actions \codeC{_UA_CLEANUP_PHASE} and \codeC{_UA_FORCE_UNWIND} are always 267 set when it is called. By the official standard that is all but both GCC and 268 Clang add an extra action on the last call at the end of the stack: 269 \codeC{_UA_END_OF_STACK}. 358 function is called and then once more after all frames of the stack are 359 unwound. 360 361 Each time it is called, the stop function should return @_URC_NO_REASON@ or 362 transfer control directly to other code outside of libunwind. The framework 363 does not provide any assistance here. 364 365 \begin{sloppypar} 366 Its arguments are the same as the paired personality function. The actions 367 @_UA_CLEANUP_PHASE@ and @_UA_FORCE_UNWIND@ are always set when it is 368 called. Beyond the libunwind standard, both GCC and Clang add an extra action 369 on the last call at the end of the stack: @_UA_END_OF_STACK@. 370 \end{sloppypar} 270 371 271 372 \section{Exception Context} 272 373 % Should I have another independent section? 273 374 % There are only two things in it, top_resume and current_exception. How it is 274 % stored changes depending on wheither or not the thread-library is linked. 275 276 The exception context is a piece of global storage used to maintain data 277 across different exception operations and to communicate between different 278 components. 279 280 Each stack has its own exception context. In a purely sequental program, using 281 only core Cforall, there is only one stack and the context is global. However 282 if the library \texttt{libcfathread} is linked then there can be multiple 283 stacks so they will each need their own. 284 285 To handle this code always gets the exception context from the function 286 \codeC{this_exception_context}. The main exception handling code is in 287 \texttt{libcfa} and that library also defines the function as a weak symbol 288 so it acts as a default. Meanwhile in \texttt{libcfathread} the function is 289 defined as a strong symbol that replaces it when the libraries are linked 290 together. 291 292 The version of the function defined in \texttt{libcfa} is very simple. It 293 returns a pointer to a global static variable. With only one stack this 294 global instance is associated with the only stack. 295 296 The version of the function defined in \texttt{libcfathread} has to handle 297 more as there are multiple stacks. The exception context is included as 298 part of the per-stack data stored as part of coroutines. In the cold data 299 section, stored at the base of each stack, is the exception context for that 300 stack. The \codeC{this_exception_context} uses the concurrency library to get 301 the current coroutine and through it the cold data section and the exception 302 context. 375 % stored changes depending on whether or not the thread-library is linked. 376 377 The exception context is global storage used to maintain data across different 378 exception operations and to communicate among different components. 379 380 Each stack must have its own exception context. In a sequential \CFA program, 381 there is only one stack with a single global exception-context. However, when 382 the library @libcfathread@ is linked, there are multiple stacks where each 383 needs its own exception context. 384 385 General access to the exception context is provided by function 386 @this_exception_context@. For sequential execution, this function is defined as 387 a weak symbol in the \CFA system-library, @libcfa@. When a \CFA program is 388 concurrent, it links with @libcfathread@, where this function is defined with a 389 strong symbol replacing the sequential version. 390 391 The sequential @this_exception_context@ returns a hard-coded pointer to the 392 global execption context. 393 The concurrent version adds the exception context to the data stored at the 394 base of each stack. When @this_exception_context@ is called it retrieves the 395 active stack and returns the address of the context saved there. 303 396 304 397 \section{Termination} … … 306 399 % catches. Talk about GCC nested functions. 307 400 308 Termination exceptions use libunwind quite heavily because it matches the309 intended use from \CPP exceptions very closely. The main complication is that 310 since the \CFA compiler works by translating to C code it cannot generate the 311 assembly toform the LSDA for try blocks or destructors.401 Termination exceptions use libunwind heavily because it matches the intended 402 use from \CC exceptions closely. The main complication for \CFA is that the 403 compiler generates C code, making it very difficult to generate the assembly to 404 form the LSDA for try blocks or destructors. 312 405 313 406 \subsection{Memory Management} 314 The first step of termination is to copy the exception into memory managed by 315 the exception system. Currently the system just uses malloc, without reserved 316 memory or and ``small allocation" optimizations. The exception handling 317 mechanism manages memory for the exception as well as memory for libunwind 318 and the system's own per-exception storage. 319 320 Exceptions are stored in variable sized block. The first component is a fixed 321 sized data structure that contains the information for libunwind and the 322 exception system. The second component is a blob of memory that is big enough 323 to store the exception. Macros with pointer arthritic and type cast are 324 used to move between the components or go from the embedded 325 \codeC{_Unwind_Exception} to the entire node. 326 327 All of these nodes are strung together in a linked list. One linked list per 328 stack, with the head stored in the exception context. Within each linked list 329 the most recently thrown exception is at the head and the older exceptions 330 are further down the list. This list format allows exceptions to be thrown 331 while a different exception is being handled. Only the exception at the head 332 of the list is currently being handled, the other will wait for the 333 exceptions before them to be removed. 334 335 The virtual members in the exception's virtual table. The size of the 336 exception, the copy function and the free function are all in the virtual 337 table so they are decided per-exception type. The size and copy function are 338 used right away when the exception is copied in to managed memory. After the 339 exception is handled the free function is used to clean up the exception and 340 then the entire node is passed to free. 341 342 \subsection{Try Statements \& Catch Clauses} 343 The try statements with termination handlers have a pretty complex conversion 344 to compensate for the lack of assembly generation. Libunwind requires an LSDA 345 (Language Specific Data Area) and personality function for a function to 346 unwind across it. The LSDA in particular is hard to generate at the level of 347 C which is what the \CFA compiler outputs so a work-around is used. 348 349 This work around is a function called \codeC{__cfaehm_try_terminate} in the 350 standard library. The contents of a try block and the termination handlers 351 are converted into functions. These are then passed to the try terminate 352 function and it calls them. This puts the try statements in their own 353 functions so that no function has to deal with both termination handlers and 354 destructors. 355 356 This function has some custom embedded assembly that defines its personality 357 function and LSDA. This is hand coded in C which is why there is only one 358 version of it, the compiler has no capability to generate it. The personality 359 function is structured so that it may be expanded, but really it only handles 360 this one function. Notably it does not handle any destructors so the function 361 is constructed so that it does need to run it. 407 The first step of a termination raise is to copy the exception into memory 408 managed by the exception system. Currently, the system uses @malloc@, rather 409 than reserved memory or the stack top. The exception handling mechanism manages 410 memory for the exception as well as memory for libunwind and the system's own 411 per-exception storage. 412 413 [Quick ASCII diagram to get started.] 414 \begin{verbatim} 415 Fixed Header | _Unwind_Exception <- pointer target 416 | 417 | Cforall storage 418 | 419 Variable Body | the exception <- fixed offset 420 V ... 421 \end{verbatim} 422 423 Exceptions are stored in variable-sized blocks. 424 The first component is a fixed sized data structure that contains the 425 information for libunwind and the exception system. The second component is an 426 area of memory big enough to store the exception. Macros with pointer arthritic 427 and type cast are used to move between the components or go from the embedded 428 @_Unwind_Exception@ to the entire node. 429 430 All of these nodes are linked together in a list, one list per stack, with the 431 list head stored in the exception context. Within each linked list, the most 432 recently thrown exception is at the head followed by older thrown 433 exceptions. This format allows exceptions to be thrown, while a different 434 exception is being handled. The exception at the head of the list is currently 435 being handled, while other exceptions wait for the exceptions before them to be 436 removed. 437 438 The virtual members in the exception's virtual table provide the size of the 439 exception, the copy function, and the free function, so they are specific to an 440 exception type. The size and copy function are used immediately to copy an 441 exception into managed memory. After the exception is handled the free function 442 is used to clean up the exception and then the entire node is passed to free 443 so the memory can be given back to the heap. 444 445 \subsection{Try Statements and Catch Clauses} 446 The try statement with termination handlers is complex because it must 447 compensate for the lack of assembly-code generated from \CFA. Libunwind 448 requires an LSDA and personality function for control to unwind across a 449 function. The LSDA in particular is hard to mimic in generated C code. 450 451 The workaround is a function called @__cfaehm_try_terminate@ in the standard 452 library. The contents of a try block and the termination handlers are converted 453 into functions. These are then passed to the try terminate function and it 454 calls them. 455 Because this function is known and fixed (and not an arbitrary function that 456 happens to contain a try statement) this means the LSDA can be generated ahead 457 of time. 458 459 Both the LSDA and the personality function are set ahead of time using 460 embedded assembly. This is handcrafted using C @asm@ statements and contains 461 enough information for the single try statement the function repersents. 362 462 363 463 The three functions passed to try terminate are: 364 \begin{ itemize}365 \item The try function: This function is the try block, all the code inside366 t he try block is placed inside the try function. It takes no parameters and367 has no return value. This function is called during regular execution to run 368 the tryblock.369 \item The match function: This function decides if this try statement should 370 handle any given termination exception. It takes a pointer to the exception 371 and returns 0 if the exception is not handled here. Otherwise the return value 372 is the id of the handler that should handle the exception. It is called 373 during the search phase. 374 It is constructed from the conditional part of each handler. It runs each 375 check in turn, first checking to see if the object 376 \item The catch function: This function handles the exception. It takes a 377 pointer to the exception and the handler's id and returns nothing. It is 378 called after the clean-up phase. 379 It is constructed by stitching together the bodies of each handler 380 \end{itemize} 381 All three are created with GCC nested functions. GCC nested functions can be 382 used to create closures, functions that can refer to the state of other 383 functions on the stack. This allows the functions to refer to the main 384 function and all the variables in scope. 385 386 These nested functions and all other functions besides 387 \codeC{__cfaehm_try_terminate} in \CFA use the GCC personality function and 388 the \texttt{-fexceptions} flag to generate the LSDA. This allows destructors 389 t o be implemented with the cleanup attribute.464 \begin{description} 465 \item[try function:] This function is the try block, all the code inside the 466 try block is placed inside the try function. It takes no parameters and has no 467 return value. This function is called during regular execution to run the try 468 block. 469 470 \item[match function:] This function is called during the search phase and 471 decides if a catch clause matches the termination exception. It is constructed 472 from the conditional part of each handler and runs each check, top to bottom, 473 in turn, first checking to see if the exception type matches and then if the 474 condition is true. It takes a pointer to the exception and returns 0 if the 475 exception is not handled here. Otherwise the return value is the id of the 476 handler that matches the exception. 477 478 \item[handler function:] This function handles the exception. It takes a 479 pointer to the exception and the handler's id and returns nothing. It is called 480 after the cleanup phase. It is constructed by stitching together the bodies of 481 each handler and dispatches to the selected handler. 482 \end{description} 483 All three functions are created with GCC nested functions. GCC nested functions 484 can be used to create closures, functions that can refer to the state of other 485 functions on the stack. This approach allows the functions to refer to all the 486 variables in scope for the function containing the @try@ statement. These 487 nested functions and all other functions besides @__cfaehm_try_terminate@ in 488 \CFA use the GCC personality function and the @-fexceptions@ flag to generate 489 the LSDA. This allows destructors to be implemented with the cleanup attribute. 390 490 391 491 \section{Resumption} 392 492 % The stack-local data, the linked list of nodes. 393 493 394 Resumption uses a list of nodes for its stack traversal. The head of the list 395 is stored in the exception context. The nodes in the list just have a pointer 494 Resumption simple to implement because there is no stack unwinding. The 495 resumption raise uses a list of nodes for its stack traversal. The head of the 496 list is stored in the exception context. The nodes in the list have a pointer 396 497 to the next node and a pointer to the handler function. 397 498 398 The on a resumption throw the this list is traversed. At each node the 399 handler function is called and is passed the exception by pointer. It returns 400 true if the exception was handled and false otherwise. 401 402 The handler function does both the matching and catching. It tries each 403 the condition of \codeCFA{catchResume} in order, top-to-bottom and until it 404 finds a handler that matches. If no handler matches then the function returns 405 false. Otherwise the matching handler is run, if it completes successfully 406 the function returns true. Rethrows, through the \codeCFA{throwResume;} 407 statement, cause the function to return true. 408 409 \subsection{Libunwind Compatibility} 410 Resumption does not use libunwind for two simple reasons. The first is that 411 it does not have to unwind anything so would never need to use the clean-up 412 phase. Still the search phase could be used to make it free to enter or exit 413 a try statement with resumption handlers in the same way termination handlers 414 are for the same trade off in the cost of the throw. This is where the second 415 reason comes in, there is no way to return from a search without installing 416 a handler or raising an error. 417 418 Although work arounds could be created none seemed to be worth it for the 419 prototype. This implementation has no difference in behaviour and is much 420 simpler. 499 A resumption raise traverses this list. At each node the handler function is 500 called, passing the exception by pointer. It returns true if the exception is 501 handled and false otherwise. 502 503 The handler function does both the matching and handling. It computes the 504 condition of each @catchResume@ in top-to-bottom order, until it finds a 505 handler that matches. If no handler matches then the function returns 506 false. Otherwise the matching handler is run; if it completes successfully, the 507 function returns true. Rethrowing, through the @throwResume;@ statement, 508 causes the function to return true. 509 510 % Recursive Resumption Stuff: 511 Search skipping \see{\VPageref{p:searchskip}}, which ignores parts of the stack 512 already examined, is accomplished by updating the front of the list as the 513 search continues. Before the handler at a node is called the head of the list 514 is updated to the next node of the current node. After the search is complete, 515 successful or not, the head of the list is reset. 516 517 This mechanism means the current handler and every handler that has already 518 been checked are not on the list while a handler is run. If a resumption is 519 thrown during the handling of another resumption the active handlers and all 520 the other handler checked up to this point are not checked again. 521 522 This structure also supports new handler added while the resumption is being 523 handled. These are added to the front of the list, pointing back along the 524 stack -- the first one points over all the checked handlers -- and the ordering 525 is maintained. 526 527 \label{p:zero-cost} 528 Note, the resumption implementation has a cost for entering/exiting a @try@ 529 statement with @catchResume@ clauses, whereas a @try@ statement with @catch@ 530 clauses has zero-cost entry/exit. While resumption does not need the stack 531 unwinding and cleanup provided by libunwind, it could use the search phase to 532 providing zero-cost enter/exit using the LSDA. Unfortunately, there is no way 533 to return from a libunwind search without installing a handler or raising an 534 error. Although workarounds might be possible, they are beyond the scope of 535 this thesis. The current resumption implementation has simplicity in its 536 favour. 421 537 % Seriously, just compare the size of the two chapters and then consider 422 538 % that unwind is required knowledge for that chapter. … … 424 540 \section{Finally} 425 541 % Uses destructors and GCC nested functions. 426 Finally clauses are a simple decomposition to some of the existing features. 427 The code in the block is placed into a GCC nested function with a unique name, 428 no arguments or return values. This nested function is then set as the 429 clean-up function of an empty object that is declared at the beginning of a 430 block placed around the contexts of the try statement. 542 Finally clauses is placed into a GCC nested-function with a unique name, and no 543 arguments or return values. This nested function is then set as the cleanup 544 function of an empty object that is declared at the beginning of a block placed 545 around the context of the associated @try@ statement. 431 546 432 547 The rest is handled by GCC. The try block and all handlers are inside the 433 block. When they are complete control exits the block and the empty object434 is cleanedup, which runs the function that contains the finally code.548 block. At completion, control exits the block and the empty object is cleaned 549 up, which runs the function that contains the finally code. 435 550 436 551 \section{Cancellation} … … 438 553 439 554 Cancellation also uses libunwind to do its stack traversal and unwinding, 440 however it uses a different primary function \codeC{_Unwind_ForcedUnwind}.441 Details of its interface can be found in the unwind section.442 443 The first step of cancellation is to find the stack was cancelled and which444 type of stack it is. Luckily the threadslibrary stores the main thread445 pointer and the current thread pointer and every thread stores a pointer to555 however it uses a different primary function @_Unwind_ForcedUnwind@. Details 556 of its interface can be found in the \VRef{s:ForcedUnwind}. 557 558 The first step of cancellation is to find the cancelled stack and its type: 559 coroutine or thread. Fortunately, the thread library stores the main thread 560 pointer and the current thread pointer, and every thread stores a pointer to 446 561 its main coroutine and the coroutine it is currently executing. 447 562 448 So if the the current thread's main and current coroutine do not match, it is 449 a coroutine cancellation. Otherwise if the main and current thread do not 450 match, it is a thread cancellation. Otherwise it is a main thread 451 cancellation. 452 453 However if the threading library is not linked then execution must be on the 454 main stack as that is the only one that exists. So the entire check is skipped 455 using the linker and weak symbols. Instead the main thread cancellation is 456 unconditionally preformed. 457 458 Regardless of how they are choosen afterwords the stop function and the stop 459 parameter are passed to the forced unwind functon. The general pattern of all 460 three stop functions is the same, they continue unwinding until the end of 461 stack when they do there primary work. 462 463 Main stack cancellation it is very simple. The ``transfer" is just an abort, 464 the program stops executing. 465 466 The coroutine cancellation stores the exception on the coroutine and then 467 does a coroutine context switch. The rest is handled inside resume. Every time 468 control returns from a resumed thread there is a check to see if it is 469 cancelled. If it is the exception is retrieved and the CoroutineCancelled 470 exception is constructed and loaded. It is then thrown as a regular exception 471 with the default handler coming from the context of the resumption call. 472 473 The thread cancellation stores the exception on the thread's main stack and 474 then returns to the scheduler. The rest is handled by the joiner. The wait 475 for the joined thread to finish works the same but after that it checks 476 to see if there was a cancellation. If there was the exception is retrieved 477 and the ThreadCancelled exception is constructed. The default handler is 478 passed in as a function pointer. If it is null (as it is for the 479 auto-generated joins on destructor call) it a default is used that simply 480 calls abort; which gives the required handling on implicate join. 563 So if the active thread's main and current coroutine are the same. If they 564 are then the current stack is a thread stack, otherwise it is a coroutine 565 stack. If it is a thread stack then an equality check with the stored main 566 thread pointer and current thread pointer is enough to tell if the current 567 thread is the main thread or not. 568 569 However, if the threading library is not linked, the sequential execution is on 570 the main stack. Hence, the entire check is skipped because the weak-symbol 571 function is loaded. Therefore, a main thread cancellation is unconditionally 572 performed. 573 574 Regardless of how the stack is chosen, the stop function and parameter are 575 passed to the forced-unwind function. The general pattern of all three stop 576 functions is the same: they continue unwinding until the end of stack when they 577 do there primary work. 578 579 For main stack cancellation, the transfer is just a program abort. 580 581 For coroutine cancellation, the exception is stored on the coroutine's stack, 582 and the coroutine context switches to its last resumer. The rest is handled on 583 the backside of the resume, which check if the resumed coroutine is 584 cancelled. If cancelled, the exception is retrieved from the resumed coroutine, 585 and a @CoroutineCancelled@ exception is constructed and loaded with the 586 cancelled exception. It is then resumed as a regular exception with the default 587 handler coming from the context of the resumption call. 588 589 For thread cancellation, the exception is stored on the thread's main stack and 590 then context switched to the scheduler. The rest is handled by the thread 591 joiner. When the join is complete, the joiner checks if the joined thread is 592 cancelled. If cancelled, the exception is retrieved and the joined thread, and 593 a @ThreadCancelled@ exception is constructed and loaded with the cancelled 594 exception. The default handler is passed in as a function pointer. If it is 595 null (as it is for the auto-generated joins on destructor call), the default is 596 used, which is a program abort. 597 %; which gives the required handling on implicate join. -
doc/theses/andrew_beach_MMath/thesis-frontpgs.tex
r342af53 r8e4aa05 36 36 37 37 A thesis \\ 38 presented to the University of Waterloo \\ 38 presented to the University of Waterloo \\ 39 39 in fulfillment of the \\ 40 40 thesis requirement for the degree of \\ … … 64 64 \cleardoublepage 65 65 66 66 67 67 %---------------------------------------------------------------------- 68 68 % EXAMINING COMMITTEE (Required for Ph.D. theses only) … … 71 71 \begin{center}\textbf{Examining Committee Membership}\end{center} 72 72 \noindent 73 The following served on the Examining Committee for this thesis. The decision of the Examining Committee is by majority vote. 74 \bigskip 75 76 \noindent 77 \begin{tabbing} 78 Internal-External Member: \= \kill % using longest text to define tab length 79 External Examiner: \> Bruce Bruce \\ 73 The following served on the Examining Committee for this thesis. The decision 74 of the Examining Committee is by majority vote. 75 \bigskip 76 77 \noindent 78 \begin{tabbing} 79 Internal-External Member: \= \kill % using longest text to define tab length 80 External Examiner: \> Bruce Bruce \\ 80 81 \> Professor, Dept. of Philosophy of Zoology, University of Wallamaloo \\ 81 \end{tabbing} 82 \bigskip 83 82 \end{tabbing} 83 \bigskip 84 84 85 \noindent 85 86 \begin{tabbing} … … 91 92 \end{tabbing} 92 93 \bigskip 93 94 94 95 \noindent 95 96 \begin{tabbing} … … 99 100 \end{tabbing} 100 101 \bigskip 101 102 102 103 \noindent 103 104 \begin{tabbing} … … 107 108 \end{tabbing} 108 109 \bigskip 109 110 110 111 \noindent 111 112 \begin{tabbing} … … 123 124 % December 13th, 2006. It is designed for an electronic thesis. 124 125 \noindent 125 I hereby declare that I am the sole author of this thesis. This is a true copy of the thesis, including any required final revisions, as accepted by my examiners. 126 127 \bigskip 128 126 I hereby declare that I am the sole author of this thesis. This is a true copy 127 of the thesis, including any required final revisions, as accepted by my 128 examiners. 129 130 \bigskip 131 129 132 \noindent 130 133 I understand that my thesis may be made electronically available to the public. -
doc/theses/andrew_beach_MMath/thesis.tex
r342af53 r8e4aa05 45 45 % FRONT MATERIAL 46 46 %---------------------------------------------------------------------- 47 \input{thesis-frontpgs} 47 \input{thesis-frontpgs} 48 48 49 49 %---------------------------------------------------------------------- … … 65 65 A \gls{computer} could compute $\pi$ all day long. In fact, subsets of digits 66 66 of $\pi$'s decimal approximation would make a good source for psuedo-random 67 vectors, \gls{rvec} . 67 vectors, \gls{rvec} . 68 68 69 69 %---------------------------------------------------------------------- … … 96 96 97 97 \begin{itemize} 98 \item A well-prepared PDF should be 98 \item A well-prepared PDF should be 99 99 \begin{enumerate} 100 100 \item Of reasonable size, {\it i.e.} photos cropped and compressed. 101 \item Scalable, to allow enlargment of text and drawings. 102 \end{enumerate} 101 \item Scalable, to allow enlargment of text and drawings. 102 \end{enumerate} 103 103 \item Photos must be bit maps, and so are not scaleable by definition. TIFF and 104 104 BMP are uncompressed formats, while JPEG is compressed. Most photos can be 105 105 compressed without losing their illustrative value. 106 \item Drawings that you make should be scalable vector graphics, \emph{not} 106 \item Drawings that you make should be scalable vector graphics, \emph{not} 107 107 bit maps. Some scalable vector file formats are: EPS, SVG, PNG, WMF. These can 108 all be converted into PNG or PDF, that pdflatex recognizes. Your drawing 109 package probably can export to one of these formats directly. Otherwise, a 110 common procedure is to print-to-file through a Postscript printer driver to 111 create a PS file, then convert that to EPS (encapsulated PS, which has a 112 bounding box to describe its exact size rather than a whole page). 108 all be converted into PNG or PDF, that pdflatex recognizes. Your drawing 109 package probably can export to one of these formats directly. Otherwise, a 110 common procedure is to print-to-file through a Postscript printer driver to 111 create a PS file, then convert that to EPS (encapsulated PS, which has a 112 bounding box to describe its exact size rather than a whole page). 113 113 Programs such as GSView (a Ghostscript GUI) can create both EPS and PDF from 114 114 PS files. Appendix~\ref{AppendixA} shows how to generate properly sized Matlab 115 115 plots and save them as PDF. 116 116 \item It's important to crop your photos and draw your figures to the size that 117 you want to appear in your thesis. Scaling photos with the 118 includegraphics command will cause loss of resolution. And scaling down 117 you want to appear in your thesis. Scaling photos with the 118 includegraphics command will cause loss of resolution. And scaling down 119 119 drawings may cause any text annotations to become too small. 120 120 \end{itemize} 121 121 122 122 For more information on \LaTeX\, see the uWaterloo Skills for the 123 Academic Workplace \href{https://uwaterloo.ca/information-systems-technology/services/electronic-thesis-preparation-and-submission-support/ethesis-guide/creating-pdf-version-your-thesis/creating-pdf-files-using-latex/latex-ethesis-and-large-documents}{course notes}. 123 Academic Workplace \href{https://uwaterloo.ca/information-systems-technology/services/electronic-thesis-preparation-and-submission-support/ethesis-guide/creating-pdf-version-your-thesis/creating-pdf-files-using-latex/latex-ethesis-and-large-documents}{course notes}. 124 124 \footnote{ 125 125 Note that while it is possible to include hyperlinks to external documents, 126 it is not wise to do so, since anything you can't control may change over time. 127 It \emph{would} be appropriate and necessary to provide external links to 128 additional resources for a multimedia ``enhanced'' thesis. 129 But also note that if the \package{hyperref} package is not included, 130 as for the print-optimized option in this thesis template, any \cmmd{href} 126 it is not wise to do so, since anything you can't control may change over time. 127 It \emph{would} be appropriate and necessary to provide external links to 128 additional resources for a multimedia ``enhanced'' thesis. 129 But also note that if the \package{hyperref} package is not included, 130 as for the print-optimized option in this thesis template, any \cmmd{href} 131 131 commands in your logical document are no longer defined. 132 132 A work-around employed by this thesis template is to define a dummy 133 \cmmd{href} command (which does nothing) in the preamble of the document, 134 before the \package{hyperref} package is included. 133 \cmmd{href} command (which does nothing) in the preamble of the document, 134 before the \package{hyperref} package is included. 135 135 The dummy definition is then redifined by the 136 136 \package{hyperref} package when it is included. … … 138 138 139 139 The classic book by Leslie Lamport \cite{lamport.book}, author of \LaTeX , is 140 worth a look too, and the many available add-on packages are described by 140 worth a look too, and the many available add-on packages are described by 141 141 Goossens \textit{et al} \cite{goossens.book}. 142 142 … … 180 180 Export Setup button in the figure Property Editor. 181 181 182 \section{From the Command Line} 182 \section{From the Command Line} 183 183 All figure properties can also be manipulated from the command line. Here's an 184 example: 184 example: 185 185 \begin{verbatim} 186 186 x=[0:0.1:pi]; -
doc/theses/andrew_beach_MMath/unwinding.tex
r342af53 r8e4aa05 1 1 \chapter{Unwinding in \CFA} 2 2 3 Stack unwinding is the process of removing things from the stack. Within 4 functions and on function return this is handled directly by the code in the 5 function itself as it knows exactly what is on the stack just from the 6 current location in the function. Unwinding across stack frames means that it 7 is no longer knows exactly what is on the stack or even how much of the stack 8 needs to be removed. 3 Stack unwinding is the process of removing stack frames (activations) from the 4 stack. On function entry and return, unwinding is handled directly by the code 5 embedded in the function. Usually, the stack-frame size is known statically 6 based on parameters and local variable declarations. For dynamically-sized 7 local variables, a runtime computation is necessary to know the frame 8 size. Finally, a function's frame-size may change during execution as local 9 variables (static or dynamic sized) go in and out of scope. 10 Allocating/deallocating stack space is usually an $O(1)$ operation achieved by 11 bumping the hardware stack-pointer up or down as needed. 9 12 10 Even this is fairly simple if nothing needs to happen when the stack unwinds. 11 Traditional C can unwind the stack by saving and restoring state (with 12 \codeC{setjmp} \& \codeC{longjmp}). However many languages define actions that 13 have to be taken when something is removed from the stack, such as running 14 a variable's destructor or a \codeCFA{try} statement's \codeCFA{finally} 15 clause. Handling this requires walking the stack going through each stack 16 frame.13 Unwinding across multiple stack frames is more complex because individual stack 14 management code associated with each frame is bypassed. That is, the location 15 of a function's frame code is largely unknown and dispersed throughout the 16 function, hence the current stack-frame size managed by that code is also 17 unknown. Hence, code unwinding across frames does not have direct knowledge 18 about what is on the stack, and hence, how much of the stack needs to be 19 removed. 17 20 18 For exceptions, this means everything from the point the exception is raised 19 to the point it is caught, while checking each frame for handlers during the 20 stack walk to find out where it should be caught. This is where the most of 21 the expense and complexity of exception handling comes from. 21 The traditional unwinding mechanism for C is implemented by saving a snap-shot 22 of a function's state with @setjmp@ and restoring that snap-shot with 23 @longjmp@. This approach bypasses the need to know stack details by simply 24 reseting to a snap-shot of an arbitrary but existing function frame on the 25 stack. It is up to the programmer to ensure the snap-shot is valid when it is 26 reset, making the code fragile with potential errors that are difficult to 27 debug because the stack becomes corrupted. 22 28 23 To do all of this we use libunwind, a low level library that provides tools 24 for stack walking and stack unwinding. What follows is an overview of all the 25 relivant features of libunwind and then how \CFA uses them to implement its 26 exception handling. 29 However, many languages define cleanup actions that have to be taken when 30 something is deallocated from the stack or blocks end, such as running a 31 variable's destructor or a @try@ statement's @finally@ clause. Handling these 32 mechanisms requires walking the stack and checking each stack frame for these 33 potential actions. 34 35 For exceptions, it must be possible to walk the stack frames in search of try 36 statements with handlers to perform exception matching. For termination 37 exceptions, it must be possible to unwind all stack frames from the throw to 38 the matching catch, and each of these frames must be checked for cleanup 39 actions. Stack walking is where the most of the complexity and expense of 40 exception handling comes from. 41 42 One of the most popular tools for stack management is libunwind, a low level 43 library that provides tools for stack walking and unwinding. What follows is an 44 overview of all the relevant features of libunwind and how \CFA uses them to 45 implement its exception handling. 27 46 28 47 \section{libunwind Usage} 29 30 \CFA uses two primary functions in libunwind to create most of its 31 exceptional control-flow: \codeC{_Unwind_RaiseException} and 32 \codeC{_Unwind_ForcedUnwind}. 33 Their operation is divided into two phases: search and clean-up. The search 34 phase -- phase 1 -- is used to scan the stack but not unwinding it. The 35 clean-up phase -- phase 2 -- is used for unwinding. 48 \CFA uses two primary functions in libunwind to create most of its exceptional 49 control-flow: @_Unwind_RaiseException@ and @_Unwind_ForcedUnwind@. Their 50 operation is divided into two phases: search and clean-up. The search phase -- 51 phase 1 -- is used to scan the stack but not unwinding it. The clean-up phase 52 -- phase 2 -- is used for unwinding. 36 53 37 54 The raise-exception function uses both phases. It starts by searching for a … … 44 61 A personality function performs three tasks, although not all have to be 45 62 present. The tasks performed are decided by the actions provided. 46 \codeC{_Unwind_Action} is a bitmask of possible actions and an argument of 47 this typeis passed into the personality function.63 @_Unwind_Action@ is a bitmask of possible actions and an argument of this type 64 is passed into the personality function. 48 65 \begin{itemize} 49 \item\codeC{_UA_SEARCH_PHASE} is passed in search phase and tells the 50 personality function to check for handlers. If there is a handler in this 51 stack frame, as defined by the language, the personality function should 52 return \codeC{_URC_HANDLER_FOUND}. Otherwise it should return 53 \codeC{_URC_CONTINUE_UNWIND}. 54 \item\codeC{_UA_CLEANUP_PHASE} is passed in during the clean-up phase and 55 means part or all of the stack frame is removed. The personality function 56 should do whatever clean-up the language defines 57 (such as running destructors/finalizers) and then generally returns 58 \codeC{_URC_CONTINUE_UNWIND}. 59 \item\codeC{_UA_HANDLER_FRAME} means the personality function must install 60 a handler. It is also passed in during the clean-up phase and is in addition 61 to the clean-up action. libunwind provides several helpers for the personality 62 function here. Once it is done, the personality function must return 63 \codeC{_URC_INSTALL_CONTEXT}. 66 \item 67 \begin{sloppypar} 68 @_UA_SEARCH_PHASE@ is passed in for the search phase and tells the personality 69 function to check for handlers. If there is a handler in a stack frame, as 70 defined by the language, the personality function returns @_URC_HANDLER_FOUND@; 71 otherwise it return @_URC_CONTINUE_UNWIND@. 72 \end{sloppypar} 73 \item 74 @_UA_CLEANUP_PHASE@ is passed in during the clean-up phase and means part or 75 all of the stack frame is removed. The personality function does whatever 76 clean-up the language defines (such as running destructors/finalizers) and then 77 generally returns @_URC_CONTINUE_UNWIND@. 78 \item 79 @_UA_HANDLER_FRAME@ means the personality function must install a handler. It 80 is also passed in during the clean-up phase and is in addition to the clean-up 81 action. libunwind provides several helpers for the personality function. Once 82 it is done, the personality function returns @_URC_INSTALL_CONTEXT@. 64 83 \end{itemize} 65 The personality function is given a number of other arguments. Some ar e for66 compatability and there is the \codeC{struct _Unwind_Context}pointer which67 passed to many helpers to get information about the current stack frame.84 The personality function is given a number of other arguments. Some arguments 85 are for compatibility, and there is the @struct _Unwind_Context@ pointer which 86 is passed to many helpers to get information about the current stack frame. 68 87 69 Forced-unwind only performs the clean-up phase. It takes three arguments: 70 a pointer to the exception, a pointer to the stop function and a pointer to 71 the stop parameter. It does most of the same things as phase two of 72 raise-exception but with some extras. 73 The first it passes in an extra action to the personality function on each 74 stack frame, \codeC{_UA_FORCE_UNWIND}, which means a handler cannot be 88 For cancellation, forced-unwind only performs the clean-up phase. It takes 89 three arguments: a pointer to the exception, a pointer to the stop function and 90 a pointer to the stop parameter. It does most of the same actions as phase two 91 of raise-exception but passes in an extra action to the personality function on 92 each stack frame, @_UA_FORCE_UNWIND@, which means a handler cannot be 75 93 installed. 76 94 77 The big change is that forced-unwind calls the stop function. Each time it 78 steps into a frame, before calling the personality function, it callsthe79 s top function. The stop function receives all the same arguments as the80 personality function will and the stop parameter supplied toforced-unwind.95 As well, forced-unwind calls the stop function each time it steps into a frame, 96 before calling the personality function. The stop function receives all the 97 same arguments as the personality function and the stop parameter supplied to 98 forced-unwind. 81 99 82 100 The stop function is called one more time at the end of the stack after all 83 stack frames have been removed. By the standard API this is markedby setting101 stack frames have been removed. The standard API marks this frame by setting 84 102 the stack pointer inside the context passed to the stop function. However both 85 GCC and Clang add an extra action for this case \codeC{_UA_END_OF_STACK}.103 GCC and Clang add an extra action for this case @_UA_END_OF_STACK@. 86 104 87 Each time function the stop function is called it can do one or two things. 88 When it is not the end of the stack it can return \codeC{_URC_NO_REASON} to 89 continue unwinding. 105 Each time the stop function is called, it can do one or two things. When it is 106 not the end of the stack it can return @_URC_NO_REASON@ to continue unwinding. 90 107 % Is there a reason that NO_REASON is used instead of CONTINUE_UNWIND? 91 Its only other option is to use its own means to transfer control elsewhere 92 and never return to its caller. It may always do this and no additional tools 93 a re provided to do it.108 The other option is to use some other means to transfer control elsewhere and 109 never return to its caller. libunwind provides no additional tools for 110 alternate transfers of control. 94 111 95 112 \section{\CFA Implementation} 96 113 97 To use libunwind, \CFA provides several wrappers, its own storage, 98 personalityfunctions, and a stop function.114 To use libunwind, \CFA provides several wrappers, its own storage, personality 115 functions, and a stop function. 99 116 100 117 The wrappers perform three tasks: set-up, clean-up and controlling the … … 108 125 The core control code is called every time a throw -- after set-up -- or 109 126 re-throw is run. It uses raise-exception to search for a handler and to run it 110 if one is found. If no handler is found and raise-exception returns then127 if one is found. If no handler is found and raise-exception returns, then 111 128 forced-unwind is called to run all destructors on the stack before terminating 112 129 the process. 113 130 114 The stop function is very simple. It checksthe end of stack flag to see if115 it is finished unwinding. If so, it calls \codeC{exit} to end the process, 116 otherwise itreturns with no-reason to continue unwinding.131 The stop function is simple. It checks for the end of stack flag to see if 132 unwinding is finished. If so, it calls @exit@ to end the process, otherwise it 133 returns with no-reason to continue unwinding. 117 134 % Yeah, this is going to have to change. 118 135 119 136 The personality routine is more complex because it has to obtain information 120 about the function by scanning the L SDA (Language Specific Data Area). This137 about the function by scanning the Language Specific Data Area (LSDA). This 121 138 step allows a single personality function to be used for multiple functions and 122 let that personaliity function figure out exactly where in the function123 execution was, what is currently in the stack frame and what handlers should124 bechecked.139 lets that personality function figure out exactly where in the function 140 execution is, what is currently in the stack frame, and what handlers should be 141 checked. 125 142 % Not that we do that yet. 126 143 127 However, generating the LSDA is difficult. It requires knowledge about the 128 location of the instruction pointer and stack layout, which varies with129 compiler and optimization levels. So for frames where there are only 130 destructors, GCC's attribute cleanup with the \texttt{-fexception} flag is 131 sufficient to handle unwinding.144 It is also necessary to generate the LSDA, which is difficult. It requires 145 knowledge about the location of the instruction pointer and stack layout, which 146 varies with compiler and optimization levels. Fortunately, for frames where 147 there are only destructors, GCC's attribute cleanup with the @-fexception@ flag 148 is sufficient to handle unwinding. 132 149 133 The only functions that require more than that are those that contain134 \codeCFA{try} statements. A \codeCFA{try} statement has a \codeCFA{try} 135 clause, some number of \codeCFA{catch} clauses and \codeCFA{catchResume} 136 c lauses and may have a \codeCFA{finally} clause. Of these only \codeCFA{try}137 statements with \codeCFA{catch} clauses need to be transformed and only they 138 and the \codeCFA{try} clause are involved.150 The only functions that require more information are those containing @try@ 151 statements. Specifically, only @try@ statements with @catch@ clauses need to be 152 transformed. The @try@ statement is converted into a series of closures that 153 can access other parts of the function according to scoping rules but can be 154 passed around. The @catch@ clauses are converted into two functions: the match 155 function and the handler function. 139 156 140 The \codeCFA{try} statement is converted into a series of closures which can 141 access other parts of the function according to scoping rules but can be 142 passed around. The \codeCFA{try} clause is converted into the try functions, 143 almost entirely unchanged. The \codeCFA{catch} clauses are converted into two 144 functions; the match function and the catch function. 157 Together the match function and the catch function form the code that runs when 158 an exception passes out of the guarded block for a try statement. The match 159 function is used during the search phase: it is passed an exception and checks 160 each handler to see if the raised exception matches the handler exception. It 161 returns an index that represents which handler matched or there is no 162 match. The catch function is used during the clean-up phase, it is passed an 163 exception and the index of a handler. It casts the exception to the exception 164 type declared in that handler and then runs the handler's body. 145 165 146 Together the match function and the catch function form the code that runs 147 when an exception passes out of a try block. The match function is used during 148 the search phase, it is passed an exception and checks each handler to see if 149 it will handle the exception. It returns an index that repersents which 150 handler matched or that none of them did. The catch function is used during 151 the clean-up phase, it is passed an exception and the index of a handler. It 152 casts the exception to the exception type declared in that handler and then 153 runs the handler's body. 154 155 These three functions are passed to \codeC{try_terminate}. This is an 166 These three functions are passed to @try_terminate@, which is an 156 167 % Maybe I shouldn't quote that, it isn't its actual name. 157 internal hand-written function that has its own personality function and 158 custom assembly LSD doesthe exception handling in \CFA. During normal159 execution all this function does is call the try function and then return.160 It is onlywhen exceptions are thrown that anything interesting happens.168 internal hand-written function that has its own personality function and custom 169 assembly LSDA for doing the exception handling in \CFA. During normal 170 execution, this function calls the try function and then return. It is only 171 when exceptions are thrown that anything interesting happens. 161 172 162 173 During the search phase the personality function gets the pointer to the match 163 function and calls it. If the match function returns a handler index the174 function and calls it. If the match function returns a handler index, the 164 175 personality function saves it and reports that the handler has been found, 165 otherwise unwinding continues. 166 During the clean-up phase the personality function only does anything if the 167 handler was found in this frame. If it was then the personality function 168 inst alls the handler, which is setting the instruction pointer in169 \codeC{try_terminate} to an otherwise unused section that calls the catch 170 function, passing it the current exception and handler index. 171 \codeC{try_terminate} returns as soon as the catch function returns.176 otherwise unwinding continues. During the clean-up phase, the personality 177 function only performs an action, when a handler is found in a frame. For each 178 found frame, the personality function installs the handler, which sets the 179 instruction pointer in @try_terminate@ to an otherwise unused section that 180 calls the catch function, passing it the current exception and handler index. 181 @try_terminate@ returns as soon as the catch function returns. At this point 182 control has returned to normal control flow. 172 183 173 At this point control has returned to normal control flow. 184 \PAB{Maybe a diagram would be helpful?} -
doc/theses/fangren_yu_COOP_F20/Report.tex
r342af53 r8e4aa05 17 17 \usepackage[usenames]{color} 18 18 \input{common} % common CFA document macros 19 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true, pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref}19 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref} 20 20 \usepackage{breakurl} 21 21 \urlstyle{sf} … … 76 76 \renewcommand{\subsectionmark}[1]{\markboth{\thesubsection\quad #1}{\thesubsection\quad #1}} 77 77 \pagenumbering{roman} 78 \linenumbers % comment out to turn off line numbering78 %\linenumbers % comment out to turn off line numbering 79 79 80 80 \maketitle 81 81 \pdfbookmark[1]{Contents}{section} 82 \tableofcontents 83 84 \clearpage 82 85 83 \thispagestyle{plain} 86 84 \pagenumbering{arabic} 87 85 88 86 \begin{abstract} 87 \CFA is an evolutionary, non-object-oriented extension of the C programming language, featuring a parametric type-system, and is currently under active development. The reference compiler for the \CFA language, @cfa-cc@, has some of its major components dated back to the early 2000s, which are based on inefficient data structures and algorithms. This report introduces improvements targeting the expression resolution algorithm, suggested by a recent prototype experiment on a simplified model, which are implemented in @cfa-cc@ to support the full \CFA language. These optimizations speed up the compiler by a factor of 20 across the existing \CFA codebase, bringing the compilation time of a mid-sized \CFA source file down to the 10-second level. A few problem cases derived from realistic code examples are analyzed in detail, with proposed solutions. This work is a critical step in the \CFA project development to achieve its eventual goal of being used alongside C for large software systems. 89 88 \end{abstract} 90 89 90 \clearpage 91 \section*{Acknowledgements} 92 \begin{sloppypar} 93 I would like to thank everyone in the \CFA team for their contribution towards this project. Programming language design and development is a tough subject and requires a lot of teamwork. Without the collaborative efforts from the team, this project could not have been a success. Specifically, I would like to thank Andrew Beach for introducing me to the \CFA codebase, Thierry Delisle for maintaining the test and build automation framework, Michael Brooks for providing example programs of various experimental language and type system features, and most importantly, Professor Martin Karsten for recommending me to the \CFA team, and my supervisor, Professor Peter Buhr for encouraging me to explore deeply into intricate compiler algorithms. Finally, I gratefully acknowledge the help from Aaron Moss, former graduate from the team and the author of the precedent thesis work, to participate in the \CFA team's virtual conferences and email correspondence, and provide many critical arguments and suggestions. 2020 had been an unusually challenging year for everyone and we managed to keep a steady pace. 94 \end{sloppypar} 95 96 \clearpage 97 \tableofcontents 98 99 \clearpage 91 100 \section{Introduction} 92 101 93 \section{Completed work} 102 \CFA language, developed by the Programming Language Group at the University of Waterloo, has a long history, with the initial language design in 1992 by Glen Ditchfield~\cite{Ditchfield92} and the first proof-of-concept compiler built in 2003 by Richard Bilson~\cite{Bilson03}. Many new features have been added to the language over time, but the core of \CFA's type-system --- parametric functions introduced by the @forall@ clause (hence the name of the language) providing parametric overloading --- remains mostly unchanged. 103 104 The current \CFA reference compiler, @cfa-cc@, is designed using the visitor pattern~\cite{vistorpattern} over an abstract syntax tree (AST), where multiple passes over the AST modify it for subsequent passes. @cfa-cc@ still includes many parts taken directly from the original Bilson implementation, which served as the starting point for this enhancement work to the type system. Unfortunately, the prior implementation did not provide the efficiency required for the language to be practical: a \CFA source file of approximately 1000 lines of code can take multiple minutes to compile. The cause of the problem is that the old compiler used inefficient data structures and algorithms for expression resolution, which involved significant copying and redundant work. 105 106 This report presents a series of optimizations to the performance-critical parts of the resolver, with a major rework of the compiler data-structures using a functional-programming approach to reduce memory complexity. The improvements were suggested by running the compiler builds with a performance profiler against the \CFA standard-library source-code and a test suite to find the most underperforming components in the compiler algorithm. 107 108 The \CFA team endorses a pragmatic philosophy that focuses on practical implications of language design and implementation rather than theoretical limits. In particular, the compiler is designed to be expressive with respect to code reuse while maintaining type safety, but compromise theoretical soundness in extreme corner cases. However, when these corner cases do appear in actual usage, they need to be thoroughly investigated. A case-by-case analysis is presented for several of these corner cases, some of which point to certain weaknesses in the language design with solutions proposed based on experimental results. 109 110 \section{AST restructuring} 94 111 95 112 \subsection{Memory model with sharing} 96 113 97 A major rework of the abstract syntax tree (AST) data structure in the compiler is completed as the first step of the project. The majority of work were documented in the reference manual of the compiler~\cite{cfa-cc}. To summarize: 98 \begin{itemize} 99 \item 100 AST nodes (and therefore subtrees) can be shared without copying when reused. 101 \item 102 Modifications apply the functional programming principle, making copies for local changes without affecting the original data shared by other owners. In-place mutations are permitted as a special case when sharing does not happen. The logic is implemented by reference counting. 103 \item 104 Memory allocation and freeing are performed automatically using smart pointers. 105 \end{itemize} 106 The resolver algorithm designed for overload resolution naturally introduces a significant amount of reused intermediate representations, especially in the following two places: 107 \begin{itemize} 108 \item 109 Function overload candidates are computed by combining the argument candidates bottom-up, with many of them being a common term. For example, if $n$ overloads of a function @f@ all take an integer for the first parameter but different types for the second (@f( int, int )@, @f( int, double )@, etc.) the first term is reused $n$ times for each of the generated candidate expressions. This effect is particularly bad for deep expression trees. 110 \item 111 In the unification algorithm and candidate elimination step, actual types are obtained by substituting the type parameters by their bindings. Let $n$ be the complexity (\ie number of nodes in representation) of the original type, $m$ be the complexity of bound type for parameters, and $k$ be the number of occurrences of type parameters in the original type. If everything needs to be deep-copied, the substitution step takes $O(n+mk)$ time and memory, while using shared nodes it is reduced to $O(n)$ time and $O(k)$ memory. 112 \end{itemize} 113 One of the worst examples for the old compiler is a long chain of I/O operations 114 \begin{cfa} 115 sout | 1 | 2 | 3 | 4 | ... 116 \end{cfa} 117 The pipe operator is overloaded by \CFA I/O library for every primitive type in C language, as well as I/O manipulators defined by the library. In total there are around 50 overloads for the output stream operation. On resolving the $n$-th pipe operator in the sequence, the first term, which is the result of sub-expression containing $n-1$ pipe operators, is reused to resolve every overload. Therefore at least $O(n^2)$ copies of expression nodes are made during resolution, not even counting type unification cost; combined with two large factors from number of overloads of pipe operators, and that the ``output stream type'' in \CFA is a trait with 27 assertions (which adds to complexity of the pipe operator's type) this makes compiling a long output sequence extremely slow. In new AST representation only $O(n)$ copies are required and type of pipe operator is not copied at all. 118 119 Reduction in space complexity is especially important, as preliminary profiling result on the old compiler build shows that over half of time spent in expression resolution are on memory allocations. 120 114 A major rework of the AST data-structure in the compiler was completed as the first step of the project. The majority of this work is documented in my prior report documenting the compiler reference-manual~\cite{cfa-cc}. To summarize: 115 \begin{itemize} 116 \item 117 AST nodes (and therefore subtrees) can be shared without copying. 118 \item 119 Modifications are performed using functional-programming principles, making copies for local changes without affecting the original data shared by other owners. In-place mutations are permitted as a special case when there is no sharing. The logic is implemented by reference counting. 120 \item 121 Memory allocation and freeing are performed automatically using smart pointers~\cite{smartpointers}. 122 \end{itemize} 123 124 The resolver algorithm, designed for overload resolution, allows a significant amount of code reused, and hence copying, for the intermediate representations, especially in the following two places: 125 \begin{itemize} 126 \item 127 Function overload candidates are computed by combining the argument candidates bottom-up, with many being a common term. For example, if $n$ overloads of a function @f@ all take an integer for the first parameter but different types for the second, \eg @f( int, int )@, @f( int, double )@, etc., the first term is copied $n$ times for each of the generated candidate expressions. This copying is particularly bad for deep expression trees. 128 \item 129 In the unification algorithm and candidate elimination step, actual types are obtained by substituting the type parameters by their bindings. Let $n$ be the complexity (\ie number of nodes in representation) of the original type, $m$ be the complexity of the bound type for parameters, and $k$ be the number of occurrences of type parameters in the original type. If every substitution needs to be deep-copied, these copy step takes $O(n+mk)$ time and memory, while using shared nodes it is reduced to $O(n)$ time and $O(k)$ memory. 130 \end{itemize} 131 One of the worst examples for the old compiler is a long chain of I/O operations: 132 \begin{cfa} 133 sout | 1 | 2 | 3 | 4 | ...; // print integer constants 134 \end{cfa} 135 The pipe operator is overloaded by the \CFA I/O library for every primitive type in the C language, as well as I/O manipulators defined by the library. In total, there are around 50 overloads for the output stream operation. On resolving the $n$-th pipe operator in the sequence, the first term, which is the result of sub-expression containing $n-1$ pipe operators, is reused to resolve every overload. Therefore at least $O(n^2)$ copies of expression nodes are made during resolution, not even counting type unification cost; combined with the two large factors from number of overloads of pipe operators, and that the ``output stream type'' in \CFA is a trait with 27 assertions (which adds to complexity of the pipe operator's type) this makes compiling a long output sequence extremely slow. In the new AST representation, only $O(n)$ copies are required and the type of the pipe operator is not copied at all. 136 Reduction in space complexity is especially important, as preliminary profiling results on the old compiler build showed over half of the time spent in expression resolution is on memory allocations. 137 138 Since the compiler codebase is large and the new memory model mostly benefits expression resolution, some of the old data structures are still kept, and a conversion pass happens before and after the general resolve phase. Rewriting every compiler module will take longer, and whether the new model is correct was unknown when this project started, therefore only the resolver is currently implemented with the new data structure. 139 121 140 122 141 \subsection{Merged resolver calls} 123 142 124 The pre-resolve phase of compilation, ina dequately called ``validate'' in the compiler source code, does more than just simple syntax validation, as it also normalizes input program. Some of them, however, requires type information on expressions and therefore needsto call the resolver before the general resolve phase. There are three notable places where the resolver is invoked:125 \begin{itemize} 126 \item 127 Attempt to generate default constructor, copy constructor and destructor for user-defined @struct@ types 128 \item 129 Resolve @with@ statements (the same as in P ython, which introduces fields of a structure directly in scope)143 The pre-resolve phase of compilation, inappropriately called ``validate'' in the compiler source code, has a number of passes that do more than simple syntax and semantic validation; some passes also normalizes the input program. A few of these passes require type information for expressions, and therefore, need to call the resolver before the general resolve phase. There are three notable places where the resolver is invoked: 144 \begin{itemize} 145 \item 146 Generate default constructor, copy constructor and destructor for user-defined @struct@ types. 147 \item 148 Resolve @with@ statements (the same as in Pascal~\cite{pascal}), which introduces fields of a structure directly into a scope. 130 149 \item 131 150 Resolve @typeof@ expressions (cf. @decltype@ in \CC); note that this step may depend on symbols introduced by @with@ statements. 132 151 \end{itemize} 133 Since the compiler codebase is large and the new memory model mostly only benefits expression resolution, the old data structure is still kept, and a conversion pass happens before and after resolve phase. Rewriting every compiler module will take a long time, and whether the new model is correct is still unknown when started, therefore only the resolver is implemented with the new data structure. 134 135 Since the constructor calls were one of the most expensive to resolve (reason will be shown in the next section), pre-resolve phase were taking more time after resolver moves to the more efficient new implementation. To better facilitate the new resolver, every step that requires type information are reintegrated as part of resolver. 136 137 A by-product of this work is that the reversed dependence of @with@ statement and @typeof@ can now be handled. Previously, the compiler is unable to handle cases such as 152 153 Since the constructor calls are one of the most expensive to resolve (reason given in~\VRef{s:SpecialFunctionLookup}), this pre-resolve phase was taking a large amount of time even after the resolver was changed to the more efficient new implementation. The problem is that multiple resolutions repeat a significant amount of work. Therefore, to better facilitate the new resolver, every step that requires type information should be integrated as part of the general resolver phase. 154 155 A by-product of this work is that reversed dependence between @with@ statement and @typeof@ can now be handled. Previously, the compiler was unable to handle cases such as: 138 156 \begin{cfa} 139 157 struct S { int x; }; 140 158 S foo(); 141 159 typeof( foo() ) s; // type is S 142 with (s) { 160 with (s) { 143 161 x; // refers to s.x 144 162 } 145 163 \end{cfa} 146 since t ype of @s@ is still unresolved when handling @with@ expressions. Instead, the new (and correct) approach is to evaluate @typeof@ expressions when the declaration is first seen, and it suffices because of the declaration-before-use rule.164 since the type of @s@ is unresolved when handling @with@ expressions because the @with@ pass follows the @typeof@ pass (interchanging passes only interchanges the problem). Instead, the new (and correct) approach is to evaluate @typeof@ expressions when the declaration is first seen during resolution, and it suffices because of the declaration-before-use rule. 147 165 148 166 149 167 \subsection{Special function lookup} 150 151 Reducing the number of functions looked up for overload resolution is an effective way to gain performance when there are many overloads but most of them are trivially wrong. In practice, most functions have few (if any) overloads but there are notable exceptions. Most importantly, constructor @?{}@, destructor @^?{}@, and assignment @?=?@ are generated for every user-defined type, and in a large source file there can be hundreds of them. Furthermore, many calls to them are generated for initializing variables and passing arguments. This fact makes them the most overloaded and most called functions. 152 153 In an object-oriented programming language, object has methods declared with their types, so a call such as @obj.f()@ only needs to perform lookup in the method table corresponding to type of @obj@. \CFA on the other hand, does not have methods, and all types are open (\ie new operations can be defined on them), so a similar approach will not work in general. However, the ``big 3'' operators have a unique property enforced by the language rules, such that the first parameter must have a reference type. Since \CFA does not have class inheritance, reference type must always match exactly. Therefore, argument-dependent lookup can be implemented for these operators, by using a dedicated symbol table. 154 155 The lookup key used for the special functions is the mangled type name of the first parameter, which acts as the @this@ parameter in an object-oriented language. To handle generic types, the type parameters are stripped off, and only the base type is matched. Note that a constructor (destructor, assignment operator) taking arbitrary @this@ argument, for example @forall( dtype T ) void ?{}( T & );@ is not allowed, and it guarantees that if the @this@ type is known, all possible overloads can be found by searching with the given type. In case that the @this@ argument itself is overloaded, it is resolved first and all possible result types are used for lookup. 156 157 Note that for the generated expressions, the particular variable for @this@ argument is fully known, without overloads, so the majority of constructor call resolutions only need to check for one given object type. Explicit constructor calls and assignment statements sometimes may require lookup for multiple types. In the extremely rare case that type of @this@ argument is yet unbound, everything will have to be checked, just like without the argument-dependent lookup algorithm; fortunately, this case almost never happens in practice. An example is found in the library function @new@: 168 \label{s:SpecialFunctionLookup} 169 170 Reducing the number of function looked ups for overload resolution is an effective way to gain performance when there are many overloads but most of them are trivially wrong. In practice, most functions have few (if any) overloads but there are notable exceptions. Most importantly, constructor @?{}@, destructor @^?{}@, and assignment @?=?@ are generated for every user-defined type (@struct@ and @union@ in C), and in a large source file there can be hundreds of them. Furthermore, many calls are generated for initializing variables, passing arguments and copying values. This fact makes them the most overloaded and most called functions. 171 172 In an object-oriented programming language, the object-method types are scoped within a class, so a call such as @obj.f()@ only needs to perform lookup in the method table corresponding to the type of @obj@. \CFA on the other hand, does not have methods, and all types are open, \ie new operations can be defined on them without inheritance; at best a \CFA type can be constrained by a translation unit. However, the ``big 3'' operators have a unique property enforced by the language rules: the first parameter must be a reference to its associated type, which acts as the @this@ parameter in an object-oriented language. Since \CFA does not have class inheritance, the reference type must always match exactly. Therefore, argument-dependent lookup can be implemented for these operators by using a dedicated, fast symbol-table. 173 174 The lookup key for the special functions is the mangled type name of the first parameter. To handle generic types, the type parameters are stripped off, and only the base type is matched. Note a constructor (destructor, assignment operator) may not take an arbitrary @this@ argument, \eg @forall( dtype T ) void ?{}( T & )@, thus guaranteeing that if the @this@ type is known, all possible overloads can be found by searching with this given type. In the case where the @this@ argument itself is overloaded, it is resolved first and all possible result types are used for lookup. 175 176 Note that for a generated expression, the particular variable for the @this@ argument is fully known, without overloads, so the majority of constructor-call resolutions only need to check for one given object type. Explicit constructor calls and assignment statements sometimes require lookup for multiple types. In the extremely rare case that the @this@-argument type is unbound, all necessary types are guaranteed to be checked, as for the previous lookup without the argument-dependent lookup; fortunately, this complex case almost never happens in practice. An example is found in the library function @new@: 158 177 \begin{cfa} 159 178 forall( dtype T | sized( T ), ttype TT | { void ?{}( T &, TT ); } ) 160 179 T * new( TT p ) { return &(*malloc()){ p }; } 161 180 \end{cfa} 162 as @malloc@ may return a pointer to any type, depending on context. 163 164 Interestingly, this particular line of code actually caused another complicated issue, where the unusually massive work of checking every constructor in presence makes the case even worse. Section~\ref{s:TtypeResolutionInfiniteRecursion} presents a detailed analysis for theproblem.165 166 The ``callable'' operator @?()@ (cf. @operator()@ in \CC) c ould also be included in the special operator list, as it is usually only on user-defined types, and the restriction that first argument must be a reference seems reasonable in this case.181 as @malloc@ may return a pointer to any type, depending on context. 182 183 Interestingly, this particular declaration actually causes another complicated issue, making the complex checking of every constructor even worse. \VRef[Section]{s:TtypeResolutionInfiniteRecursion} presents a detailed analysis of this problem. 184 185 The ``callable'' operator @?()@ (cf. @operator()@ in \CC) can also be included in this special operator list, as it is usually only on user-defined types, and the restriction that the first argument must be a reference seems reasonable in this case. 167 186 168 187 169 188 \subsection{Improvement of function type representation} 170 189 171 Since substituting type parameters with their bound types is one fundamental operation in many parts of resolver algorithm (particularly unification and environment binding), making as few copies of type nodes as possible helps reducing memory complexity. Even with the new memory management model, allocation is still a significant factor of resolver performance. Conceptually, operations on type nodes of AST should be performed in functional programming style, treating the data structure as immutable and only copy when necessary. The in-place mutation is a mere optimization that does not change logic of operations. 172 The model was broken on function types by an inappropriate design. Function types require some special treatment due to the existence of assertions. In particular, it must be able to distinguish two different kinds of type parameter usage: 190 Since substituting type parameters with their bound types is one fundamental operation in many parts of resolver algorithm (particularly unification and environment binding), making as few copies of type nodes as possible helps reducing memory complexity. Even with the new memory management model, allocation is still a significant factor of resolver performance. Conceptually, operations on type nodes of the AST should be performed in functional-programming style, treating the data structure as immutable and only copying when necessary. The in-place mutation is a mere optimization that does not change the logic for operations. 191 192 However, the model was broken for function types by an inappropriate design. Function types require special treatment due to the existence of assertions that constrain the types it supports. Specifically, it must be possible to distinguish two different kinds of type parameter usage: 173 193 \begin{cfa} 174 194 forall( dtype T ) void foo( T * t ) { 175 forall( dtype U ) void bar( T * t, U* u ) { ... }176 } 177 \end{cfa} 178 Here, only @U@ is a free parameter in declaration of @bar@, as it appears in the function's own forall clause; while @T@ is not free.179 180 Moreover, the resolution algorithm also has to distinguish type bindings of multiple calls to the same function, for example with195 forall( dtype U ) void bar( @T@ * t, @U@ * u ) { ... } 196 } 197 \end{cfa} 198 Here, only @U@ is a free parameter in the nested declaration of function @bar@, as @T@ must be bound at the call site when resolving @bar@. 199 200 Moreover, the resolution algorithm also has to distinguish type bindings of multiple calls to the same function, \eg: 181 201 \begin{cfa} 182 202 forall( dtype T ) int foo( T x ); 183 foo( foo( 1.0 ) );184 \end{cfa} 185 The inner call has binding (T: double) while the outer call has binding (T: int). Therefore a unique representation of free parameters in each expression is required. This was previously done by creating a copy of the parameter declarations inside function type, and fixing references afterwards. However, fixing references is an inherently deep operation that does not work well with functional programming model, as it must be evaluated eagerlyon the entire syntax tree representing the function type.186 187 The revised approach generates a unique ID value for each function call expression instance and represents an occurrence of free parameter type with a pair of generated ID and the original parameter declaration, so that references do not need to be fixed, and a shallow copy of function type is possible.188 189 Note that after the change, all declaration nodes in syntax tree representation maps one-to-one with the actual declarations in the program, and therefore are guaranteed to be unique. Such property can potentially enable more optimizations, and some related ideas are presented after Section~\ref{s:SharedSub-ExpressionCaseUniqueExpressions}.203 int i = foo( foo( 1.0 ) ); 204 \end{cfa} 205 The inner call has binding (T: double) while the outer call has binding (T: int). Therefore a unique representation for the free parameters is required in each expression. This type binding was previously done by creating a copy of the parameter declarations inside the function type and fixing references afterwards. However, fixing references is an inherently deep operation that does not work well with the functional-programming style, as it forces eager evaluation on the entire syntax tree representing the function type. 206 207 The revised approach generates a unique ID value for each function call expression instance and represents an occurrence of a free-parameter type with a pair of generated ID and original parameter declaration, so references are unique and a shallow copy of the function type is possible. 208 209 Note that after the change, all declaration nodes in the syntax-tree representation now map one-to-one with the actual declarations in the program, and therefore are guaranteed to be unique. This property can potentially enable more optimizations, and some related ideas are presented at the end of \VRef{s:SharedSub-ExpressionCaseUniqueExpressions}. 190 210 191 211 192 212 \subsection{Improvement of pruning steps} 193 213 194 A minor improvement for candidate elimination is to skip the step on the function overloads themselves and only perform on results of function application. As function calls are usually by name, the name resolution rule dictates that every function candidate necessarily has a different type; indirect function calls are rare, and when they do appear, they usually will not have many possible interpretations, and those rarely matches exactly in argument type. Since function types have a much more complex representation than data types (with multiple parameters and assertions), checking equality on them also takes longer.195 196 A brief test of this approach shows that the number of function overloads considered in expression resolution increases by a negligible amount of less than 1 percent, while type comparisons in candidate elimination are cut by more than half. Improvement is consistent over all \CFA source files in the test suite.214 A minor improvement for candidate elimination is to skip the step on the function overloads and only check the results of function application. As function calls are usually by name (versus pointers to functions), the name resolution rule dictates that every function candidate necessarily has a different type; indirect function calls are rare, and when they do appear, there are even fewer cases with multiple interpretations, and these rarely match exactly in argument type. Since function types have a much more complex representation (with multiple parameters and assertions) than data types, checking equality on them also takes longer. 215 216 A brief test of this approach shows that the number of function overloads considered in expression resolution increases by an amount of less than 1 percent, while type comparisons in candidate elimination are reduced by more than half. This improvement is consistent over all \CFA source files in the test suite. 197 217 198 218 … … 200 220 \label{s:SharedSub-ExpressionCaseUniqueExpressions} 201 221 202 Unique expression denotes an expression that must be evaluated only once, to prevent unwanted side effects. It is currently only a compiler artifact, generated on tuple member expression of the form222 Unique expression denotes an expression evaluated only once to prevent unwanted side effects. It is currently only a compiler artifact, generated for tuple-member expression of the form: 203 223 \begin{cfa} 204 224 struct S { int a; int b; }; … … 206 226 s.[a, b]; // tuple member expression, type is [int, int] 207 227 \end{cfa} 208 If the aggregate expression contains function calls, it cannot be evaluated multiple times:228 If the aggregate expression is function call, it cannot be evaluated multiple times: 209 229 \begin{cfa} 210 230 S makeS(); 211 makeS().[a, b]; // this should only make one S231 makeS().[a, b]; // this should only generate a unique S 212 232 \end{cfa} 213 233 Before code generation, the above expression is internally represented as … … 226 246 \end{cfa} 227 247 at code generation, where @_unique_var@ and @_unique_var_evaluated@ are generated variables whose scope covers all appearances of the same expression. 228 229 Note that although the unique expression is only used for tuple expansion now, it is a generally useful construction, and can be seen in other languages, such as Scala's @lazy val@~\cite{Scala}; therefore it could be worthwhile to introduce the unique expression to a broader context in \CFA and even make it directly available to programmers. 230 231 In the compiler's visitor pattern, however, this creates a problem where multiple paths to a logically unique expression exist, so it may be modified more than once and become ill-formed; some specific intervention is required to ensure that unique expressions are only visited once. Furthermore, a unique expression appearing in more than one places will be copied on mutation so its representation is no longer unique. Some hacks are required to keep it in sync, and the methods are different when mutating the unique expression instance itself or its underlying expression. 232 233 Example when mutating the underlying expression (visit-once guard) 248 The conditional check ensures a single call to @makeS()@ even though there are logically multiple calls because of the tuple field expansion. 249 250 Note that although the unique expression is only used for tuple expansion now, it is a generally useful construction, and is seen in other programming languages, such as Scala's @lazy val@~\cite{Scala}; therefore it may be worthwhile to introduce the unique expression to a broader context in \CFA and even make it directly available to programmers. 251 252 In the compiler's visitor pattern, however, this creates a problem where multiple paths to a logically unique expression exist, so it may be modified more than once and become ill-formed; some specific intervention is required to ensure unique expressions are only visited once. Furthermore, a unique expression appearing in more than one places is copied on mutation so its representation is no longer unique. 253 254 Currently, special cases are required to keep everything synchronized, and the methods are different when mutating the unique expression instance itself or its underlying expression: 255 \begin{itemize} 256 \item 257 When mutating the underlying expression (visit-once guard) 234 258 \begin{cfa} 235 259 void InsertImplicitCalls::previsit( const ast::UniqueExpr * unqExpr ) { 236 if ( visitedIds.count( unqExpr->id ) ) visit_children = false;260 @if ( visitedIds.count( unqExpr->id ) ) visit_children = false;@ 237 261 else visitedIds.insert( unqExpr->id ); 238 262 } 239 263 \end{cfa} 240 Example when mutating the unique instance itself, which actually creates copies 264 \item 265 When mutating the unique instance itself, which actually creates copies 241 266 \begin{cfa} 242 267 auto mutExpr = mutate( unqExpr ); // internally calls copy when shared 243 if ( ! unqMap.count( unqExpr->id ) ) { 268 @if ( ! unqMap.count( unqExpr->id ) ) {@ 244 269 ... 245 270 } else { … … 248 273 } 249 274 \end{cfa} 250 Such workaround seems difficult to be fit into a common visitor template. This suggests the memory model may need different kinds of nodes to accurately represent the syntax tree. 251 252 Together with the fact that declaration nodes are always unique, it is possible that AST nodes can be classified by three different types: 253 \begin{itemize} 254 \item 255 \textbf{Strictly unique} with only one owner (declarations); 256 \item 257 \textbf{Logically unique} with (possibly) many owners but should not be copied (unique expression example presented here); 258 \item 259 \textbf{Shared} by functional programming model, which assume immutable data structure and are copied on mutation. 275 \end{itemize} 276 Such workarounds are difficult to fit into the common visitor pattern, which suggests the memory model may need different kinds of nodes to accurately represent this feature in the AST. 277 278 Given that declaration nodes are unique, it is possible for AST nodes to be divided into three different types: 279 \begin{itemize} 280 \item 281 \textbf{Singleton} with only one owner (declarations); 282 \item 283 \textbf{No-copy} with multiple owners but cannot be copied (unique expression example presented here); 284 \item 285 \textbf{Copy} by functional-programming style, which assumes immutable data structures that are copied on mutation. 260 286 \end{itemize} 261 287 The boilerplate code can potentially handle these three cases differently. … … 264 290 \section{Analysis of resolver algorithm complexity} 265 291 266 The focus of this chapter is to identify and analyze some realistic cases that cause resolver algorithm to have an exponential run time. As previous work has shown [3], the overload resolution problem in \CFA has worst-case exponential complexity; however, only few specific patterns can trigger the exponential complexity in practice. Implementing heuristic-based optimization for those selected cases is helpful to alleviate the problem.292 The focus of this section is to identify and analyze some realistic cases that cause the resolver algorithm to have an exponential runtime. As previous work has shown~\cite[\S~4.2.1]{Moss19}, the overload resolution problem in \CFA has worst-case exponential complexity; however, only few specific patterns can trigger the exponential complexity in practice. Implementing heuristic-based optimization for those selected cases is helpful to alleviate the problem. 267 293 268 294 … … 270 296 \label{s:UnboundReturnType} 271 297 272 The interaction of return type overloading and polymorphic functions creates this problem of function calls with unbound returntype, and is further complicated by the presence of assertions.298 The interaction of return-type overloading and polymorphic functions creates function calls with unbounded return-type, and is further complicated by the presence of assertions. 273 299 The prime example of a function with unbound return type is the type-safe version of C @malloc@: 274 300 \begin{cfa} 275 // size deduced from type, so no need to provide the size argument 276 forall( dtype T | sized( T ) ) T * malloc( void ); 277 \end{cfa} 278 Unbound return type can be problematic in resolver algorithm complexity because a single match of function call with unbound return type may create multiple candidates. In the worst case, consider a function declared to return any @otype@: 301 forall( dtype T | sized( T ) ) 302 T * malloc( void ) { return (T *)malloc( sizeof(T) ); } // call C malloc 303 int * i = malloc(); // type deduced from left-hand size $\(\Rightarrow\)$ no size argument or return cast 304 \end{cfa} 305 An unbound return-type is problematic in resolver complexity because a single match of a function call with an unbound return type may create multiple candidates. In the worst case, consider a function declared that returns any @otype@ (defined \VPageref{otype}): 279 306 \begin{cfa} 280 307 forall( otype T ) T anyObj( void ); 281 308 \end{cfa} 282 As the resolver attempts to satisfy the otype constraint on @T@, a single call to @anyObj()@ without the result type known creates at least as many candidates as the number of complete types currently in scope; with generic types it becomes even worse, for example, assuming a declaration of generic pairis available at that point:309 As the resolver attempts to satisfy the otype constraint on @T@, a call to @anyObj()@ in an expression, without the result type known, creates at least as many candidates as the number of complete types currently in scope; with generic types it becomes even worse, \eg assuming a declaration of a generic @pair@ is available at that point: 283 310 \begin{cfa} 284 311 forall( otype T, otype U ) struct pair { T first; U second; }; 285 312 \end{cfa} 286 Then an @anyObj()@ call can result in arbitrarily complex types, such as @pair( pair( int, int ), pair( int,int ) )@, and the depth can grow indefinitely until the specified parameter depth limit, thus creating exponentially many candidates. However, the expected types allowed by parent expressions are practically very few, so most of those interpretations are invalid; if the result type is never bound up to top level, by the semantic rules it is ambiguous if there are more than one valid bindings, and resolution can fail fast. It is therefore reasonable to delay resolving assertions on an unbound parameter in return type; however, with the current cost model, such behavior may further cause irregularities in candidate selection, such that the presence of assertions can change the preferred candidate, even when order of expression costs are supposed to stay the same. Detailed analysis of this issue will be presented later, in the correctness part.313 Then an @anyObj()@ call can result in arbitrarily complex types, such as @pair( pair( int, int ), pair( int, int ) )@, and the depth can grow indefinitely until a specified parameter-depth limit, thus creating exponentially many candidates. However, the expected types allowed by parent expressions are practically very few, so most of those interpretations are invalid; if the result type is never bound up to the top level, by the semantic rules it is ambiguous if there is more than one valid binding and resolution fails quickly. It is therefore reasonable to delay resolving assertions on an unbound parameter in a return type; however, with the current cost model, such behavior may further cause irregularities in candidate selection, such that the presence of assertions can change the preferred candidate, even when order of expression costs are supposed to stay the same. A detailed analysis of this issue is presented in \VRef{s:AnalysisTypeSystemCorrectness}. 287 314 288 315 … … 290 317 \label{s:TtypeResolutionInfiniteRecursion} 291 318 292 @ttype@ (``tuple type'') is a relatively new addition to the language that attempts to provide type-safe variadic argument semantics. Unlike regular @dtype@ parameters, @ttype@ is only valid in function parameter list, and may only appear once as the type of last parameter. At the call site, a @ttype@ parameter is bound to the tuple type of all remaining functioncall arguments.319 @ttype@ (``tuple type'') is a relatively new addition to the language that attempts to provide type-safe variadic argument semantics. Unlike regular @dtype@ parameters, @ttype@ is only valid in a function parameter-list, and may only appear once as the last parameter type. At the call site, a @ttype@ parameter is bound to the tuple type of all remaining function-call arguments. 293 320 294 321 There are two kinds of idiomatic @ttype@ usage: one is to provide flexible argument forwarding, similar to the variadic template in \CC (\lstinline[language=C++]|template<typename... args>|), as shown below in the implementation of @unique_ptr@ … … 298 325 T * data; 299 326 }; 300 forall( dtype T | sized( T ), ttype Args| { void ?{}( T &, Args ); })301 void ?{}( unique_ptr( T ) & this, Args args) {302 this.data = new( args );303 } 304 \end{cfa} 305 the other is to implement structural recursion in the first-rest manner:306 \begin{cfa} 307 forall( otype T, ttype Params| { void process( T ); void func( Params ); })327 forall( dtype T | sized( T ), @ttype Args@ | { void ?{}( T &, Args ); }) 328 void ?{}( unique_ptr( T ) & this, Args @args@ ) { 329 this.data = new( @args@ ); // forward constructor arguments to dynamic allocator 330 } 331 \end{cfa} 332 The other usage is to implement structural recursion in the first-rest pattern: 333 \begin{cfa} 334 forall( otype T, @ttype Params@ | { void process( T ); void func( Params ); }) 308 335 void func( T arg1, Params p ) { 309 336 process( arg1 ); 310 func( p ); 311 } 312 \end{cfa} 313 For the second use case, it is important that the number of parameters in the recursive call go down, since the call site must deduce all assertion candidates, and that is only possible if by just looking at argument types (and not their values), the recursion is known to be completed in a finite number of steps. 314 315 In recent experiments, however, some flaw in the type binding rules can lead to the first kind of @ttype@ use case produce an invalid candidate that the resolver enters an infinite loop. 316 317 This bug was discovered in an attempt to raise assertion recursive depth limit and one of the library program takes exponentially longer time to compile. The cause of the problem is identified to be the following set of functions. 318 File @memory.cfa@ contains 319 \begin{cfa} 320 #include "memory.hfa" 321 #include "stdlib.hfa" 322 \end{cfa} 323 where file @memory.hfa@ contains the @unique_ptr@ declaration above, and two other similar functions with @ttype@ parameter: 324 \begin{cfa} 325 forall( dtype T | sized( T ), ttype Args | { void ?{}( T &, Args ); }) { 337 func( @p@ ); // recursive call until base case of one argument 338 } 339 \end{cfa} 340 For the second use case, it is imperative the number of parameters in the recursive call goes down, since the call site must deduce all assertion candidates, and that is only possible if by observation of the argument types (and not their values), the recursion is known to be completed in a finite number of steps. 341 342 In recent experiments, however, a flaw in the type-binding rules can lead to the first kind of @ttype@ use case producing an invalid candidate and the resolver enters an infinite loop. 343 This bug was discovered in an attempt to raise the assertion recursive-depth limit and one of the library programs took exponentially longer to compile. The cause of the problem is the following set of functions: 344 \begin{cfa} 345 // unique_ptr declaration from above 346 347 forall( dtype T | sized( T ), ttype Args | { void ?{}( T &, Args ); } ) { // distribute forall clause 326 348 void ?{}( counter_data( T ) & this, Args args ); 327 349 void ?{}( counter_ptr( T ) & this, Args args ); 328 350 void ?{}( unique_ptr( T ) & this, Args args ); 329 351 } 330 \end{cfa} 331 File @stdlib.hfa@ contains 332 \begin{cfa} 352 333 353 forall( dtype T | sized( T ), ttype TT | { void ?{}( T &, TT ); } ) 334 T * new( TT p ) { return &(*malloc()){ p }; } 335 \end{cfa} 336 337 In the expression @(*malloc()){p}@, the type of object being constructed is yet unknown, since the return type information is not immediately provided. That caused every constructor to be searched, and while normally a bound @ttype@ cannot be unified with any free parameter, it is possible with another free @ttype@. Therefore in addition to the correct option provided by assertion, 3 wrong options are examined, each of which again requires the same assertion, for an unknown base type T and @ttype@ arguments, and that becomes an infinite loop, until the specified recursion limit and resolution is forced to fail. Moreover, during the recursion steps, number of candidates grows exponentially, since there are always 3 options at each step. 338 339 Unfortunately, @ttype@ to @ttype@ binding is necessary, to allow calling the function provided by assertion indirectly. 340 \begin{cfa} 341 forall( dtype T | sized( T ), ttype Args | { void ?{}( T &, Args ); }) 342 void ?{}( unique_ptr( T ) & this, Args args ) { this.data = (T * )new( args ); } 343 \end{cfa} 344 Here the constructor assertion is used for the @new( args )@ call. 354 T * new( TT p ) { return @&(*malloc()){ p };@ } 355 \end{cfa} 356 In the expression @(*malloc()){p}@, the type of the object being constructed is unknown, since the return-type information is not immediately available. That causes every constructor to be searched, and while normally a bound @ttype@ cannot be unified with any free parameter, it is possible with another free @ttype@. Therefore, in addition to the correct option provided by the assertion, 3 wrong options are examined, each of which again requires the same assertion, for an unknown base-type @T@ and @ttype@ argument, which becomes an infinite loop until the specified recursion limit and resolution is fails. Moreover, during the recursion steps, the number of candidates grows exponentially, since there are always 3 options at each step. 357 358 Unfortunately, @ttype@ to @ttype@ binding is necessary, to allow indirectly calling a function provided in an assertion. 359 \begin{cfa} 360 forall( dtype T | sized( T ), ttype Args | { @void ?{}( T &, Args );@ }) 361 void ?{}( unique_ptr( T ) & this, Args args ) { this.data = (T *)@new( args )@; } // constructor call 362 \end{cfa} 363 Here the constructor assertion is used by the @new( args )@ call to indirectly call the constructor on the allocated storage. 345 364 Therefore, it is hard, perhaps impossible, to solve this problem by tweaking the type binding rules. An assertion caching algorithm can help improve this case by detecting cycles in recursion. 346 365 347 Meanwhile, without the caching algorithm implemented, some changes in the \CFA source code are enough to eliminate this problem, at least in the current codebase. Note that the issue only happens with an overloaded variadic function, which rarely appears in practice, since the idiomatic use cases are for argument forwarding and self-recursion. The only overloaded @ttype@ function so far discovered in all of \CFA standard library code is the constructor, and by utilizing the argument-dependent lookup process described in Section~\ref{s:UnboundReturnType}, adding a cast before constructor call gets rid of the issue.348 \begin{cfa} 349 T * new( TT p ) { return &(* (T * )malloc()){ p }; }366 Meanwhile, without a caching algorithm implemented, some changes in the \CFA source code are enough to eliminate this problem, at least in the current codebase. Note that the issue only happens with an overloaded variadic function, which rarely appears in practice, since the idiomatic use cases are for argument forwarding and self-recursion. The only overloaded @ttype@ function so far discovered in all of \CFA standard library is the constructor, and by utilizing the argument-dependent lookup process described in \VRef{s:UnboundReturnType}, adding a cast before the constructor call removes the issue. 367 \begin{cfa} 368 T * new( TT p ) { return &(*@(T * )@malloc()){ p }; } 350 369 \end{cfa} 351 370 … … 353 372 \subsection{Reused assertions in nested generic type} 354 373 355 The following test of deeply nested dynamic generic type reveals that locally caching reused assertions is necessary, rather than just a resolver optimization, because recomputing assertions can result in bloated generated code size:374 The following test of deeply nested, dynamic generic type reveals that locally caching reused assertions is necessary, rather than just a resolver optimization, because recomputing assertions can result in bloated generated code size: 356 375 \begin{cfa} 357 376 struct nil {}; … … 361 380 int main() { 362 381 #if N==0 363 nil x;382 nil @x@; 364 383 #elif N==1 365 cons( size_t, nil ) x;384 cons( size_t, nil ) @x@; 366 385 #elif N==2 367 cons( size_t, cons( size_t, nil ) ) x;386 cons( size_t, cons( size_t, nil ) ) @x@; 368 387 #elif N==3 369 cons( size_t, cons( size_t, cons( size_t, nil ) ) ) x;388 cons( size_t, cons( size_t, cons( size_t, nil ) ) ) @x@; 370 389 // similarly for N=4,5,6 371 390 #endif 372 391 } 373 392 \end{cfa} 374 At the declaration of @x@, it is implicitly initialized by generated constructor call, w hose signature is given by393 At the declaration of @x@, it is implicitly initialized by generated constructor call, with signature: 375 394 \begin{cfa} 376 395 forall( otype L, otype R ) void ?{}( cons( L, R ) & ); 377 396 \end{cfa} 378 Note that the @otype@ constraint contains 4 assertions: 397 where the @otype@ constraint contains the 4 assertions:\label{otype} 379 398 \begin{cfa} 380 399 void ?{}( L & ); // default constructor … … 383 402 L & ?=?( L &, L & ); // assignment 384 403 \end{cfa} 385 Now since the right hand side of outermost cons is again a cons, recursive assertions are required. When the compiler cannot cache and reuse already resolved assertions, it becomes a problem, as each of those 4 pending assertions again asks for 4 more assertions one level below. Without any caching, number of resolved assertions grows exponentially, while that is obviously unnecessary since there are only $n+1$ different types involved. Even worse, this causes exponentially many wrapper functions generated later at the codegen step, and results in huge compiled binary. 386 387 \ begin{table}[h]404 405 \begin{table}[htb] 406 \centering 388 407 \caption{Compilation results of nested cons test} 408 \label{t:NestedConsTest} 389 409 \begin{tabular}{|r|r|r|} 390 410 \hline … … 402 422 \end{table} 403 423 404 As the local functions are implemented by emitting executable code on the stack~\cite{gcc-nested-func}, it eventually means that compiled code also has exponential run time. This problem has evident practical implications, as nested collection types are frequently used in real production code. 405 424 Now since the right hand side of outermost cons is again a cons, recursive assertions are required. \VRef[Table]{t:NestedConsTest} shows when the compiler does not cache and reuse already resolved assertions, it becomes a problem, as each of these 4 pending assertions again asks for 4 more assertions one level below. Without caching, the number of resolved assertions grows exponentially, which is unnecessary since there are only $n+1$ different types involved. Even worse, this problem causes exponentially many wrapper functions to be generated at the backend, resulting in a huge binary. As the local functions are implemented by emitting executable code on the stack~\cite{gcc-nested-func}, it means that compiled code also has exponential run time. This problem has practical implications, as nested collection types are frequently used in real production code. 406 425 407 426 \section{Analysis of type system correctness} 427 \label{s:AnalysisTypeSystemCorrectness} 408 428 409 429 In Moss' thesis~\cite[\S~4.1.2,~p.~45]{Moss19}, the author presents the following example: … … 412 432 \begin{cfa} 413 433 void f( int ); 414 double g$ _1$( int );415 int g$ _2$( long );434 double g$\(_1\)$( int ); 435 int g$\(_2\)$( long ); 416 436 f( g( 42 ) ); 417 437 \end{cfa} … … 422 442 From the set of candidates whose parameter and argument types have been unified and whose assertions have been satisfied, those whose sub-expression interpretations have the smallest total cost of conversion are selected ... The total cost of conversion for each of these candidates is then calculated based on the implicit conversions and polymorphism involved in adapting the types of the sub-expression interpretations to the formal parameter types. 423 443 \end{quote} 424 With this model, the algorithm picks @g1@ in resolving the @f( g( 42 ) )@ call, which seems to be undesirable. 425 426 There are further evidence that shows the Bilson model is fundamentally incorrect, following the discussion of unbound return type in Section~\ref{s:UnboundReturnType}. By the conversion cost specification, a binding from a polymorphic type parameter to a concrete type incurs a polymorphic cost of 1. It remains unspecified \emph{when} the type parameters should become bound. When the parameterized types appear in the function parameters, they can be deduced from the argument type, and there is no ambiguity. In the unbound return case, however, the binding may happen at any stage in expression resolution, therefore it is impossible to define a unique local conversion cost. Note that type binding happens exactly once per parameter in resolving the entire expression, so the global binding cost is unambiguously 1. 427 428 As per the current compiler implementation, it does have a notable inconsistency in handling such case. For any unbound parameter that does \emph{not} come with an associated assertion, it remains unbound to the parent expression; for those that does however, they are immediately bound in the assertion resolution step, and concrete result types are used in the parent expressions. 429 444 With this model, the algorithm picks @g1@ in resolving the @f( g( 42 ) )@ call, which is undesirable. 445 446 There is further evidence that shows the Bilson model is fundamentally incorrect, following the discussion of unbound return type in \VRef{s:UnboundReturnType}. By the conversion-cost specification, a binding from a polymorphic type-parameter to a concrete type incurs a polymorphic cost of 1. It remains unspecified \emph{when} the type parameters should become bound. When the parameterized types appear in function parameters, they can be deduced from the argument type, and there is no ambiguity. In the unbound return case, however, the binding may happen at any stage in expression resolution, therefore it is impossible to define a unique local conversion cost. Note that type binding happens exactly once per parameter in resolving the entire expression, so the global binding cost is unambiguously 1. 447 448 In the current compiler implementation, there is a notable inconsistency in handling this case. For any unbound parameter that does \emph{not} come with an associated assertion, it remains unbound to the parent expression; for those that do, however, they are immediately bound in the assertion resolution step, and concrete result types are used in the parent expressions. 430 449 Consider the following example: 431 450 \begin{cfa} … … 433 452 void h( int * ); 434 453 \end{cfa} 435 The expression @h( f() )@ eventually has a total cost of 1 from binding (T: int), but in the eager resolution model, the cost of 1 may occur either atcall to @f@ or at call to @h@, and with the assertion resolution triggering a binding, the local cost of @f()@ is (0 poly, 0 spec) with no assertions, but (1 poly, -1 spec) with an assertion:436 \begin{cfa} 437 forall( dtype T | { void g( T * ); }) T * f( void );454 The expression @h( f() )@ eventually has a total cost of 1 from binding (T: int), but in the eager-resolution model, the cost of 1 may occur either at the call to @f@ or at call to @h@, and with the assertion resolution triggering a binding, the local cost of @f()@ is (0 poly, 0 spec) with no assertions, but (1 poly, -1 spec) with an assertion: 455 \begin{cfa} 456 forall( dtype T | @{ void g( T * ); }@ ) T * f( void ); 438 457 void g( int * ); 439 458 void h( int * ); 440 459 \end{cfa} 441 and that contradicts the principle that adding assertions should make expression cost lower. Furthermore, the time at which type binding and assertion resolution happens is an implementation detail of the compiler, but not a part of language definition. That means two compliant \CFA compilers, one performing immediate assertion resolution at each step, and one delaying assertion resolution on unbound types, can produce different expression costs and therefore different candidate selection, making the language rule itself partially undefined and thereforeunsound. By the above reasoning, the updated cost model using global sum of costs should be accepted as the standard. It also allows the compiler to freely choose when to resolve assertions, as the sum of total costs is independent of that choice; more optimizations regarding assertion resolution can also be implemented.460 and that contradicts the principle that adding assertions should make expression cost lower. Furthermore, the time at which type binding and assertion resolution happens is an implementation detail of the compiler, not part of the language definition. That means two compliant \CFA compilers, one performing immediate assertion resolution at each step, and one delaying assertion resolution on unbound types, can produce different expression costs and therefore different candidate selection, making the language rule itself partially undefined, and therefore, unsound. By the above reasoning, the updated cost model using global sum of costs should be accepted as the standard. It also allows the compiler to freely choose when to resolve assertions, as the sum of total costs is independent of that choice; more optimizations regarding assertion resolution can also be implemented. 442 461 443 462 444 463 \section{Timing results} 445 464 446 For the timing results presented here, the \CFA compiler is built with gcc 9.3.0, and tested on a server machine running Ubuntu 20.04, 64GB RAM and 32-core 2.2 GHz CPU, results reported by the time command, and using only 8 cores in parallel such that the time is close to the case with 100% CPU utilization on a single thread. 447 448 On the most recent build, the \CFA standard library (~1.3 MB of source code) compiles in 4 minutes 47 seconds total processor time (single thread equivalent), with the slowest file taking 13 seconds. The test suite (178 test cases, ~2.2MB of source code) completes within 25 minutes total processor time,\footnote{Including a few runtime tests; total time spent in compilation is approximately 21 minutes.} with the slowest file taking 23 seconds. In contrast, the library build on old compiler takes 85 minutes total, 5 minutes for the slowest file. Full test suite takes too long with old compiler build and is therefore not run, but the slowest test cases take approximately 5 minutes. Overall, the most recent build compared to old build in April 2020, before the project started, is consistently faster by a factor of 20. 449 450 Additionally, 6 selected \CFA source files with distinct features from library and test suite are used to test compiler performance after each of the optimizations are implemented. Test files are from the most recent build and run through C preprocessor to eliminate the factor of header file changes. The selected tests are: 451 \begin{itemize} 452 \item 453 @lib/fstream@ (112 KB)\footnote{File sizes are after preprocessing, with no line information (\lstinline|gcc -E -P|).}: implementation of I/O library 465 For the timing results presented here, the \CFA compiler is built with gcc 9.3.0, and tested on a server machine running Ubuntu 20.04, 64GB RAM and 32-core 2.2 GHz CPU. 466 Timing is reported by the @time@ command and an experiment is run using 8 cores, where each core is at 100\% CPU utilization. 467 468 On the most recent build, the \CFA standard library ($\approx$1.3 MB of source code) compiles in 4 minutes 47 seconds total processor time (single thread equivalent), with the slowest file taking 13 seconds. The test suite (178 test cases, $\approx$2.2MB of source code) completes within 25 minutes total processor time, 469 % PAB: I do not understand this footnote. 470 %\footnote{Including a few runtime tests; total time spent in compilation is approximately 21 minutes.} 471 with the slowest file taking 23 seconds. In contrast, the library build with the old compiler takes 85 minutes total, 5 minutes for the slowest file. The full test-suite takes too long with old compiler build and is therefore not run, but the slowest test cases take approximately 5 minutes. Overall, the most recent build compared to an old build is consistently faster by a factor of 20. 472 473 Additionally, 6 selected \CFA source files with distinct features from the library and test suite are used to illustrate the compiler performance change after each of the implemented optimizations. Test files are from the most recent build and run through the C preprocessor to expand header file, perform macro expansions, but no line number information (@gcc -E -P@). 474 \VRef[Table]{t:SelectedFileByCompilerBuild} shows the selected tests: 475 \begin{itemize} 476 \item 477 @lib/fstream@ (112 KB) 454 478 \item 455 479 @lib/mutex@ (166 KB): implementation of concurrency primitive … … 459 483 @lib/stdlib@ (64 KB): type-safe wrapper to @void *@-based C standard library functions 460 484 \item 461 @test/ ISO2@ (55 KB): application of I/O library485 @test/io2@ (55 KB): application of I/O library 462 486 \item 463 487 @test/thread@ (188 KB): application of threading library 464 488 \end{itemize} 465 466 The \CFA compiler builds are picked from git commit history that passed the test suite, and implement the optimizations incrementally: 467 \begin{itemize} 468 \item 469 \#0 is the first working build of new AST data structure 489 versus \CFA compiler builds picked from the git commit history that implement the optimizations incrementally: 490 \begin{itemize} 491 \item 492 old resolver 493 \item 494 \#0 is the first working build of the new AST data structure 470 495 \item 471 496 \#1 implements special symbol table and argument-dependent lookup 472 497 \item 473 \#2 implements late assertion satisfaction 474 \item 475 \#3 implements revised function type representation 476 \item 477 \#4 skips pruning on expressions with function type (most recent build) 478 \end{itemize} 479 The old resolver with no memory sharing and none of the optimizations above is also tested. 480 \begin{table} 498 \#2 implements late assertion-satisfaction 499 \item 500 \#3 implements revised function-type representation 501 \item 502 \#4 skips pruning on expressions for function types (most recent build) 503 \end{itemize} 504 Reading left to right for a test shows the benefit of each optimization on the cost of compilation. 505 506 \begin{table}[htb] 507 \centering 481 508 \caption{Compile time of selected files by compiler build, in seconds} 509 \label{t:SelectedFileByCompilerBuild} 482 510 \begin{tabular}{|l|r|r|r|r|r|r|} 483 511 \hline … … 502 530 \end{table} 503 531 504 505 532 \section{Conclusion} 506 533 507 Over the course of 8 months of active research and development in \CFA type system and compiler algorithm, performance of the reference \CFA compiler, cfa-cc, has been greatly improved, allowing mid-sized \CFA programs to be compiled and built reasonably fast. As there are also ongoing efforts in the team on building a standard library, evaluating the runtime performance, and attempting to incorporate \CFA with existing software written in C, this project is especially meaningful forpractical purposes.508 509 A nalysis conducted in the project were based significantly on heuristics and practical evidence, as the theoretical bounds and average cases for the expression resolution problem differ. This approach was difficult at start to follow, with an unacceptably slow compiler, since running the program through debugger and validation tools (\eg @gdb@, @valgrind@) adds another order of magnitude to run time, which was already in minutes. However, near the end of the project, many significant improvements have already been made and new optimizations can be tested immediately. The positive feedback in development cycle benefits the \CFA team as a whole, more than just for the compiler optimizations.510 511 Some potential issues of the language that may happen frequently in practice have been identified. Due to the time constraint and complex nature of these problems, a handful of them remain unsolved, but some constructive proposals are made. Notably, introducing a local assertion cache in the resolver is a common solution for a few remaining problems, so that should be the focus of work soon.512 513 The \CFA team are planning on a public alpha release of the language as the compiler performance becomes promising, and other parts of the system, such as a standard library, are also being enhanced. Ideally, the remaining problems should be resolved before release, and the solutions will also be integral to drafting a formal specification.534 Over the course of 8 months of active research and development of the \CFA type system and compiler algorithms, performance of the reference \CFA compiler, cfa-cc, has been greatly improved. Now, mid-sized \CFA programs are compiled reasonably fast. Currently, there are ongoing efforts by the \CFA team to augment the standard library and evaluate its runtime performance, and incorporate \CFA with existing software written in C; therefore this project is especially meaningful for these practical purposes. 535 536 Accomplishing this work was difficult. Analysis conducted in the project is based significantly on heuristics and practical evidence, as the theoretical bounds and average cases for the expression resolution problem differ. As well, the slowness of the initial compiler made attempts to understand why and where problems exist extremely difficult because both debugging and validation tools (\eg @gdb@, @valgrind@, @pref@) further slowed down compilation time. However, by the end of the project, I had found and fixed several significant problems and new optimizations are easier to introduce and test. The reduction in the development cycle benefits the \CFA team as a whole. 537 538 Some potential issues of the language, which happen frequently in practice, have been identified. Due to the time constraint and complex nature of these problems, a handful of them remain unsolved, but some constructive proposals are made. Notably, introducing a local assertion cache in the resolver is a reasonable solution for a few remaining problems, so that should be the focus of future work. 539 540 The \CFA team are planning on a public alpha release of the language as the compiler performance, given my recent improvements, is now useable. Other parts of the system, such as the standard library, have made significant gains due to the speed up in the development cycle. Ideally, the remaining problems should be resolved before release, and the solutions will also be integral to drafting a formal specification. 514 541 515 542 \addcontentsline{toc}{section}{\refname} -
doc/theses/fangren_yu_COOP_S20/Report.tex
r342af53 r8e4aa05 17 17 \usepackage[usenames]{color} 18 18 \input{common} % common CFA document macros 19 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true, pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref}19 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref} 20 20 \usepackage{breakurl} 21 21 \urlstyle{sf} -
doc/theses/thierry_delisle_PhD/thesis/Makefile
r342af53 r8e4aa05 8 8 BibTeX = BIBINPUTS=${TeXLIB} && export BIBINPUTS && bibtex 9 9 10 MAKEFLAGS = --no-print-directory --silent10 MAKEFLAGS = --no-print-directory # --silent 11 11 VPATH = ${Build} ${Figures} 12 12 … … 32 32 emptytree \ 33 33 fairness \ 34 io_uring \ 35 pivot_ring \ 34 36 system \ 35 37 } … … 43 45 ## Define the documents that need to be made. 44 46 all: thesis.pdf 45 thesis.pdf: ${TEXTS} ${FIGURES} ${PICTURES} glossary.tex local.bib47 thesis.pdf: ${TEXTS} ${FIGURES} ${PICTURES} thesis.tex glossary.tex local.bib 46 48 47 49 DOCUMENT = thesis.pdf … … 49 51 50 52 # Directives # 53 54 .NOTPARALLEL: # cannot make in parallel 51 55 52 56 .PHONY : all clean # not file names … … 81 85 ${LaTeX} $< 82 86 83 build/fairness.svg : fig/fairness.py | ${Build}84 python3 $< $@85 86 87 ## Define the default recipes. 87 88 … … 105 106 sed -i 's/$@/${Build}\/$@/g' ${Build}/$@_t 106 107 108 build/fairness.svg : fig/fairness.py | ${Build} 109 python3 $< $@ 110 107 111 ## pstex with inverted colors 108 112 %.dark.pstex : fig/%.fig Makefile | ${Build} -
doc/theses/thierry_delisle_PhD/thesis/local.bib
r342af53 r8e4aa05 512 512 } 513 513 514 @manual{MAN:bsd/kqueue, 515 title = {KQUEUE(2) - FreeBSD System Calls Manual}, 516 url = {https://www.freebsd.org/cgi/man.cgi?query=kqueue}, 517 year = {2020}, 518 month = {may} 519 } 520 514 521 % Apple's MAC OS X 515 522 @manual{MAN:apple/scheduler, … … 577 584 578 585 % -------------------------------------------------- 586 % Man Pages 587 @manual{MAN:open, 588 key = "open", 589 title = "open(2) Linux User's Manual", 590 year = "2020", 591 month = "February", 592 } 593 594 @manual{MAN:accept, 595 key = "accept", 596 title = "accept(2) Linux User's Manual", 597 year = "2019", 598 month = "March", 599 } 600 601 @manual{MAN:select, 602 key = "select", 603 title = "select(2) Linux User's Manual", 604 year = "2019", 605 month = "March", 606 } 607 608 @manual{MAN:poll, 609 key = "poll", 610 title = "poll(2) Linux User's Manual", 611 year = "2019", 612 month = "July", 613 } 614 615 @manual{MAN:epoll, 616 key = "epoll", 617 title = "epoll(7) Linux User's Manual", 618 year = "2019", 619 month = "March", 620 } 621 622 @manual{MAN:aio, 623 key = "aio", 624 title = "aio(7) Linux User's Manual", 625 year = "2019", 626 month = "March", 627 } 628 629 @misc{MAN:io_uring, 630 title = {Efficient IO with io\_uring}, 631 author = {Axboe, Jens}, 632 year = "2019", 633 month = "March", 634 version = {0,4}, 635 howpublished = {\url{https://kernel.dk/io_uring.pdf}} 636 } 637 638 % -------------------------------------------------- 579 639 % Wikipedia Entries 580 640 @misc{wiki:taskparallel, … … 617 677 note = "[Online; accessed 2-January-2021]" 618 678 } 679 680 @misc{wiki:future, 681 author = "{Wikipedia contributors}", 682 title = "Futures and promises --- {W}ikipedia{,} The Free Encyclopedia", 683 year = "2020", 684 url = "https://en.wikipedia.org/wiki/Futures_and_promises", 685 note = "[Online; accessed 9-February-2021]" 686 } -
doc/theses/thierry_delisle_PhD/thesis/text/core.tex
r342af53 r8e4aa05 49 49 50 50 \section{Design} 51 In general, a na\"{i}ve \glsxtrshort{fifo} ready-queue does not scale with increased parallelism from \glspl{hthrd}, resulting in decreased performance. The problem is adding/removing \glspl{thrd} is a single point of contention. As shown in the evaluation sections, most production schedulers do scale when adding \glspl{hthrd}. The common solution to the single point of contention is to shard the ready-queue so each \gls{hthrd} can access the ready-queue without contention, increasing performance though lack of contention.51 In general, a na\"{i}ve \glsxtrshort{fifo} ready-queue does not scale with increased parallelism from \glspl{hthrd}, resulting in decreased performance. The problem is adding/removing \glspl{thrd} is a single point of contention. As shown in the evaluation sections, most production schedulers do scale when adding \glspl{hthrd}. The common solution to the single point of contention is to shard the ready-queue so each \gls{hthrd} can access the ready-queue without contention, increasing performance. 52 52 53 53 \subsection{Sharding} \label{sec:sharding} 54 An interesting approach to sharding a queue is presented in \cit{Trevors paper}. This algorithm presents a queue with a relaxed \glsxtrshort{fifo} guarantee using an array of strictly \glsxtrshort{fifo} sublists as shown in Figure~\ref{fig:base}. Each \emph{cell} of the array has a timestamp for the last operation and a pointer to a linked-list with a lock and each node in the list is marked with a timestamp indicating when it is added to the list. A push operation is done by picking a random cell, acquiring the list lock, and pushing to the list. If the cell is locked, the operation is simply retried on another random cell until a lock is acquired. A pop operation is done in a similar fashion except two random cells are picked. If both cells are unlocked with non-empty lists, the operation pops the node with the oldest celltimestamp. If one of the cells is unlocked and non-empty, the operation pops from that cell. If both cells are either locked or empty, the operation picks two new random cells and tries again.54 An interesting approach to sharding a queue is presented in \cit{Trevors paper}. This algorithm presents a queue with a relaxed \glsxtrshort{fifo} guarantee using an array of strictly \glsxtrshort{fifo} sublists as shown in Figure~\ref{fig:base}. Each \emph{cell} of the array has a timestamp for the last operation and a pointer to a linked-list with a lock. Each node in the list is marked with a timestamp indicating when it is added to the list. A push operation is done by picking a random cell, acquiring the list lock, and pushing to the list. If the cell is locked, the operation is simply retried on another random cell until a lock is acquired. A pop operation is done in a similar fashion except two random cells are picked. If both cells are unlocked with non-empty lists, the operation pops the node with the oldest timestamp. If one of the cells is unlocked and non-empty, the operation pops from that cell. If both cells are either locked or empty, the operation picks two new random cells and tries again. 55 55 56 56 \begin{figure} … … 100 100 \paragraph{Local Information} Figure~\ref{fig:emptytls} shows an approach using dense information, similar to the bitmap, but each \gls{hthrd} keeps its own independent copy. While this approach can offer good scalability \emph{and} low latency, the liveliness and discovery of the information can become a problem. This case is made worst in systems with few processors where even blind random picks can find \glspl{thrd} in a few tries. 101 101 102 I built a prototype of these approaches and none of these techniques offer satisfying performance when few threads are present. All of these approach hit the same 2 problems. First, randomly picking sub-queues is very fast but means any improvement to the hit rate can easily be countered by a slow-down in look-up speed when there are empty lists. Second, the array is already assharded to avoid contention bottlenecks, so any denser data structure tends to become a bottleneck. In all cases, these factors meant the best cases scenario, \ie many threads, would get worst throughput, and the worst-case scenario, few threads, would get a better hit rate, but an equivalent poor throughput. As a result I tried an entirely different approach.102 I built a prototype of these approaches and none of these techniques offer satisfying performance when few threads are present. All of these approach hit the same 2 problems. First, randomly picking sub-queues is very fast. That speed means any improvement to the hit rate can easily be countered by a slow-down in look-up speed, whether or not there are empty lists. Second, the array is already sharded to avoid contention bottlenecks, so any denser data structure tends to become a bottleneck. In all cases, these factors meant the best cases scenario, \ie many threads, would get worst throughput, and the worst-case scenario, few threads, would get a better hit rate, but an equivalent poor throughput. As a result I tried an entirely different approach. 103 103 104 104 \subsection{Dynamic Entropy}\cit{https://xkcd.com/2318/} 105 In the worst-case scenario there are only few \glspl{thrd} ready to run, or more precisely given $P$ \glspl{proc}\footnote{For simplicity, this assumes there is a one-to-one match between \glspl{proc} and \glspl{hthrd}.}, $T$ \glspl{thrd} and $\epsilon$ a very small number, than the worst case scenario can be represented by $ \epsilon \ll P$, than $T = P + \epsilon$. It is important to note in this case that fairness is effectively irrelevant. Indeed, this case is close to \emph{actually matching} the model of the ``Ideal multi-tasking CPU'' on page \pageref{q:LinuxCFS}. In this context, it is possible to use a purely internal-locality based approach and still meet the fairness requirements. This approach simply has each \gls{proc} running a single \gls{thrd} repeatedly. Or from the shared ready-queue viewpoint, each \gls{proc} pushes to a given sub-queue and then popes from the \emph{same} subqueue. In cases where $T \gg P$, the scheduler should also achieves similar performance without affecting the fairness guarantees.105 In the worst-case scenario there are only few \glspl{thrd} ready to run, or more precisely given $P$ \glspl{proc}\footnote{For simplicity, this assumes there is a one-to-one match between \glspl{proc} and \glspl{hthrd}.}, $T$ \glspl{thrd} and $\epsilon$ a very small number, than the worst case scenario can be represented by $T = P + \epsilon$, with $\epsilon \ll P$. It is important to note in this case that fairness is effectively irrelevant. Indeed, this case is close to \emph{actually matching} the model of the ``Ideal multi-tasking CPU'' on page \pageref{q:LinuxCFS}. In this context, it is possible to use a purely internal-locality based approach and still meet the fairness requirements. This approach simply has each \gls{proc} running a single \gls{thrd} repeatedly. Or from the shared ready-queue viewpoint, each \gls{proc} pushes to a given sub-queue and then pops from the \emph{same} subqueue. The challenge is for the the scheduler to achieve good performance in both the $T = P + \epsilon$ case and the $T \gg P$ case, without affecting the fairness guarantees in the later. 106 106 107 To handle this case, I use a pseudo random-number generator, \glsxtrshort{prng} in a novel way. When the scheduler uses a \glsxtrshort{prng} instance per \gls{proc} exclusively, the random-number seed effectively starts an encoding that produces a list of all accessed subqueues, from latest to oldest. The novel approach is to be able to ``replay'' the \glsxtrshort{prng} backwards and there exist \glsxtrshort{prng}s that are fast, compact \emph{and} can be run forward and backwards. Linear congruential generators~\cite{wiki:lcg} are an example of \glsxtrshort{prng}s that match these requirements.107 To handle this case, I use a \glsxtrshort{prng}\todo{Fix missing long form} in a novel way. There exist \glsxtrshort{prng}s that are fast, compact and can be run forward \emph{and} backwards. Linear congruential generators~\cite{wiki:lcg} are an example of \glsxtrshort{prng}s of such \glsxtrshort{prng}s. The novel approach is to use the ability to run backwards to ``replay'' the \glsxtrshort{prng}. The scheduler uses an exclusive \glsxtrshort{prng} instance per \gls{proc}, the random-number seed effectively starts an encoding that produces a list of all accessed subqueues, from latest to oldest. Replaying the \glsxtrshort{prng} to identify cells accessed recently and which probably have data still cached. 108 108 109 109 The algorithm works as follows: -
doc/theses/thierry_delisle_PhD/thesis/text/intro.tex
r342af53 r8e4aa05 7 7 While previous work on the concurrent package of \CFA focused on features and interfaces, this thesis focuses on performance, introducing \glsxtrshort{api} changes only when required by performance considerations. More specifically, this thesis concentrates on scheduling and \glsxtrshort{io}. Prior to this work, the \CFA runtime used a strictly \glsxtrshort{fifo} \gls{rQ}. 8 8 9 This work exclusively concentrates on Linux as it's operating system since the existing \CFA runtime and compiler does not already support other operating systems. Furthermore, as \CFA is yet to be released, supporting version of Linux older tha tthe latest version is not a goal of this work.9 This work exclusively concentrates on Linux as it's operating system since the existing \CFA runtime and compiler does not already support other operating systems. Furthermore, as \CFA is yet to be released, supporting version of Linux older than the latest version is not a goal of this work. -
doc/theses/thierry_delisle_PhD/thesis/text/io.tex
r342af53 r8e4aa05 1 \chapter{User Level \ glsxtrshort{io}}2 As mention ned in Section~\ref{prev:io}, User-Level \glsxtrshort{io} requires multiplexing the \glsxtrshort{io} operations of many \glspl{thrd} onto fewer \glspl{proc} using asynchronous \glsxtrshort{io} operations. Various operating systems offer various forms of asynchronous operations and as mentioned in Chapter~\ref{intro}, this work is exclusively focuesd on Linux.1 \chapter{User Level \io} 2 As mentioned in Section~\ref{prev:io}, User-Level \io requires multiplexing the \io operations of many \glspl{thrd} onto fewer \glspl{proc} using asynchronous \io operations. Different operating systems offer various forms of asynchronous operations and as mentioned in Chapter~\ref{intro}, this work is exclusively focused on the Linux operating-system. 3 3 4 \section{ Existing options}5 Since \glsxtrshort{io} operations are generally handled by the4 \section{Kernel Interface} 5 Since this work fundamentally depends on operating-system support, the first step of any design is to discuss the available interfaces and pick one (or more) as the foundations of the non-blocking \io subsystem. 6 6 7 \subsection{\lstinline|epoll|, \lstinline|poll| and \lstinline|select|} 7 \subsection{\lstinline{O_NONBLOCK}} 8 In Linux, files can be opened with the flag @O_NONBLOCK@~\cite{MAN:open} (or @SO_NONBLOCK@~\cite{MAN:accept}, the equivalent for sockets) to use the file descriptors in ``nonblocking mode''. In this mode, ``Neither the @open()@ nor any subsequent \io operations on the [opened file descriptor] will cause the calling 9 process to wait''~\cite{MAN:open}. This feature can be used as the foundation for the non-blocking \io subsystem. However, for the subsystem to know when an \io operation completes, @O_NONBLOCK@ must be use in conjunction with a system call that monitors when a file descriptor becomes ready, \ie, the next \io operation on it does not cause the process to wait\footnote{In this context, ready means \emph{some} operation can be performed without blocking. It does not mean an operation returning \lstinline{EAGAIN} succeeds on the next try. For example, a ready read may only return a subset of bytes and the read must be issues again for the remaining bytes, at which point it may return \lstinline{EAGAIN}.}. 10 This mechanism is also crucial in determining when all \glspl{thrd} are blocked and the application \glspl{kthrd} can now block. 8 11 9 \subsection{Linux's AIO} 12 There are three options to monitor file descriptors in Linux\footnote{For simplicity, this section omits \lstinline{pselect} and \lstinline{ppoll}. The difference between these system calls and \lstinline{select} and \lstinline{poll}, respectively, is not relevant for this discussion.}, @select@~\cite{MAN:select}, @poll@~\cite{MAN:poll} and @epoll@~\cite{MAN:epoll}. All three of these options offer a system call that blocks a \gls{kthrd} until at least one of many file descriptors becomes ready. The group of file descriptors being waited is called the \newterm{interest set}. 10 13 14 \paragraph{\lstinline{select}} is the oldest of these options, it takes as an input a contiguous array of bits, where each bits represent a file descriptor of interest. On return, it modifies the set in place to identify which of the file descriptors changed status. This destructive change means that calling select in a loop requires re-initializing the array each time and the number of file descriptors supported has a hard limit. Another limit of @select@ is that once the call is started, the interest set can no longer be modified. Monitoring a new file descriptor generally requires aborting any in progress call to @select@\footnote{Starting a new call to \lstinline{select} is possible but requires a distinct kernel thread, and as a result is not an acceptable multiplexing solution when the interest set is large and highly dynamic unless the number of parallel calls to \lstinline{select} can be strictly bounded.}. 11 15 16 \paragraph{\lstinline{poll}} is an improvement over select, which removes the hard limit on the number of file descriptors and the need to re-initialize the input on every call. It works using an array of structures as an input rather than an array of bits, thus allowing a more compact input for small interest sets. Like @select@, @poll@ suffers from the limitation that the interest set cannot be changed while the call is blocked. 17 18 \paragraph{\lstinline{epoll}} further improves these two functions by allowing the interest set to be dynamically added to and removed from while a \gls{kthrd} is blocked on an @epoll@ call. This dynamic capability is accomplished by creating an \emph{epoll instance} with a persistent interest set, which is used across multiple calls. This capability significantly reduces synchronization overhead on the part of the caller (in this case the \io subsystem), since the interest set can be modified when adding or removing file descriptors without having to synchronize with other \glspl{kthrd} potentially calling @epoll@. 19 20 However, all three of these system calls have limitations. The @man@ page for @O_NONBLOCK@ mentions that ``[@O_NONBLOCK@] has no effect for regular files and block devices'', which means none of these three system calls are viable multiplexing strategies for these types of \io operations. Furthermore, @epoll@ has been shown to have problems with pipes and ttys~\cit{Peter's examples in some fashion}. Finally, none of these are useful solutions for multiplexing \io operations that do not have a corresponding file descriptor and can be awkward for operations using multiple file descriptors. 21 22 \subsection{POSIX asynchronous I/O (AIO)} 23 An alternative to @O_NONBLOCK@ is the AIO interface. Its interface lets programmers enqueue operations to be performed asynchronously by the kernel. Completions of these operations can be communicated in various ways: either by spawning a new \gls{kthrd}, sending a Linux signal, or by polling for completion of one or more operation. For this work, spawning a new \gls{kthrd} is counter-productive but a related solution is discussed in Section~\ref{io:morethreads}. Using interrupts handlers can also lead to fairly complicated interactions between subsystems. Leaving polling for completion, which is similar to the previous system calls. While AIO only supports read and write operations to file descriptors, it does not have the same limitation as @O_NONBLOCK@, \ie, the file descriptors can be regular files and blocked devices. It also supports batching multiple operations in a single system call. 24 25 AIO offers two different approach to polling: @aio_error@ can be used as a spinning form of polling, returning @EINPROGRESS@ until the operation is completed, and @aio_suspend@ can be used similarly to @select@, @poll@ or @epoll@, to wait until one or more requests have completed. For the purpose of \io multiplexing, @aio_suspend@ is the best interface. However, even if AIO requests can be submitted concurrently, @aio_suspend@ suffers from the same limitation as @select@ and @poll@, \ie, the interest set cannot be dynamically changed while a call to @aio_suspend@ is in progress. AIO also suffers from the limitation of specifying which requests have completed, \ie programmers have to poll each request in the interest set using @aio_error@ to identify the completed requests. This limitation means that, like @select@ and @poll@ but not @epoll@, the time needed to examine polling results increases based on the total number of requests monitored, not the number of completed requests. 26 Finally, AIO does not seem to be a popular interface, which I believe is due in part to this poor polling interface. Linus Torvalds talks about this interface as follows: 12 27 13 28 \begin{displayquote} 14 AIO is a horrible ad-hoc design, with the main excuse being "other,29 AIO is a horrible ad-hoc design, with the main excuse being ``other, 15 30 less gifted people, made that design, and we are implementing it for 16 31 compatibility because database people - who seldom have any shred of 17 taste - actually use it ".32 taste - actually use it''. 18 33 19 34 But AIO was always really really ugly. … … 24 39 \end{displayquote} 25 40 26 Interestingly, in this e-mail answer, Linus goes on to describe41 Interestingly, in this e-mail, Linus goes on to describe 27 42 ``a true \textit{asynchronous system call} interface'' 28 43 that does … … 30 45 in 31 46 ``some kind of arbitrary \textit{queue up asynchronous system call} model''. 32 This description is actually quite close to the interface of the interfacedescribed in the next section.47 This description is actually quite close to the interface described in the next section. 33 48 34 \subsection{\texttt{io\_uring}} 35 A very recent addition to Linux, @io_uring@\cit{io\_uring} is a framework that aims to solve many of the problems listed with the above mentioned solutions. 49 \subsection{\lstinline{io_uring}} 50 A very recent addition to Linux, @io_uring@~\cite{MAN:io_uring}, is a framework that aims to solve many of the problems listed in the above interfaces. Like AIO, it represents \io operations as entries added to a queue. But like @epoll@, new requests can be submitted while a blocking call waiting for requests to complete is already in progress. The @io_uring@ interface uses two ring buffers (referred to simply as rings) at its core: a submit ring to which programmers push \io requests and a completion ring from which programmers poll for completion. 51 52 One of the big advantages over the prior interfaces is that @io_uring@ also supports a much wider range of operations. In addition to supporting reads and writes to any file descriptor like AIO, it supports other operations like @open@, @close@, @fsync@, @accept@, @connect@, @send@, @recv@, @splice@, \etc. 53 54 On top of these, @io_uring@ adds many extras like avoiding copies between the kernel and user-space using shared memory, allowing different mechanisms to communicate with device drivers, and supporting chains of requests, \ie, requests that automatically trigger followup requests on completion. 36 55 37 56 \subsection{Extra Kernel Threads}\label{io:morethreads} 38 Finally, if the operating system does not offer a ny satisfying forms of asynchronous \glsxtrshort{io} operations, a solution is to fake it by creating a pool of \glspl{kthrd} and delegating operations to them in order to avoid blocking \glspl{proc}.57 Finally, if the operating system does not offer a satisfactory form of asynchronous \io operations, an ad-hoc solution is to create a pool of \glspl{kthrd} and delegate operations to it to avoid blocking \glspl{proc}, which is a compromise for multiplexing. In the worst case, where all \glspl{thrd} are consistently blocking on \io, it devolves into 1-to-1 threading. However, regardless of the frequency of \io operations, it achieves the fundamental goal of not blocking \glspl{proc} when \glspl{thrd} are ready to run. This approach is used by languages like Go\cit{Go} and frameworks like libuv\cit{libuv}, since it has the advantage that it can easily be used across multiple operating systems. This advantage is especially relevant for languages like Go, which offer a homogeneous \glsxtrshort{api} across all platforms. As opposed to C, which has a very limited standard api for \io, \eg, the C standard library has no networking. 39 58 40 59 \subsection{Discussion} 60 These options effectively fall into two broad camps: waiting for \io to be ready versus waiting for \io to complete. All operating systems that support asynchronous \io must offer an interface along one of these lines, but the details vary drastically. For example, Free BSD offers @kqueue@~\cite{MAN:bsd/kqueue}, which behaves similarly to @epoll@, but with some small quality of use improvements, while Windows (Win32)~\cit{https://docs.microsoft.com/en-us/windows/win32/fileio/synchronous-and-asynchronous-i-o} offers ``overlapped I/O'', which handles submissions similarly to @O_NONBLOCK@ with extra flags on the synchronous system call, but waits for completion events, similarly to @io_uring@. 41 61 62 For this project, I selected @io_uring@, in large parts because to its generality. While @epoll@ has been shown to be a good solution for socket \io (\cite{DBLP:journals/pomacs/KarstenB20}), @io_uring@'s transparent support for files, pipes, and more complex operations, like @splice@ and @tee@, make it a better choice as the foundation for a general \io subsystem. 42 63 43 64 \section{Event-Engine} 65 An event engine's responsibility is to use the kernel interface to multiplex many \io operations onto few \glspl{kthrd}. In concrete terms, this means \glspl{thrd} enter the engine through an interface, the event engines then starts the operation and parks the calling \glspl{thrd}, returning control to the \gls{proc}. The parked \glspl{thrd} are then rescheduled by the event engine once the desired operation has completed. 66 67 \subsection{\lstinline{io_uring} in depth} 68 Before going into details on the design of my event engine, more details on @io_uring@ usage are presented, each important in the design of the engine. 69 Figure~\ref{fig:iouring} shows an overview of an @io_uring@ instance. 70 Two ring buffers are used to communicate with the kernel: one for submissions~(left) and one for completions~(right). 71 The submission ring contains entries, \newterm{Submit Queue Entries} (SQE), produced (appended) by the application when an operation starts and then consumed by the kernel. 72 The completion ring contains entries, \newterm{Completion Queue Entries} (CQE), produced (appended) by the kernel when an operation completes and then consumed by the application. 73 The submission ring contains indexes into the SQE array (denoted \emph{S}) containing entries describing the I/O operation to start; 74 the completion ring contains entries for the completed I/O operation. 75 Multiple @io_uring@ instances can be created, in which case they each have a copy of the data structures in the figure. 76 77 \begin{figure} 78 \centering 79 \input{io_uring.pstex_t} 80 \caption{Overview of \lstinline{io_uring}} 81 % \caption[Overview of \lstinline{io_uring}]{Overview of \lstinline{io_uring} \smallskip\newline Two ring buffer are used to communicate with the kernel, one for completions~(right) and one for submissions~(left). The completion ring contains entries, \newterm{CQE}s: Completion Queue Entries, that are produced by the kernel when an operation completes and then consumed by the application. On the other hand, the application produces \newterm{SQE}s: Submit Queue Entries, which it appends to the submission ring for the kernel to consume. Unlike the completion ring, the submission ring does not contain the entries directly, it indexes into the SQE array (denoted \emph{S}) instead.} 82 \label{fig:iouring} 83 \end{figure} 84 85 New \io operations are submitted to the kernel following 4 steps, which use the components shown in the figure. 86 \begin{enumerate} 87 \item 88 An SQE is allocated from the pre-allocated array (denoted \emph{S} in Figure~\ref{fig:iouring}). This array is created at the same time as the @io_uring@ instance, is in kernel-locked memory visible by both the kernel and the application, and has a fixed size determined at creation. How these entries are allocated is not important for the functioning of @io_uring@, the only requirement is that no entry is reused before the kernel has consumed it. 89 \item 90 The SQE is filled according to the desired operation. This step is straight forward, the only detail worth mentioning is that SQEs have a @user_data@ field that must be filled in order to match submission and completion entries. 91 \item 92 The SQE is submitted to the submission ring by appending the index of the SQE to the ring following regular ring buffer steps: \lstinline{buffer[head] = item; head++}. Since the head is visible to the kernel, some memory barriers may be required to prevent the compiler from reordering these operations. Since the submission ring is a regular ring buffer, more than one SQE can be added at once and the head is updated only after all entries are updated. 93 \item 94 The kernel is notified of the change to the ring using the system call @io_uring_enter@. The number of elements appended to the submission ring is passed as a parameter and the number of elements consumed is returned. The @io_uring@ instance can be constructed so this step is not required, but this requires elevated privilege.% and an early version of @io_uring@ had additional restrictions. 95 \end{enumerate} 96 97 \begin{sloppypar} 98 The completion side is simpler: applications call @io_uring_enter@ with the flag @IORING_ENTER_GETEVENTS@ to wait on a desired number of operations to complete. The same call can be used to both submit SQEs and wait for operations to complete. When operations do complete, the kernel appends a CQE to the completion ring and advances the head of the ring. Each CQE contains the result of the operation as well as a copy of the @user_data@ field of the SQE that triggered the operation. It is not necessary to call @io_uring_enter@ to get new events because the kernel can directly modify the completion ring. The system call is only needed if the application wants to block waiting for operations to complete. 99 \end{sloppypar} 100 101 The @io_uring_enter@ system call is protected by a lock inside the kernel. This protection means that concurrent call to @io_uring_enter@ using the same instance are possible, but there is no performance gained from parallel calls to @io_uring_enter@. It is possible to do the first three submission steps in parallel, however, doing so requires careful synchronization. 102 103 @io_uring@ also introduces constraints on the number of simultaneous operations that can be ``in flight''. Obviously, SQEs are allocated from a fixed-size array, meaning that there is a hard limit to how many SQEs can be submitted at once. In addition, the @io_uring_enter@ system call can fail because ``The kernel [...] ran out of resources to handle [a request]'' or ``The application is attempting to overcommit the number of requests it can have pending.''. This restriction means \io request bursts may have to be subdivided and submitted in chunks at a later time. 104 105 \subsection{Multiplexing \io: Submission} 106 The submission side is the most complicated aspect of @io_uring@ and its design largely dictates the completion side. 107 108 While it is possible to do the first steps of submission in parallel, the duration of the system call scales with number of entries submitted. The consequence is that the amount of parallelism used to prepare submissions for the next system call is limited. Beyond this limit, the length of the system call is the throughput limiting factor. I concluded from early experiments that preparing submissions seems to take about as long as the system call itself, which means that with a single @io_uring@ instance, there is no benefit in terms of \io throughput to having more than two \glspl{hthrd}. Therefore the design of the submission engine must manage multiple instances of @io_uring@ running in parallel, effectively sharding @io_uring@ instances. Similarly to scheduling, this sharding can be done privately, \ie, one instance per \glspl{proc}, or in decoupled pools, \ie, a pool of \glspl{proc} use a pool of @io_uring@ instances without one-to-one coupling between any given instance and any given \gls{proc}. 109 110 \subsubsection{Pool of Instances} 111 One approach is to have multiple shared instances. \Glspl{thrd} attempting \io operations pick one of the available instances and submits operations to that instance. Since the completion will be sent to the same instance, all instances with pending operations must be polled continuously\footnote{As will be described in Chapter~\ref{practice}, this does not translate into constant CPU usage.}. Since there is no coupling between \glspl{proc} and @io_uring@ instances in this approach, \glspl{thrd} running on more than one \gls{proc} can attempt to submit to the same instance concurrently. Since @io_uring@ effectively sets the amount of sharding needed to avoid contention on its internal locks, performance in this approach is based on two aspects: the synchronization needed to submit does not induce more contention than @io_uring@ already does and the scheme to route \io requests to specific @io_uring@ instances does not introduce contention. This second aspect has an oversized importance because it comes into play before the sharding of instances, and as such, all \glspl{hthrd} can contend on the routing algorithm. 112 113 Allocation in this scheme can be handled fairly easily. Free SQEs, \ie, SQEs that aren't currently being used to represent a request, can be written to safely and have a field called @user_data@ which the kernel only reads to copy to CQEs. Allocation also requires no ordering guarantee as all free SQEs are interchangeable. This requires a simple concurrent bag. The only added complexity is that the number of SQEs is fixed, which means allocation can fail. This failure needs to be pushed up to the routing algorithm, \glspl{thrd} attempting \io operations must not be directed to @io_uring@ instances without any available SQEs. Ideally, the routing algorithm would block operations up-front if none of the instances have available SQEs. 114 115 Once an SQE is allocated, \glspl{thrd} can fill them normally, they simply need to keep track of the SQE index and which instance it belongs to. 116 117 Once an SQE is filled in, what needs to happen is that the SQE must be added to the submission ring buffer, an operation that is not thread-safe on itself, and the kernel must be notified using the @io_uring_enter@ system call. The submission ring buffer is the same size as the pre-allocated SQE buffer, therefore pushing to the ring buffer cannot fail\footnote{This is because it is invalid to have the same \lstinline{sqe} multiple times in the ring buffer.}. However, as mentioned, the system call itself can fail with the expectation that it will be retried once some of the already submitted operations complete. Since multiple SQEs can be submitted to the kernel at once, it is important to strike a balance between batching and latency. Operations that are ready to be submitted should be batched together in few system calls, but at the same time, operations should not be left pending for long period of times before being submitted. This can be handled by either designating one of the submitting \glspl{thrd} as the being responsible for the system call for the current batch of SQEs or by having some other party regularly submitting all ready SQEs, \eg, the poller \gls{thrd} mentioned later in this section. 118 119 In the case of designating a \gls{thrd}, ideally, when multiple \glspl{thrd} attempt to submit operations to the same @io_uring@ instance, all requests would be batched together and one of the \glspl{thrd} would do the system call on behalf of the others, referred to as the \newterm{submitter}. In practice however, it is important that the \io requests are not left pending indefinitely and as such, it may be required to have a current submitter and a next submitter. Indeed, as long as there is a ``next'' submitter, \glspl{thrd} submitting new \io requests can move on, knowing that some future system call will include their request. Once the system call is done, the submitter must also free SQEs so that the allocator can reused them. 120 121 Finally, the completion side is much simpler since the @io_uring@ system call enforces a natural synchronization point. Polling simply needs to regularly do the system call, go through the produced CQEs and communicate the result back to the originating \glspl{thrd}. Since CQEs only own a signed 32 bit result, in addition to the copy of the @user_data@ field, all that is needed to communicate the result is a simple future~\cite{wiki:future}. If the submission side does not designate submitters, polling can also submit all SQEs as it is polling events. A simple approach to polling is to allocate a \gls{thrd} per @io_uring@ instance and simply let the poller \glspl{thrd} poll their respective instances when scheduled. This design is especially convenient for reasons explained in Chapter~\ref{practice}. 122 123 With this pool of instances approach, the big advantage is that it is fairly flexible. It does not impose restrictions on what \glspl{thrd} submitting \io operations can and cannot do between allocations and submissions. It also can gracefully handle running out of resources, SQEs or the kernel returning @EBUSY@. The down side to this is that many of the steps used for submitting need complex synchronization to work properly. The routing and allocation algorithm needs to keep track of which ring instances have available SQEs, block incoming requests if no instance is available, prevent barging if \glspl{thrd} are already queued up waiting for SQEs and handle SQEs being freed. The submission side needs to safely append SQEs to the ring buffer, make sure no SQE is dropped or left pending forever, notify the allocation side when SQEs can be reused and handle the kernel returning @EBUSY@. Sharding the @io_uring@ instances should alleviate much of the contention caused by this, but all this synchronization may still have non-zero cost. 124 125 \subsubsection{Private Instances} 126 Another approach is to simply create one ring instance per \gls{proc}. This alleviate the need for synchronization on the submissions, requiring only that \glspl{thrd} are not interrupted in between two submission steps. This is effectively the same requirement as using @thread_local@ variables. Since SQEs that are allocated must be submitted to the same ring, on the same \gls{proc}, this effectively forces the application to submit SQEs in allocation order\footnote{The actual requirement is that \glspl{thrd} cannot context switch between allocation and submission. This requirement means that from the subsystem's point of view, the allocation and submission are sequential. To remove this requirement, a \gls{thrd} would need the ability to ``yield to a specific \gls{proc}'', \ie, park with the promise that it will be run next on a specific \gls{proc}, the \gls{proc} attached to the correct ring. This is not a current or planned feature of \CFA.}, greatly simplifying both allocation and submission. In this design, allocation and submission form a ring partitioned ring buffer as shown in Figure~\ref{fig:pring}. Once added to the ring buffer, the attached \gls{proc} has a significant amount of flexibility with regards to when to do the system call. Possible options are: when the \gls{proc} runs out of \glspl{thrd} to run, after running a given number of threads \glspl{thrd}, etc. 127 128 \begin{figure} 129 \centering 130 \input{pivot_ring.pstex_t} 131 \caption[Partitioned ring buffer]{Partitioned ring buffer \smallskip\newline Allocated sqes are appending to the first partition. When submitting, the partition is simply advanced to include all the sqes that should be submitted. The kernel considers the partition as the head of the ring.} 132 \label{fig:pring} 133 \end{figure} 134 135 This approach has the advantage that it does not require much of the synchronization needed in the shared approach. This comes at the cost that \glspl{thrd} submitting \io operations have less flexibility, they cannot park or yield, and several exceptional cases are handled poorly. Instances running out of SQEs cannot run \glspl{thrd} wanting to do \io operations, in such a case the \gls{thrd} needs to be moved to a different \gls{proc}, the only current way of achieving this would be to @yield()@ hoping to be scheduled on a different \gls{proc}, which is not guaranteed. Another problematic case is that \glspl{thrd} that do not park for long periods of time will delay the submission of any SQE not already submitted. This issue is similar to fairness issues which schedulers that use work-stealing mentioned in the previous chapter. 136 44 137 45 138 46 139 \section{Interface} 140 Finally, the last important part of the \io subsystem is it's interface. There are multiple approaches that can be offered to programmers, each with advantages and disadvantages. The new \io subsystem can replace the C runtime's API or extend it. And in the later case the interface can go from very similar to vastly different. The following sections discuss some useful options using @read@ as an example. The standard Linux interface for C is : 141 142 @ssize_t read(int fd, void *buf, size_t count);@. 143 144 \subsection{Replacement} 145 Replacing the C \glsxtrshort{api} 146 147 \subsection{Synchronous Extension} 148 149 \subsection{Asynchronous Extension} 150 151 \subsection{Interface directly to \lstinline{io_uring}} -
doc/theses/thierry_delisle_PhD/thesis/text/runtime.tex
r342af53 r8e4aa05 11 11 12 12 \section{Clusters} 13 \CFA allows the option to group user-level threading, in the form of clusters. Both \glspl{thrd} and \glspl{proc} belong to a specific cluster. \Glspl{thrd} are only bescheduled onto \glspl{proc} in the same cluster and scheduling is done independently of other clusters. Figure~\ref{fig:system} shows an overview of the \CFA runtime, which allows programmers to tightly control parallelism. It also opens the door to handling effects like NUMA, by pining clusters to a specific NUMA node\footnote{This is not currently implemented in \CFA, but the only hurdle left is creating a generic interface for cpu masks.}.13 \CFA allows the option to group user-level threading, in the form of clusters. Both \glspl{thrd} and \glspl{proc} belong to a specific cluster. \Glspl{thrd} are only scheduled onto \glspl{proc} in the same cluster and scheduling is done independently of other clusters. Figure~\ref{fig:system} shows an overview of the \CFA runtime, which allows programmers to tightly control parallelism. It also opens the door to handling effects like NUMA, by pining clusters to a specific NUMA node\footnote{This is not currently implemented in \CFA, but the only hurdle left is creating a generic interface for cpu masks.}. 14 14 15 15 \begin{figure} … … 25 25 26 26 \section{\glsxtrshort{io}}\label{prev:io} 27 Prior to this work, the \CFA runtime did not add any particular support for \glsxtrshort{io} operations. %\CFA being built on C, this means that,28 While all I/O operations available in C are available in \CFA, \glsxtrshort{io} operations are designed for the POSIX threading model~\cite{pthreads}. Using these 1:1 threading operations in an M:N threading model means I/O operations block \glspl{proc} instead of \glspl{thrd}. While this can work in certain cases, it limits the number of concurrent operations to the number of \glspl{proc} rather than \glspl{thrd}. It also means deadlock can occur because all \glspl{proc} are blocked even if at least one \gls{thrd} is ready to run. A simple example of this type of deadlock would be as follows: 27 Prior to this work, the \CFA runtime did not add any particular support for \glsxtrshort{io} operations. While all \glsxtrshort{io} operations available in C are available in \CFA, \glsxtrshort{io} operations are designed for the POSIX threading model~\cite{pthreads}. Using these 1:1 threading operations in an M:N threading model means \glsxtrshort{io} operations block \glspl{proc} instead of \glspl{thrd}. While this can work in certain cases, it limits the number of concurrent operations to the number of \glspl{proc} rather than \glspl{thrd}. It also means deadlock can occur because all \glspl{proc} are blocked even if at least one \gls{thrd} is ready to run. A simple example of this type of deadlock would be as follows: 28 29 29 \begin{quote} 30 30 Given a simple network program with 2 \glspl{thrd} and a single \gls{proc}, one \gls{thrd} sends network requests to a server and the other \gls{thrd} waits for a response from the server. If the second \gls{thrd} races ahead, it may wait for responses to requests that have not been sent yet. In theory, this should not be a problem, even if the second \gls{thrd} waits, because the first \gls{thrd} is still ready to run and should be able to get CPU time to send the request. With M:N threading, while the first \gls{thrd} is ready, the lone \gls{proc} \emph{cannot} run the first \gls{thrd} if it is blocked in the \glsxtrshort{io} operation of the second \gls{thrd}. If this happen, the system is in a synchronization deadlock\footnote{In this example, the deadlocked could be resolved if the server sends unprompted messages to the client. However, this solution is not general and may not be appropriate even in this simple case.}. 31 31 \end{quote} 32 Therefore, one of the objective of this work is to introduce \emph{User-Level \glsxtrshort{io}}, like \glslink{uthrding}{User-Level \emph{Threading}} blocks \glspl{thrd} rather than \glspl{proc} when doing \glsxtrshort{io} operations, which entails multiplexing the \glsxtrshort{io} operations of many \glspl{thrd} onto fewer \glspl{proc}. This multiplexing requires that a single \gls{proc} be able to execute multiple I/O operations in parallel. This requirement cannot be done with operations that block \glspl{proc}, \ie \glspl{kthrd}, since the first operation would prevent starting new operations for its blocking duration. Executing I/O operations in parallel requires \emph{asynchronous} \glsxtrshort{io}, sometimes referred to as \emph{non-blocking}, since the \gls{kthrd} does not block.33 32 34 \section{Interoperating with C} 33 Therefore, one of the objective of this work is to introduce \emph{User-Level \glsxtrshort{io}}, like \glslink{uthrding}{User-Level \emph{Threading}} blocks \glspl{thrd} rather than \glspl{proc} when doing \glsxtrshort{io} operations, which entails multiplexing the \glsxtrshort{io} operations of many \glspl{thrd} onto fewer \glspl{proc}. This multiplexing requires that a single \gls{proc} be able to execute multiple \glsxtrshort{io} operations in parallel. This requirement cannot be done with operations that block \glspl{proc}, \ie \glspl{kthrd}, since the first operation would prevent starting new operations for its blocking duration. Executing \glsxtrshort{io} operations in parallel requires \emph{asynchronous} \glsxtrshort{io}, sometimes referred to as \emph{non-blocking}, since the \gls{kthrd} does not block. 34 35 \section{Interoperating with \texttt{C}} 35 36 While \glsxtrshort{io} operations are the classical example of operations that block \glspl{kthrd}, the non-blocking challenge extends to all blocking system-calls. The POSIX standard states~\cite[\S~2.9.1]{POSIX17}: 36 37 \begin{quote} … … 44 45 \begin{enumerate} 45 46 \item Precisely identifying blocking C calls is difficult. 46 \item Introducing newcode can have a significant impact on general performance.47 \item Introducing control points code can have a significant impact on general performance. 47 48 \end{enumerate} 48 Because of these consequences, this work does not attempt to ``sandbox'' calls to C. Therefore, it is possible for an unidentified library calls toblock a \gls{kthrd} leading to deadlocks in \CFA's M:N threading model, which would not occur in a traditional 1:1 threading model. Currently, all M:N thread systems interacting with UNIX without sandboxing suffer from this problem but manage to work very well in the majority of applications. Therefore, a complete solution to this problem is outside the scope of this thesis.49 Because of these consequences, this work does not attempt to ``sandbox'' calls to C. Therefore, it is possible calls from an unidentified library will block a \gls{kthrd} leading to deadlocks in \CFA's M:N threading model, which would not occur in a traditional 1:1 threading model. Currently, all M:N thread systems interacting with UNIX without sandboxing suffer from this problem but manage to work very well in the majority of applications. Therefore, a complete solution to this problem is outside the scope of this thesis. -
doc/theses/thierry_delisle_PhD/thesis/thesis.tex
r342af53 r8e4aa05 1 % uWaterloo Thesis Template for LaTeX 2 % Last Updated June 14, 2017 by Stephen Carr, IST Client Services 3 % FOR ASSISTANCE, please send mail to rt-IST-CSmathsci@ist.uwaterloo.ca 4 5 % Effective October 2006, the University of Waterloo 6 % requires electronic thesis submission. See the uWaterloo thesis regulations at 1 %====================================================================== 2 % University of Waterloo Thesis Template for LaTeX 3 % Last Updated November, 2020 4 % by Stephen Carr, IST Client Services, 5 % University of Waterloo, 200 University Ave. W., Waterloo, Ontario, Canada 6 % FOR ASSISTANCE, please send mail to request@uwaterloo.ca 7 8 % DISCLAIMER 9 % To the best of our knowledge, this template satisfies the current uWaterloo thesis requirements. 10 % However, it is your responsibility to assure that you have met all requirements of the University and your particular department. 11 12 % Many thanks for the feedback from many graduates who assisted the development of this template. 13 % Also note that there are explanatory comments and tips throughout this template. 14 %====================================================================== 15 % Some important notes on using this template and making it your own... 16 17 % The University of Waterloo has required electronic thesis submission since October 2006. 18 % See the uWaterloo thesis regulations at 7 19 % https://uwaterloo.ca/graduate-studies/thesis. 8 9 % DON'T FORGET TO ADD YOUR OWN NAME AND TITLE in the "hyperref" package 10 % configuration below. THIS INFORMATION GETS EMBEDDED IN THE PDF FINAL PDF DOCUMENT. 11 % You can view the information if you view Properties of the PDF document. 12 13 % Many faculties/departments also require one or more printed 14 % copies. This template attempts to satisfy both types of output. 15 % It is based on the standard "book" document class which provides all necessary 16 % sectioning structures and allows multi-part theses. 17 18 % DISCLAIMER 19 % To the best of our knowledge, this template satisfies the current uWaterloo requirements. 20 % However, it is your responsibility to assure that you have met all 21 % requirements of the University and your particular department. 22 % Many thanks for the feedback from many graduates that assisted the development of this template. 23 24 % ----------------------------------------------------------------------- 25 26 % By default, output is produced that is geared toward generating a PDF 27 % version optimized for viewing on an electronic display, including 28 % hyperlinks within the PDF. 29 20 % This thesis template is geared towards generating a PDF version optimized for viewing on an electronic display, including hyperlinks within the PDF. 21 22 % DON'T FORGET TO ADD YOUR OWN NAME AND TITLE in the "hyperref" package configuration below. 23 % THIS INFORMATION GETS EMBEDDED IN THE PDF FINAL PDF DOCUMENT. 24 % You can view the information if you view properties of the PDF document. 25 26 % Many faculties/departments also require one or more printed copies. 27 % This template attempts to satisfy both types of output. 28 % See additional notes below. 29 % It is based on the standard "book" document class which provides all necessary sectioning structures and allows multi-part theses. 30 31 % If you are using this template in Overleaf (cloud-based collaboration service), then it is automatically processed and previewed for you as you edit. 32 33 % For people who prefer to install their own LaTeX distributions on their own computers, and process the source files manually, the following notes provide the sequence of tasks: 34 30 35 % E.g. to process a thesis called "mythesis.tex" based on this template, run: 31 36 32 37 % pdflatex mythesis -- first pass of the pdflatex processor 33 38 % bibtex mythesis -- generates bibliography from .bib data file(s) 34 % makeindex -- should be run only if an index is used 39 % makeindex -- should be run only if an index is used 35 40 % pdflatex mythesis -- fixes numbering in cross-references, bibliographic references, glossaries, index, etc. 36 % pdflatex mythesis -- fixes numbering in cross-references, bibliographic references, glossaries, index, etc. 37 38 % If you use the recommended LaTeX editor, Texmaker, you would open the mythesis.tex 39 % file, then click the PDFLaTeX button. Then run BibTeX (under the Tools menu). 40 % Then click the PDFLaTeX button two more times. If you have an index as well, 41 % you'll need to run MakeIndex from the Tools menu as well, before running pdflatex 41 % pdflatex mythesis -- it takes a couple of passes to completely process all cross-references 42 43 % If you use the recommended LaTeX editor, Texmaker, you would open the mythesis.tex file, then click the PDFLaTeX button. Then run BibTeX (under the Tools menu). 44 % Then click the PDFLaTeX button two more times. 45 % If you have an index as well,you'll need to run MakeIndex from the Tools menu as well, before running pdflatex 42 46 % the last two times. 43 47 44 % N.B. The "pdftex" program allows graphics in the following formats to be 45 % included with the "\includegraphics" command: PNG, PDF, JPEG, TIFF 46 % Tip 1: Generate your figures and photos in the size you want them to appear 47 % in your thesis, rather than scaling them with \includegraphics options. 48 % Tip 2: Any drawings you do should be in scalable vector graphic formats: 49 % SVG, PNG, WMF, EPS and then converted to PNG or PDF, so they are scalable in 50 % the final PDF as well. 51 % Tip 3: Photographs should be cropped and compressed so as not to be too large. 52 53 % To create a PDF output that is optimized for double-sided printing: 54 % 55 % 1) comment-out the \documentclass statement in the preamble below, and 56 % un-comment the second \documentclass line. 57 % 58 % 2) change the value assigned below to the boolean variable 59 % "PrintVersion" from "false" to "true". 60 61 % --------------------- Start of Document Preamble ----------------------- 62 63 % Specify the document class, default style attributes, and page dimensions 48 % N.B. The "pdftex" program allows graphics in the following formats to be included with the "\includegraphics" command: PNG, PDF, JPEG, TIFF 49 % Tip: Generate your figures and photos in the size you want them to appear in your thesis, rather than scaling them with \includegraphics options. 50 % Tip: Any drawings you do should be in scalable vector graphic formats: SVG, PNG, WMF, EPS and then converted to PNG or PDF, so they are scalable in the final PDF as well. 51 % Tip: Photographs should be cropped and compressed so as not to be too large. 52 53 % To create a PDF output that is optimized for double-sided printing: 54 % 1) comment-out the \documentclass statement in the preamble below, and un-comment the second \documentclass line. 55 % 2) change the value assigned below to the boolean variable "PrintVersion" from " false" to "true". 56 57 %====================================================================== 58 % D O C U M E N T P R E A M B L E 59 % Specify the document class, default style attributes, and page dimensions, etc. 64 60 % For hyperlinked PDF, suitable for viewing on a computer, use this: 65 61 \documentclass[letterpaper,12pt,titlepage,oneside,final]{book} 66 62 67 % For PDF, suitable for double-sided printing, change the PrintVersion variable below 68 % to "true" and use this \documentclass line instead of the one above: 63 % For PDF, suitable for double-sided printing, change the PrintVersion variable below to "true" and use this \documentclass line instead of the one above: 69 64 %\documentclass[letterpaper,12pt,titlepage,openright,twoside,final]{book} 70 65 71 \newcommand{\href}[1]{#1} % does nothing, but defines the command so the 72 % print-optimized version will ignore \href tags (redefined by hyperref pkg). 66 % Some LaTeX commands I define for my own nomenclature. 67 % If you have to, it's easier to make changes to nomenclature once here than in a million places throughout your thesis! 68 \newcommand{\package}[1]{\textbf{#1}} % package names in bold text 69 \newcommand{\cmmd}[1]{\textbackslash\texttt{#1}} % command name in tt font 70 \newcommand{\href}[1]{#1} % does nothing, but defines the command so the print-optimized version will ignore \href tags (redefined by hyperref pkg). 71 %\newcommand{\texorpdfstring}[2]{#1} % does nothing, but defines the command 72 % Anything defined here may be redefined by packages added below... 73 73 74 74 % This package allows if-then-else control structures. … … 76 76 \newboolean{PrintVersion} 77 77 \setboolean{PrintVersion}{false} 78 % CHANGE THIS VALUE TO "true" as necessary, to improve printed results for hard copies 79 % by overriding some options of the hyperref package below. 78 % CHANGE THIS VALUE TO "true" as necessary, to improve printed results for hard copies by overriding some options of the hyperref package, called below. 80 79 81 80 %\usepackage{nomencl} % For a nomenclature (optional; available from ctan.org) 82 81 \usepackage{amsmath,amssymb,amstext} % Lots of math symbols and environments 82 \usepackage{xcolor} 83 83 \usepackage{graphicx} % For including graphics 84 84 85 85 % Hyperlinks make it very easy to navigate an electronic document. 86 % In addition, this is where you should specify the thesis title 87 % and author as they appear in the properties of the PDF document. 86 % In addition, this is where you should specify the thesis title and author as they appear in the properties of the PDF document. 88 87 % Use the "hyperref" package 89 88 % N.B. HYPERREF MUST BE THE LAST PACKAGE LOADED; ADD ADDITIONAL PKGS ABOVE 90 89 \usepackage[pagebackref=false]{hyperref} % with basic options 91 % N.B. pagebackref=true provides links back from the References to the body text. This can cause trouble for printing. 90 %\usepackage[pdftex,pagebackref=true]{hyperref} 91 % N.B. pagebackref=true provides links back from the References to the body text. This can cause trouble for printing. 92 92 \hypersetup{ 93 93 plainpages=false, % needed if Roman numbers in frontpages 94 unicode=false, % non-Latin characters in Acrobat ’s bookmarks95 pdftoolbar=true, % show Acrobat ’s toolbar?96 pdfmenubar=true, % show Acrobat ’s menu?94 unicode=false, % non-Latin characters in Acrobat's bookmarks 95 pdftoolbar=true, % show Acrobat's toolbar? 96 pdfmenubar=true, % show Acrobat's menu? 97 97 pdffitwindow=false, % window fit to page when opened 98 98 pdfstartview={FitH}, % fits the width of the page to the window … … 110 110 \ifthenelse{\boolean{PrintVersion}}{ % for improved print quality, change some hyperref options 111 111 \hypersetup{ % override some previously defined hyperref options 112 citecolor=black, 113 filecolor=black, 114 linkcolor=black, 112 citecolor=black,% 113 filecolor=black,% 114 linkcolor=black,% 115 115 urlcolor=black 116 116 }}{} % end of ifthenelse (no else) … … 120 120 % although it's supposed to be in both the TeX Live and MikTeX distributions. There are also documentation and 121 121 % installation instructions there. 122 \renewcommand*{\glstextformat}[1]{\textsf{#1}} 122 \makeatletter 123 \newcommand*{\glsplainhyperlink}[2]{% 124 \colorlet{currenttext}{.}% store current text color 125 \colorlet{currentlink}{\@linkcolor}% store current link color 126 \hypersetup{linkcolor=currenttext}% set link color 127 \hyperlink{#1}{#2}% 128 \hypersetup{linkcolor=currentlink}% reset to default 129 } 130 \let\@glslink\glsplainhyperlink 131 \makeatother 123 132 124 133 \usepackage{csquotes} … … 126 135 127 136 % Setting up the page margins... 128 \setlength{\textheight}{9in}\setlength{\topmargin}{-0.45in}\setlength{\headsep}{0.25in} 137 \setlength{\textheight}{9in} 138 \setlength{\topmargin}{-0.45in} 139 \setlength{\headsep}{0.25in} 129 140 % uWaterloo thesis requirements specify a minimum of 1 inch (72pt) margin at the 130 % top, bottom, and outside page edges and a 1.125 in. (81pt) gutter 131 % margin (on binding side). While this is not an issue for electronic 132 % viewing, a PDF may be printed, and so we have the same page layout for 133 % both printed and electronic versions, we leave the gutter margin in. 141 % top, bottom, and outside page edges and a 1.125 in. (81pt) gutter margin (on binding side). 142 % While this is not an issue for electronic viewing, a PDF may be printed, and so we have the same page layout for both printed and electronic versions, we leave the gutter margin in. 134 143 % Set margins to minimum permitted by uWaterloo thesis regulations: 135 144 \setlength{\marginparwidth}{0pt} % width of margin notes … … 140 149 \setlength{\evensidemargin}{0.125in} % Adds 1/8 in. to binding side of all 141 150 % even-numbered pages when the "twoside" printing option is selected 142 \setlength{\oddsidemargin}{0.125in} % Adds 1/8 in. to the left of all pages 143 % when "oneside" printing is selected, and to the left of all odd-numbered 144 % pages when "twoside" printing is selected 145 \setlength{\textwidth}{6.375in} % assuming US letter paper (8.5 in. x 11 in.) and 146 % side margins as above 151 \setlength{\oddsidemargin}{0.125in} % Adds 1/8 in. to the left of all pages when "oneside" printing is selected, and to the left of all odd-numbered pages when "twoside" printing is selected 152 \setlength{\textwidth}{6.375in} % assuming US letter paper (8.5 in. x 11 in.) and side margins as above 147 153 \raggedbottom 148 154 149 % The following statement specifies the amount of space between 150 % paragraphs. Other reasonable specifications are \bigskipamount and \smallskipamount. 155 % The following statement specifies the amount of space between paragraphs. Other reasonable specifications are \bigskipamount and \smallskipamount. 151 156 \setlength{\parskip}{\medskipamount} 152 157 153 % The following statement controls the line spacing. The default 154 % spacing corresponds to good typographic conventions and only slight 155 % changes (e.g., perhaps "1.2"), if any, should be made. 158 % The following statement controls the line spacing. 159 % The default spacing corresponds to good typographic conventions and only slight changes (e.g., perhaps "1.2"), if any, should be made. 156 160 \renewcommand{\baselinestretch}{1} % this is the default line space setting 157 161 158 % By default, each chapter will start on a recto (right-hand side) 159 % page. We also force each section of the front pages to start on 160 % a recto page by inserting \cleardoublepage commands. 161 % In many cases, this will require that the verso page be 162 % blank and, while it should be counted, a page number should not be 163 % printed. The following statements ensure a page number is not 164 % printed on an otherwise blank verso page. 162 % By default, each chapter will start on a recto (right-hand side) page. 163 % We also force each section of the front pages to start on a recto page by inserting \cleardoublepage commands. 164 % In many cases, this will require that the verso (left-hand) page be blank, and while it should be counted, a page number should not be printed. 165 % The following statements ensure a page number is not printed on an otherwise blank verso page. 165 166 \let\origdoublepage\cleardoublepage 166 167 \newcommand{\clearemptydoublepage}{% … … 194 195 \input{common} 195 196 \CFAStyle % CFA code-style for all languages 196 \lstset{ basicstyle=\linespread{0.9}\tt}197 \lstset{language=CFA,basicstyle=\linespread{0.9}\tt} % CFA default language 197 198 198 199 % glossary of terms to use … … 200 201 \makeindex 201 202 202 %====================================================================== 203 % L O G I C A L D O C U M E N T -- the content of your thesis 203 \newcommand\io{\glsxtrshort{io}\xspace}% 204 205 %====================================================================== 206 % L O G I C A L D O C U M E N T 207 % The logical document contains the main content of your thesis. 208 % Being a large document, it is a good idea to divide your thesis into several files, each one containing one chapter or other significant chunk of content, so you can easily shuffle things around later if desired. 204 209 %====================================================================== 205 210 \begin{document} 206 211 207 % For a large document, it is a good idea to divide your thesis208 % into several files, each one containing one chapter.209 % To illustrate this idea, the "front pages" (i.e., title page,210 % declaration, borrowers' page, abstract, acknowledgements,211 % dedication, table of contents, list of tables, list of figures,212 % nomenclature) are contained within the file "uw-ethesis-frontpgs.tex" which is213 % included into the document by the following statement.214 212 %---------------------------------------------------------------------- 215 213 % FRONT MATERIAL 214 % title page,declaration, borrowers' page, abstract, acknowledgements, 215 % dedication, table of contents, list of tables, list of figures, nomenclature, etc. 216 216 %---------------------------------------------------------------------- 217 217 \input{text/front.tex} 218 218 219 220 219 %---------------------------------------------------------------------- 221 220 % MAIN BODY 222 % ----------------------------------------------------------------------223 % Because this is a short document, and to reduce the number of files224 % needed for this template, the chapters are not separate225 % documents as suggested above, but you get the idea. If they were226 % separate documents, they would each start with the \chapter command, i.e,227 % do not contain \documentclass or \begin{document} and \end{document} commands. 221 % We suggest using a separate file for each chapter of your thesis. 222 % Start each chapter file with the \chapter command. 223 % Only use \documentclass or \begin{document} and \end{document} commands in this master document. 224 % Tip: Putting each sentence on a new line is a way to simplify later editing. 225 %---------------------------------------------------------------------- 226 228 227 \part{Introduction} 229 228 \input{text/intro.tex} … … 232 231 \part{Design} 233 232 \input{text/core.tex} 233 \input{text/io.tex} 234 234 \input{text/practice.tex} 235 \input{text/io.tex}236 235 \part{Evaluation} 237 236 \label{Evaluation} … … 243 242 %---------------------------------------------------------------------- 244 243 % END MATERIAL 245 %---------------------------------------------------------------------- 246 247 % B I B L I O G R A P H Y 248 % ----------------------- 249 250 % The following statement selects the style to use for references. It controls the sort order of the entries in the bibliography and also the formatting for the in-text labels. 244 % Bibliography, Appendices, Index, etc. 245 %---------------------------------------------------------------------- 246 247 % Bibliography 248 249 % The following statement selects the style to use for references. 250 % It controls the sort order of the entries in the bibliography and also the formatting for the in-text labels. 251 251 \bibliographystyle{plain} 252 252 % This specifies the location of the file containing the bibliographic information. 253 % It assumes you're using BibTeX (if not, why not?). 254 \cleardoublepage % This is needed if the book class is used, to place the anchor in the correct page, 255 % because the bibliography will start on its own page. 256 % Use \clearpage instead if the document class uses the "oneside" argument 253 % It assumes you're using BibTeX to manage your references (if not, why not?). 254 \cleardoublepage % This is needed if the "book" document class is used, to place the anchor in the correct page, because the bibliography will start on its own page. 255 % Use \clearpage instead if the document class uses the "oneside" argument 257 256 \phantomsection % With hyperref package, enables hyperlinking from the table of contents to bibliography 258 257 % The following statement causes the title "References" to be used for the bibliography section: … … 263 262 264 263 \bibliography{local,pl} 265 % Tip 5: You can create multiple .bib files to organize your references.264 % Tip: You can create multiple .bib files to organize your references. 266 265 % Just list them all in the \bibliogaphy command, separated by commas (no spaces). 267 266 268 % % The following statement causes the specified references to be added to the bibliography% even if they were not269 % % cited in the text.The asterisk is a wildcard that causes all entries in the bibliographic database to be included (optional).267 % The following statement causes the specified references to be added to the bibliography even if they were not cited in the text. 268 % The asterisk is a wildcard that causes all entries in the bibliographic database to be included (optional). 270 269 % \nocite{*} 270 %---------------------------------------------------------------------- 271 272 % Appendices 271 273 272 274 % The \appendix statement indicates the beginning of the appendices. 273 275 \appendix 274 % Add a title page before the appendices and a line in the Table of Contents276 % Add an un-numbered title page before the appendices and a line in the Table of Contents 275 277 \chapter*{APPENDICES} 276 278 \addcontentsline{toc}{chapter}{APPENDICES} 279 % Appendices are just more chapters, with different labeling (letters instead of numbers). 277 280 %====================================================================== 278 281 \chapter[PDF Plots From Matlab]{Matlab Code for Making a PDF Plot} … … 312 315 %\input{thesis.ind} % index 313 316 314 \phantomsection 315 316 \end{document} 317 \phantomsection % allows hyperref to link to the correct page 318 319 %---------------------------------------------------------------------- 320 \end{document} % end of logical document -
doc/user/figures/Cdecl.fig
r342af53 r8e4aa05 19 19 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 20 20 2850 1200 3600 1200 3600 1350 2850 1350 2850 1200 21 4 1 0 50 -1 4 1 00.0000 2 120 90 2925 1325 0\00122 4 1 0 50 -1 4 1 00.0000 2 120 90 3075 1325 1\00123 4 1 0 50 -1 4 1 00.0000 2 120 90 3225 1325 2\00124 4 1 0 50 -1 4 1 00.0000 2 120 90 3375 1325 3\00125 4 1 0 50 -1 4 1 00.0000 2 120 90 3525 1325 4\00121 4 1 0 50 -1 4 11 0.0000 2 120 90 2925 1325 0\001 22 4 1 0 50 -1 4 11 0.0000 2 120 90 3075 1325 1\001 23 4 1 0 50 -1 4 11 0.0000 2 120 90 3225 1325 2\001 24 4 1 0 50 -1 4 11 0.0000 2 120 90 3375 1325 3\001 25 4 1 0 50 -1 4 11 0.0000 2 120 90 3525 1325 4\001 26 26 -6 27 27 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 … … 55 55 1 1 1.00 45.00 60.00 56 56 2550 1275 2850 1275 57 4 1 0 50 -1 4 1 00.0000 2 120 90 1350 1650 0\00158 4 1 0 50 -1 4 1 00.0000 2 120 90 1500 1650 1\00159 4 1 0 50 -1 4 1 00.0000 2 120 90 1650 1650 2\00160 4 1 0 50 -1 4 1 00.0000 2 120 90 1800 1650 3\00161 4 1 0 50 -1 4 1 00.0000 2 120 90 1950 1650 4\00162 4 1 0 50 -1 4 1 00.0000 2 90 90 1200 1325 x\00163 4 1 0 50 -1 4 1 00.0000 2 90 90 2400 1325 x\00157 4 1 0 50 -1 4 11 0.0000 2 120 90 1350 1650 0\001 58 4 1 0 50 -1 4 11 0.0000 2 120 90 1500 1650 1\001 59 4 1 0 50 -1 4 11 0.0000 2 120 90 1650 1650 2\001 60 4 1 0 50 -1 4 11 0.0000 2 120 90 1800 1650 3\001 61 4 1 0 50 -1 4 11 0.0000 2 120 90 1950 1650 4\001 62 4 1 0 50 -1 4 11 0.0000 2 90 90 1200 1325 x\001 63 4 1 0 50 -1 4 11 0.0000 2 90 90 2400 1325 x\001 -
doc/user/user.tex
r342af53 r8e4aa05 11 11 %% Created On : Wed Apr 6 14:53:29 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Mon Oct 5 08:57:29 202014 %% Update Count : 399813 %% Last Modified On : Mon Feb 15 13:48:53 2021 14 %% Update Count : 4452 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 37 37 \usepackage{mathptmx} % better math font with "times" 38 38 \usepackage[usenames]{color} 39 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref} 40 \usepackage{breakurl} 41 42 \renewcommand\footnoterule{\kern -3pt\rule{0.3\linewidth}{0.15pt}\kern 2pt} 43 44 \usepackage[pagewise]{lineno} 45 \renewcommand{\linenumberfont}{\scriptsize\sffamily} 46 \usepackage[firstpage]{draftwatermark} 47 \SetWatermarkLightness{0.9} 48 49 % Default underscore is too low and wide. Cannot use lstlisting "literate" as replacing underscore 50 % removes it as a variable-name character so keywords in variables are highlighted. MUST APPEAR 51 % AFTER HYPERREF. 52 \renewcommand{\textunderscore}{\leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.075ex}}} 53 54 \setlength{\topmargin}{-0.45in} % move running title into header 55 \setlength{\headsep}{0.25in} 56 57 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 58 39 59 \newcommand{\CFALatin}{} 40 60 % inline code ©...© (copyright symbol) emacs: C-q M-) … … 46 66 % math escape $...$ (dollar symbol) 47 67 \input{common} % common CFA document macros 48 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref}49 \usepackage{breakurl}50 51 \renewcommand\footnoterule{\kern -3pt\rule{0.3\linewidth}{0.15pt}\kern 2pt}52 53 \usepackage[pagewise]{lineno}54 \renewcommand{\linenumberfont}{\scriptsize\sffamily}55 \usepackage[firstpage]{draftwatermark}56 \SetWatermarkLightness{0.9}57 58 % Default underscore is too low and wide. Cannot use lstlisting "literate" as replacing underscore59 % removes it as a variable-name character so keywords in variables are highlighted. MUST APPEAR60 % AFTER HYPERREF.61 \renewcommand{\textunderscore}{\leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.075ex}}}62 63 \setlength{\topmargin}{-0.45in} % move running title into header64 \setlength{\headsep}{0.25in}65 66 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%67 68 68 \CFAStyle % use default CFA format-style 69 \lstset{language=CFA} % CFA default lnaguage 69 70 \lstnewenvironment{C++}[1][] % use C++ style 70 {\lstset{language=C++,moredelim=**[is][\protect\color{red}]{ ®}{®},#1}}71 {\lstset{language=C++,moredelim=**[is][\protect\color{red}]{@}{@},#1}} 71 72 {} 72 73 … … 81 82 \newcommand{\Emph}[2][red]{{\color{#1}\textbf{\emph{#2}}}} 82 83 \newcommand{\R}[1]{\Textbf{#1}} 84 \newcommand{\RC}[1]{\Textbf{\LstBasicStyle{#1}}} 83 85 \newcommand{\B}[1]{{\Textbf[blue]{#1}}} 84 86 \newcommand{\G}[1]{{\Textbf[OliveGreen]{#1}}} … … 103 105 104 106 \author{ 105 \huge \CFA Team \medskip \\ 106 \Large Andrew Beach, Richard Bilson, Peter A. Buhr, Thierry Delisle, \smallskip \\ 107 \Large Glen Ditchfield, Rodolfo G. Esteves, Aaron Moss, Rob Schluntz 107 \huge \CFA Team (past and present) \medskip \\ 108 \Large Andrew Beach, Richard Bilson, Michael Brooks, Peter A. Buhr, Thierry Delisle, \smallskip \\ 109 \Large Glen Ditchfield, Rodolfo G. Esteves, Aaron Moss, Colby Parsons, Rob Schluntz, \smallskip \\ 110 \Large Fangren Yu, Mubeen Zulfiqar 108 111 }% author 109 112 … … 126 129 \vspace*{\fill} 127 130 \noindent 128 \copyright\,2016 \CFA Project \\ \\131 \copyright\,2016, 2018, 2021 \CFA Project \\ \\ 129 132 \noindent 130 133 This work is licensed under the Creative Commons Attribution 4.0 International License. … … 144 147 \section{Introduction} 145 148 146 \CFA{}\index{cforall@\CFA}\footnote{Pronounced ``\Index*{C-for-all}'', and written \CFA, CFA, or \CFL.} is a modern general-purpose programming-language, designed as an evolutionary step forward for the C programming language.149 \CFA{}\index{cforall@\CFA}\footnote{Pronounced ``\Index*{C-for-all}'', and written \CFA, CFA, or \CFL.} is a modern general-purpose concurrent programming-language, designed as an evolutionary step forward for the C programming language. 147 150 The syntax of \CFA builds from C and should look immediately familiar to C/\Index*[C++]{\CC{}} programmers. 148 151 % Any language feature that is not described here can be assumed to be using the standard \Celeven syntax. 149 \CFA adds many modern programming-languagefeatures that directly lead to increased \emph{\Index{safety}} and \emph{\Index{productivity}}, while maintaining interoperability with existing C programs and achieving similar performance.152 \CFA adds many modern features that directly lead to increased \emph{\Index{safety}} and \emph{\Index{productivity}}, while maintaining interoperability with existing C programs and achieving similar performance. 150 153 Like C, \CFA is a statically typed, procedural (non-\Index{object-oriented}) language with a low-overhead runtime, meaning there is no global \Index{garbage-collection}, but \Index{regional garbage-collection}\index{garbage-collection!regional} is possible. 151 154 The primary new features include polymorphic routines and types, exceptions, concurrency, and modules. … … 157 160 instead, a programmer evolves a legacy program into \CFA by incrementally incorporating \CFA features. 158 161 As well, new programs can be written in \CFA using a combination of C and \CFA features. 162 In many ways, \CFA is to C as \Index{Scala}~\cite{Scala} is to Java, providing a vehicle for new typing and control-flow capabilities on top of a highly popular programming language allowing immediate dissemination. 159 163 160 164 \Index*[C++]{\CC{}}~\cite{c++:v1} had a similar goal 30 years ago, allowing object-oriented programming to be incrementally added to C. … … 165 169 For example, the following programs compare the C, \CFA, and \CC I/O mechanisms, where the programs output the same result. 166 170 \begin{center} 167 \begin{tabular}{@{}l@{\hspace{1 .5em}}l@{\hspace{1.5em}}l@{}}168 \multicolumn{1}{c@{\hspace{1 .5em}}}{\textbf{C}} & \multicolumn{1}{c}{\textbf{\CFA}} & \multicolumn{1}{c}{\textbf{\CC}} \\169 \begin{cfa} 170 #include <stdio.h> §\indexc{stdio.h}§171 \begin{tabular}{@{}l@{\hspace{1em}}l@{\hspace{1em}}l@{}} 172 \multicolumn{1}{c@{\hspace{1em}}}{\textbf{C}} & \multicolumn{1}{c}{\textbf{\CFA}} & \multicolumn{1}{c}{\textbf{\CC}} \\ 173 \begin{cfa} 174 #include <stdio.h>$\indexc{stdio.h}$ 171 175 172 176 int main( void ) { 173 177 int x = 0, y = 1, z = 2; 174 ®printf( "%d %d %d\n", x, y, z );®178 @printf( "%d %d %d\n", x, y, z );@ 175 179 } 176 180 \end{cfa} 177 181 & 178 182 \begin{cfa} 179 #include <fstream> §\indexc{fstream}§183 #include <fstream>$\indexc{fstream}$ 180 184 181 185 int main( void ) { 182 186 int x = 0, y = 1, z = 2; 183 ®sout | x | y | z;®§\indexc{sout}§187 @sout | x | y | z;@$\indexc{sout}$ 184 188 } 185 189 \end{cfa} 186 190 & 187 191 \begin{cfa} 188 #include <iostream> §\indexc{iostream}§192 #include <iostream>$\indexc{iostream}$ 189 193 using namespace std; 190 194 int main() { 191 195 int x = 0, y = 1, z = 2; 192 ®cout<<x<<" "<<y<<" "<<z<<endl;®196 @cout<<x<<" "<<y<<" "<<z<<endl;@ 193 197 } 194 198 \end{cfa} 195 199 \end{tabular} 196 200 \end{center} 197 While the \CFA I/O looks similar to the \Index*[C++]{\CC{}} output style, there are important differences, such as automatic spacing between variables as in \Index*{Python} (see~\VRef{s:IOLibrary}).201 While \CFA I/O \see{\VRef{s:StreamIOLibrary}} looks similar to \Index*[C++]{\CC{}}, there are important differences, such as automatic spacing between variables and an implicit newline at the end of the expression list, similar to \Index*{Python}~\cite{Python}. 198 202 199 203 … … 210 214 \section{Why fix C?} 211 215 212 The C programming language is a foundational technology for modern computing with millions of lines of code implementing everything from hobby projects to commercial operating-systems.216 The C programming language is a foundational technology for modern computing with billions of lines of code implementing everything from hobby projects to commercial operating-systems. 213 217 This installation base and the programmers producing it represent a massive software-engineering investment spanning decades and likely to continue for decades more. 214 218 Even with all its problems, C continues to be popular because it allows writing software at virtually any level in a computer system without restriction. 215 For system programming, where direct access to hardware, storage management, and real-time issues are a requirement, C is usuallythe only language of choice.216 The TIOBE index~\cite{TIOBE} for February 202 0 ranks the top six most \emph{popular} programming languages as \Index*{Java} 17.4\%, C 16.8\%, Python 9.3\%, \Index*[C++]{\CC{}} 6.2\%, \Csharp 5.9\%, Visual Basic 5.9\% = 61.5\%, where the next 50 languages are less than 2\% each, with a long tail.219 For system programming, where direct access to hardware, storage management, and real-time issues are a requirement, C is the only language of choice. 220 The TIOBE index~\cite{TIOBE} for February 2021 ranks the top six most \emph{popular} programming languages as C 17.4\%, \Index*{Java} 12\%, Python 12\%, \Index*[C++]{\CC{}} 7.6\%, \Csharp 4\%, Visual Basic 3.8\% = 56.8\%, where the next 50 languages are less than 2\% each, with a long tail. 217 221 The top 4 rankings over the past 35 years are: 218 222 \begin{center} 219 223 \setlength{\tabcolsep}{10pt} 220 224 \begin{tabular}{@{}rcccccccc@{}} 221 & 202 0 & 2015 & 2010 & 2005 & 2000 & 1995 & 1990 & 1985\\ \hline222 Java & 1 & 2 & 1 & 2 & 3 & - & - & -\\223 \R{C} & \R{2} & \R{1} & \R{2} & \R{1} & \R{1} & \R{2} & \R{1} & \R{1}\\224 Python & 3 & 7 & 6 & 6 & 22 & 21& - & - \\225 \CC & 4 & 4 & 4 & 3 & 2 & 1 & 2 & 12\\225 & 2021 & 2016 & 2011 & 2006 & 2001 & 1996 & 1991 & 1986 \\ \hline 226 \R{C} & \R{1} & \R{2} & \R{2} & \R{1} & \R{1} & \R{1} & \R{1} & \R{1} \\ 227 Java & 2 & 1 & 1 & 2 & 3 & 28 & - & - \\ 228 Python & 3 & 5 & 6 & 7 & 23 & 13 & - & - \\ 229 \CC & 4 & 3 & 3 & 3 & 2 & 2 & 2 & 8 \\ 226 230 \end{tabular} 227 231 \end{center} … … 232 236 As stated, the goal of the \CFA project is to engineer modern language-features into C in an evolutionary rather than revolutionary way. 233 237 \CC~\cite{C++14,C++} is an example of a similar project; 234 however, it largely extended the C language, and did not address m ostof C's existing problems.\footnote{%238 however, it largely extended the C language, and did not address many of C's existing problems.\footnote{% 235 239 Two important existing problems addressed were changing the type of character literals from ©int© to ©char© and enumerator from ©int© to the type of its enumerators.} 236 240 \Index*{Fortran}~\cite{Fortran08}, \Index*{Ada}~\cite{Ada12}, and \Index*{Cobol}~\cite{Cobol14} are examples of programming languages that took an evolutionary approach, where modern language-features (\eg objects, concurrency) are added and problems fixed within the framework of the existing language. … … 241 245 242 246 The result of this project is a language that is largely backwards compatible with \Index*[C11]{\Celeven{}}~\cite{C11}, but fixes many of the well known C problems while adding modern language-features. 243 To achieve these goals required a significant engineering exercise, where we had to ``think inside the existing C box''. 244 Without these significant extension to C, it is unable to cope with the needs of modern programming problems and programmers; 245 as a result, it will fade into disuse. 246 Considering the large body of existing C code and programmers, there is significant impetus to ensure C is transformed into a modern programming language. 247 To achieve these goals required a significant engineering exercise, \ie ``thinking \emph{inside} the C box''. 248 Considering the large body of existing C code and programmers, there is significant impetus to ensure C is transformed into a modern language. 247 249 While \Index*[C11]{\Celeven{}} made a few simple extensions to the language, nothing was added to address existing problems in the language or to augment the language with modern language-features. 248 250 While some may argue that modern language-features may make C complex and inefficient, it is clear a language without modern capabilities is insufficient for the advanced programming problems existing today. … … 251 253 \section{History} 252 254 253 The \CFA project started with \Index*{Dave Till}\index{Till, Dave}'s \Index*{K-W C}~\cite{Buhr94a,Till89}, which extended C with new declaration syntax, multiple return values from routines, and advanced assignment capabilities using the notion of tuples. 254 (See~\cite{Werther96} for similar work in \Index*[C++]{\CC{}}.) 255 The \CFA project started with \Index*{Dave Till}\index{Till, Dave}'s \Index*{K-W C}~\cite{Buhr94a,Till89}, which extended C with new declaration syntax, multiple return values from routines, and advanced assignment capabilities using the notion of tuples \see{\cite{Werther96} for similar work in \Index*[C++]{\CC{}}}. 255 256 The first \CFA implementation of these extensions was by \Index*{Rodolfo Esteves}\index{Esteves, Rodolfo}~\cite{Esteves04}. 256 257 257 258 The signature feature of \CFA is \emph{\Index{overload}able} \Index{parametric-polymorphic} functions~\cite{forceone:impl,Cormack90,Duggan96} with functions generalized using a ©forall© clause (giving the language its name): 258 259 \begin{cfa} 259 ®forall( otype T )®T identity( T val ) { return val; }260 int forty_two = identity( 42 ); §\C{// T is bound to int, forty\_two == 42}§260 @forall( otype T )@ T identity( T val ) { return val; } 261 int forty_two = identity( 42 ); $\C{// T is bound to int, forty\_two == 42}$ 261 262 \end{cfa} 262 263 % extending the C type system with parametric polymorphism and overloading, as opposed to the \Index*[C++]{\CC{}} approach of object-oriented extensions. 263 264 \CFA{}\hspace{1pt}'s polymorphism was originally formalized by \Index*{Glen Ditchfield}\index{Ditchfield, Glen}~\cite{Ditchfield92}, and first implemented by \Index*{Richard Bilson}\index{Bilson, Richard}~\cite{Bilson03}. 264 265 However, at that time, there was little interesting in extending C, so work did not continue. 265 As the saying goes, ``\Index*{What goes around, comes around.}'', and there is now renewed interest in the C programming language because of legacy code-bases, so the \CFA project has been restarted.266 As the saying goes, ``\Index*{What goes around, comes around.}'', and there is now renewed interest in the C programming language because of the legacy code-base, so the \CFA project was restarted in 2015. 266 267 267 268 … … 273 274 This feature allows \CFA programmers to take advantage of the existing panoply of C libraries to access thousands of external software features. 274 275 Language developers often state that adequate \Index{library support} takes more work than designing and implementing the language itself. 275 Fortunately, \CFA, like \Index*[C++]{\CC{}}, starts with immediate access to all exiting C libraries, and in many cases, can easily wrap library routines with simpler and safer interfaces, at very low cost.276 Fortunately, \CFA, like \Index*[C++]{\CC{}}, starts with immediate access to all exiting C libraries, and in many cases, can easily wrap library routines with simpler and safer interfaces, at zero or very low cost. 276 277 Hence, \CFA begins by leveraging the large repository of C libraries, and than allows programmers to incrementally augment their C programs with modern \Index{backward-compatible} features. 277 278 … … 286 287 287 288 double key = 5.0, vals[10] = { /* 10 sorted floating values */ }; 288 double * val = (double *)bsearch( &key, vals, 10, sizeof(vals[0]), comp ); §\C{// search sorted array}§289 double * val = (double *)bsearch( &key, vals, 10, sizeof(vals[0]), comp ); $\C{// search sorted array}$ 289 290 \end{cfa} 290 291 which can be augmented simply with a polymorphic, type-safe, \CFA-overloaded wrappers: … … 295 296 296 297 forall( otype T | { int ?<?( T, T ); } ) unsigned int bsearch( T key, const T * arr, size_t size ) { 297 T * result = bsearch( key, arr, size ); §\C{// call first version}§298 return result ? result - arr : size; } §\C{// pointer subtraction includes sizeof(T)}§299 300 double * val = bsearch( 5.0, vals, 10 ); §\C{// selection based on return type}§298 T * result = bsearch( key, arr, size ); $\C{// call first version}$ 299 return result ? result - arr : size; } $\C{// pointer subtraction includes sizeof(T)}$ 300 301 double * val = bsearch( 5.0, vals, 10 ); $\C{// selection based on return type}$ 301 302 int posn = bsearch( 5.0, vals, 10 ); 302 303 \end{cfa} … … 310 311 \begin{cfa} 311 312 forall( dtype T | sized(T) ) T * malloc( void ) { return (T *)malloc( sizeof(T) ); } 312 int * ip = malloc(); §\C{// select type and size from left-hand side}§313 int * ip = malloc(); $\C{// select type and size from left-hand side}$ 313 314 double * dp = malloc(); 314 315 struct S {...} * sp = malloc(); … … 319 320 However, it is necessary to differentiate between C and \CFA code because of name \Index{overload}ing, as for \CC. 320 321 For example, the C math-library provides the following routines for computing the absolute value of the basic types: ©abs©, ©labs©, ©llabs©, ©fabs©, ©fabsf©, ©fabsl©, ©cabsf©, ©cabs©, and ©cabsl©. 321 Whereas, \CFA wraps each of these routines into ones with the overloaded name ©abs©: 322 \begin{cfa} 323 char ®abs®( char ); 324 extern "C" { int ®abs®( int ); } §\C{// use default C routine for int}§ 325 long int ®abs®( long int ); 326 long long int ®abs®( long long int ); 327 float ®abs®( float ); 328 double ®abs®( double ); 329 long double ®abs®( long double ); 330 float _Complex ®abs®( float _Complex ); 331 double _Complex ®abs®( double _Complex ); 332 long double _Complex ®abs®( long double _Complex ); 333 \end{cfa} 334 The problem is the name clash between the library routine ©abs© and the \CFA names ©abs©. 335 Hence, names appearing in an ©extern "C"© block have \newterm*{C linkage}. 336 Then overloading polymorphism uses a mechanism called \newterm{name mangling}\index{mangling!name} to create unique names that are different from C names, which are not mangled. 337 Hence, there is the same need, as in \CC, to know if a name is a C or \CFA name, so it can be correctly formed. 338 There is no way around this problem, other than C's approach of creating unique names for each pairing of operation and types. 339 340 This example strongly illustrates a core idea in \CFA: \emph{the \Index{power of a name}}. 322 Whereas, \CFA wraps each of these routines into one overloaded name ©abs©: 323 \begin{cfa} 324 char @abs@( char ); 325 extern "C" { int @abs@( int ); } $\C{// use default C routine for int}$ 326 long int @abs@( long int ); 327 long long int @abs@( long long int ); 328 float @abs@( float ); 329 double @abs@( double ); 330 long double @abs@( long double ); 331 float _Complex @abs@( float _Complex ); 332 double _Complex @abs@( double _Complex ); 333 long double _Complex @abs@( long double _Complex ); 334 \end{cfa} 335 The problem is \Index{name clash} between the C name ©abs© and the \CFA names ©abs©, resulting in two name linkages\index{C linkage}: ©extern "C"© and ©extern "Cforall"© (default). 336 Overloaded names must use \newterm{name mangling}\index{mangling!name} to create unique names that are different from unmangled C names. 337 Hence, there is the same need as in \CC to know if a name is a C or \CFA name, so it can be correctly formed. 338 The only way around this problem is C's approach of creating unique names for each pairing of operation and type. 339 340 This example illustrates a core idea in \CFA: \emph{the \Index{power of a name}}. 341 341 The name ``©abs©'' evokes the notion of absolute value, and many mathematical types provide the notion of absolute value. 342 342 Hence, knowing the name ©abs© is sufficient to apply it to any type where it is applicable. … … 344 344 345 345 346 \section [Compiling a CFA Program]{Compiling a \CFA Program}346 \section{\CFA Compilation} 347 347 348 348 The command ©cfa© is used to compile a \CFA program and is based on the \Index{GNU} \Indexc{gcc} command, \eg: 349 349 \begin{cfa} 350 cfa§\indexc{cfa}\index{compilation!cfa@©cfa©}§ [ gcc-options ] [ C/§\CFA{}§ source-files ] [ assembler/loader files ] 351 \end{cfa} 352 \CFA programs having the following ©gcc© flags turned on: 353 \begin{description} 350 cfa$\indexc{cfa}\index{compilation!cfa@©cfa©}$ [ gcc/$\CFA{}$-options ] [ C/$\CFA{}$ source-files ] [ assembler/loader files ] 351 \end{cfa} 352 There is no ordering among options (flags) and files, unless an option has an argument, which must appear immediately after the option possibly with or without a space separating option and argument. 353 354 \CFA has the following ©gcc© flags turned on: 355 \begin{description}[topsep=0pt] 354 356 \item 355 357 \Indexc{-std=gnu11}\index{compilation option!-std=gnu11@{©-std=gnu11©}} … … 359 361 Use the traditional GNU semantics for inline routines in C11 mode, which allows inline routines in header files. 360 362 \end{description} 361 The following new \CFA options are available: 362 \begin{description} 363 364 \CFA has the following new options: 365 \begin{description}[topsep=0pt] 363 366 \item 364 367 \Indexc{-CFA}\index{compilation option!-CFA@©-CFA©} 365 Only the C preprocessor and the \CFA translator steps are performed and the transformed program is written to standard output, which makes it possible to examine the code generated by the \CFA translator.368 Only the C preprocessor (flag ©-E©) and the \CFA translator steps are performed and the transformed program is written to standard output, which makes it possible to examine the code generated by the \CFA translator. 366 369 The generated code starts with the standard \CFA \Index{prelude}. 370 371 \item 372 \Indexc{-XCFA}\index{compilation option!-XCFA@©-XCFA©} 373 Pass next flag as-is to the ©cfa-cpp© translator (see details below). 367 374 368 375 \item 369 376 \Indexc{-debug}\index{compilation option!-debug@©-debug©} 370 377 The program is linked with the debugging version of the runtime system. 371 The debug version performs runtime checks to help duringthe debugging phase of a \CFA program, but can substantially slow program execution.378 The debug version performs runtime checks to aid the debugging phase of a \CFA program, but can substantially slow program execution. 372 379 The runtime checks should only be removed after the program is completely debugged. 373 380 \textbf{This option is the default.} … … 399 406 \item 400 407 \Indexc{-no-include-stdhdr}\index{compilation option!-no-include-stdhdr@©-no-include-stdhdr©} 401 Do not supply ©extern "C"© wrappers for \Celeven standard include files (see~\VRef{s:StandardHeaders}).408 Do not supply ©extern "C"© wrappers for \Celeven standard include files \see{\VRef{s:StandardHeaders}}. 402 409 \textbf{This option is \emph{not} the default.} 403 410 \end{comment} … … 430 437 \begin{cfa} 431 438 #ifndef __CFORALL__ 432 #include <stdio.h> §\indexc{stdio.h}§ §\C{// C header file}§439 #include <stdio.h>$\indexc{stdio.h}$ $\C{// C header file}$ 433 440 #else 434 #include <fstream> §\indexc{fstream}§ §\C{// \CFA header file}§441 #include <fstream>$\indexc{fstream}$ $\C{// \CFA header file}$ 435 442 #endif 436 443 \end{cfa} … … 438 445 439 446 The \CFA translator has multiple steps. 440 The following flags control how the tran lator works, the stages run, and printing within a stage.447 The following flags control how the translator works, the stages run, and printing within a stage. 441 448 The majority of these flags are used by \CFA developers, but some are occasionally useful to programmers. 449 Each option must be escaped with \Indexc{-XCFA}\index{translator option!-XCFA@{©-XCFA©}} to direct it to the compiler step, similar to the ©-Xlinker© flag for the linker, \eg: 450 \begin{lstlisting}[language=sh] 451 cfa $test$.cfa -CFA -XCFA -p # print translated code without printing the standard prelude 452 cfa $test$.cfa -XCFA -P -XCFA parse -XCFA -n # show program parse without prelude 453 \end{lstlisting} 442 454 \begin{description}[topsep=5pt,itemsep=0pt,parsep=0pt] 443 455 \item 444 \Indexc{-h}\index{translator option!-h@{©-h©}}, \Indexc{--help}\index{translator option!--help@{©--help©}} \, print help message 445 \item 446 \Indexc{-l}\index{translator option!-l@{©-l©}}, \Indexc{--libcfa}\index{translator option!--libcfa@{©--libcfa©}} \, generate libcfa.c 456 \Indexc{-c}\index{translator option!-c@{©-c©}}, \Indexc{--colors}\index{translator option!--colors@{©--colors©}} \, diagnostic color: ©never©, ©always©, \lstinline[deletekeywords=auto]{auto} 457 \item 458 \Indexc{-g}\index{translator option!-g@{©-g©}}, \Indexc{--gdb}\index{translator option!--gdb@{©--gdb©}} \, wait for gdb to attach 459 \item 460 \Indexc{-h}\index{translator option!-h@{©-h©}}, \Indexc{--help}\index{translator option!--help@{©--help©}} \, print translator help message 461 \item 462 \Indexc{-l}\index{translator option!-l@{©-l©}}, \Indexc{--libcfa}\index{translator option!--libcfa@{©--libcfa©}} \, generate ©libcfa.c© 447 463 \item 448 464 \Indexc{-L}\index{translator option!-L@{©-L©}}, \Indexc{--linemarks}\index{translator option!--linemarks@{©--linemarks©}} \, generate line marks … … 454 470 \Indexc{-n}\index{translator option!-n@{©-n©}}, \Indexc{--no-prelude}\index{translator option!--no-prelude@{©--no-prelude©}} \, do not read prelude 455 471 \item 456 \Indexc{-p}\index{translator option!-p@{©-p©}}, \Indexc{--prototypes}\index{translator option!--prototypes@{©--prototypes©}} \, generate prototypes for prelude functions 472 \Indexc{-p}\index{translator option!-p@{©-p©}}, \Indexc{--prototypes}\index{translator option!--prototypes@{©--prototypes©}} \, do not generate prelude prototypes $\Rightarrow$ prelude not printed 473 \item 474 \Indexc{-d}\index{translator option!-d@{©-d©}}, \Indexc{--deterministic-out}\index{translator option!--deterministic-out@{©--deterministic-out©}} \, only print deterministic output 457 475 \item 458 476 \Indexc{-P}\index{translator option!-P@{©-P©}}, \Indexc{--print}\index{translator option!--print@{©--print©}} \, one of: 459 477 \begin{description}[topsep=0pt,itemsep=0pt,parsep=0pt] 460 478 \item 479 \Indexc{ascodegen}\index{translator option!-P@{©-P©}!©ascodegen©}\index{translator option!--print@{©-print©}!©ascodegen©} \, as codegen rather than AST 480 \item 481 \Indexc{asterr}\index{translator option!-P@{©-P©}!©asterr©}\index{translator option!--print@{©-print©}!©asterr©} \, AST on error 482 \item 483 \Indexc{declstats}\index{translator option!-P@{©-P©}!©declstats©}\index{translator option!--print@{©-print©}!©declstats©} \, code property statistics 484 \item 485 \Indexc{parse}\index{translator option!-P@{©-P©}!©parse©}\index{translator option!--print@{©-print©}!©parse©} \, yacc (parsing) debug information 486 \item 487 \Indexc{pretty}\index{translator option!-P@{©-P©}!©pretty©}\index{translator option!--print@{©-print©}!©pretty©} \, prettyprint for ©ascodegen© flag 488 \item 489 \Indexc{rproto}\index{translator option!-P@{©-P©}!©rproto©}\index{translator option!--print@{©-print©}!©rproto©} \, resolver-proto instance 490 \item 491 \Indexc{rsteps}\index{translator option!-P@{©-P©}!©rsteps©}\index{translator option!--print@{©-print©}!©rsteps©} \, resolver steps 492 \item 493 \Indexc{tree}\index{translator option!-P@{©-P©}!©tree©}\index{translator option!--print@{©-print©}!©tree©} \, parse tree 494 \item 495 \Indexc{ast}\index{translator option!-P@{©-P©}!©ast©}\index{translator option!--print@{©-print©}!©ast©} \, AST after parsing 496 \item 497 \Indexc{symevt}\index{translator option!-P@{©-P©}!©symevt©}\index{translator option!--print@{©-print©}!©symevt©} \, symbol table events 498 \item 461 499 \Indexc{altexpr}\index{translator option!-P@{©-P©}!©altexpr©}\index{translator option!--print@{©-print©}!©altexpr©} \, alternatives for expressions 462 500 \item 463 \Indexc{ascodegen}\index{translator option!-P@{©-P©}!©ascodegen©}\index{translator option!--print@{©-print©}!©ascodegen©} \, as codegen rather than AST464 \item465 \Indexc{ast}\index{translator option!-P@{©-P©}!©ast©}\index{translator option!--print@{©-print©}!©ast©} \, AST after parsing466 \item467 501 \Indexc{astdecl}\index{translator option!-P@{©-P©}!©astdecl©}\index{translator option!--print@{©-print©}!©astdecl©} \, AST after declaration validation pass 468 502 \item 469 \Indexc{ asterr}\index{translator option!-P@{©-P©}!©asterr©}\index{translator option!--print@{©-print©}!©asterr©} \, AST on error503 \Indexc{resolver}\index{translator option!-P@{©-P©}!©resolver©}\index{translator option!--print@{©-print©}!©resolver©} \, before resolver step 470 504 \item 471 505 \Indexc{astexpr}\index{translator option!-P@{©-P©}!©astexpr©}\index{translator option!--print@{©-print©}!©altexpr©} \, AST after expression analysis 472 506 \item 507 \Indexc{ctordtor}\index{translator option!-P@{©-P©}!©ctordtor©}\index{translator option!--print@{©-print©}!©ctordtor©} \, after ctor/dtor are replaced 508 \item 509 \Indexc{tuple}\index{translator option!-P@{©-P©}!©tuple©}\index{translator option!--print@{©-print©}!©tuple©} \, after tuple expansion 510 \item 473 511 \Indexc{astgen}\index{translator option!-P@{©-P©}!©astgen©}\index{translator option!--print@{©-print©}!©astgen©} \, AST after instantiate generics 474 512 \item 475 513 \Indexc{box}\index{translator option!-P@{©-P©}!©box©}\index{translator option!--print@{©-print©}!©box©} \, before box step 476 514 \item 477 \Indexc{ctordtor}\index{translator option!-P@{©-P©}!©ctordtor©}\index{translator option!--print@{©-print©}!©ctordtor©} \, after ctor/dtor are replaced478 \item479 515 \Indexc{codegen}\index{translator option!-P@{©-P©}!©codegen©}\index{translator option!--print@{©-print©}!©codegen©} \, before code generation 480 \item481 \Indexc{declstats}\index{translator option!-P@{©-P©}!©declstats©}\index{translator option!--print@{©-print©}!©declstats©} \, code property statistics482 \item483 \Indexc{parse}\index{translator option!-P@{©-P©}!©parse©}\index{translator option!--print@{©-print©}!©parse©} \, yacc (parsing) debug information484 \item485 \Indexc{pretty}\index{translator option!-P@{©-P©}!©pretty©}\index{translator option!--print@{©-print©}!©pretty©} \, prettyprint for ascodegen flag486 \item487 \Indexc{resolver}\index{translator option!-P@{©-P©}!©resolver©}\index{translator option!--print@{©-print©}!©resolver©} \, before resolver step488 \item489 \Indexc{rproto}\index{translator option!-P@{©-P©}!©rproto©}\index{translator option!--print@{©-print©}!©rproto©} \, resolver-proto instance490 \item491 \Indexc{rsteps}\index{translator option!-P@{©-P©}!©rsteps©}\index{translator option!--print@{©-print©}!©rsteps©} \, resolver steps492 \item493 \Indexc{symevt}\index{translator option!-P@{©-P©}!©symevt©}\index{translator option!--print@{©-print©}!©symevt©} \, symbol table events494 \item495 \Indexc{tree}\index{translator option!-P@{©-P©}!©tree©}\index{translator option!--print@{©-print©}!©tree©} \, parse tree496 \item497 \Indexc{tuple}\index{translator option!-P@{©-P©}!©tuple©}\index{translator option!--print@{©-print©}!©tuple©} \, after tuple expansion498 516 \end{description} 499 517 \item 500 518 \Indexc{--prelude-dir} <directory> \, prelude directory for debug/nodebug 501 519 \item 502 \Indexc{-S}\index{translator option!-S@{©-S©}!©counters,heap,time,all,none©}, \Indexc{--statistics}\index{translator option!--statistics@{©--statistics©}!©counters,heap,time,all,none©} <option-list> \, enable profiling information: 503 \begin{description}[topsep=0pt,itemsep=0pt,parsep=0pt] 504 \item 505 \Indexc{counters,heap,time,all,none} 506 \end{description} 520 \Indexc{-S}\index{translator option!-S@{©-S©}!©counters,heap,time,all,none©}, \Indexc{--statistics}\index{translator option!--statistics@{©--statistics©}!©counters,heap,time,all,none©} <option-list> \, enable profiling information: ©counters©, ©heap©, ©time©, ©all©, ©none© 507 521 \item 508 522 \Indexc{-t}\index{translator option!-t@{©-t©}}, \Indexc{--tree}\index{translator option!--tree@{©--tree©}} build in tree … … 513 527 \label{s:BackquoteIdentifiers} 514 528 515 \CFA introduces several new keywords (see \VRef{s:CFAKeywords})that can clash with existing C variable-names in legacy code.529 \CFA introduces several new keywords \see{\VRef{s:CFAKeywords}} that can clash with existing C variable-names in legacy code. 516 530 Keyword clashes are accommodated by syntactic transformations using the \CFA backquote escape-mechanism: 517 531 \begin{cfa} 518 int ®``®otype = 3; §\C{// make keyword an identifier}§519 double ®``®forall = 3.5;532 int @``@otype = 3; $\C{// make keyword an identifier}$ 533 double @``@forall = 3.5; 520 534 \end{cfa} 521 535 522 536 Existing C programs with keyword clashes can be converted by enclosing keyword identifiers in backquotes, and eventually the identifier name can be changed to a non-keyword name. 523 \VRef[Figure]{f:HeaderFileInterposition} shows how clashes in existing C header-files (see~\VRef{s:StandardHeaders})can be handled using preprocessor \newterm{interposition}: ©#include_next© and ©-I filename©.537 \VRef[Figure]{f:HeaderFileInterposition} shows how clashes in existing C header-files \see{\VRef{s:StandardHeaders}} can be handled using preprocessor \newterm{interposition}: ©#include_next© and ©-I filename©. 524 538 Several common C header-files with keyword clashes are fixed in the standard \CFA header-library, so there is a seamless programming-experience. 525 539 … … 527 541 \begin{cfa} 528 542 // include file uses the CFA keyword "with". 529 #if ! defined( with ) §\C{// nesting ?}§530 #define with ®``®with §\C{// make keyword an identifier}§543 #if ! defined( with ) $\C{// nesting ?}$ 544 #define with @``@with $\C{// make keyword an identifier}$ 531 545 #define __CFA_BFD_H__ 532 546 #endif 533 §{\color{red}\#\textbf{include\_next} <bfdlink.h>}§ §\C{// must have internal check for multiple expansion}§ 534 #if defined( with ) && defined( __CFA_BFD_H__ ) §\C{// reset only if set}§547 $\R{\#include\_next} <bfdlink.h>$ $\C{// must have internal check for multiple expansion}$ 548 #if defined( with ) && defined( __CFA_BFD_H__ ) $\C{// reset only if set}$ 535 549 #undef with 536 550 #undef __CFA_BFD_H__ … … 544 558 \section{Constant Underscores} 545 559 546 Numeric constants are extended to allow \Index{underscore}s\index{constant!underscore} , \eg:547 \begin{cfa} 548 2 ®_®147®_®483®_®648; §\C{// decimal constant}§549 56 ®_®ul; §\C{// decimal unsigned long constant}§550 0 ®_®377; §\C{// octal constant}§551 0x ®_®ff®_®ff; §\C{// hexadecimal constant}§552 0x ®_®ef3d®_®aa5c; §\C{// hexadecimal constant}§553 3.141 ®_®592®_®654; §\C{// floating constant}§554 10 ®_®e®_®+1®_®00; §\C{// floating constant}§555 0x ®_®ff®_®ff®_®p®_®3; §\C{// hexadecimal floating}§556 0x ®_®1.ffff®_®ffff®_®p®_®128®_®l; §\C{// hexadecimal floating long constant}§557 L ®_®§"\texttt{\textbackslash{x}}§®_®§\texttt{ff}§®_®§\texttt{ee}"§; §\C{// wide character constant}§560 Numeric constants are extended to allow \Index{underscore}s\index{constant!underscore} as a separator, \eg: 561 \begin{cfa} 562 2@_@147@_@483@_@648; $\C{// decimal constant}$ 563 56@_@ul; $\C{// decimal unsigned long constant}$ 564 0@_@377; $\C{// octal constant}$ 565 0x@_@ff@_@ff; $\C{// hexadecimal constant}$ 566 0x@_@ef3d@_@aa5c; $\C{// hexadecimal constant}$ 567 3.141@_@592@_@654; $\C{// floating constant}$ 568 10@_@e@_@+1@_@00; $\C{// floating constant}$ 569 0x@_@ff@_@ff@_@p@_@3; $\C{// hexadecimal floating}$ 570 0x@_@1.ffff@_@ffff@_@p@_@128@_@l; $\C{// hexadecimal floating long constant}$ 571 L@_@$"\texttt{\textbackslash{x}}$@_@$\texttt{ff}$@_@$\texttt{ee}"$; $\C{// wide character constant}$ 558 572 \end{cfa} 559 573 The rules for placement of underscores are: … … 574 588 It is significantly easier to read and enter long constants when they are broken up into smaller groupings (many cultures use comma and/or period among digits for the same purpose). 575 589 This extension is backwards compatible, matches with the use of underscore in variable names, and appears in \Index*{Ada} and \Index*{Java} 8. 590 \CC uses the single quote (©'©) as a separator, restricted within a sequence of digits, \eg ©0xaa©©'©©ff©, ©3.141©©'©©592E1©©'©©1©. 576 591 577 592 578 593 \section{Exponentiation Operator} 579 594 580 C, \CC, and Java (and manyother programming languages) have no exponentiation operator\index{exponentiation!operator}\index{operator!exponentiation}, \ie $x^y$, and instead use a routine, like \Indexc{pow(x,y)}, to perform the exponentiation operation.581 \CFA extends the basic operators with the exponentiation operator ©? ®\®?©\index{?\\?@©?®\®?©} and ©?\=?©\index{?\\=?@©®\®=?©}, as in, ©x ®\® y© and ©x ®\®= y©, which means $x^y$ and $x \leftarrow x^y$.582 The priority of the exponentiation operator is between the cast and multiplicative operators, so that ©w * (int)x \ (int)y * z© is parenthesized as ©( (w * (((int)x) \ ((int)y))) * z)©.595 C, \CC, and Java (and other programming languages) have no exponentiation operator\index{exponentiation!operator}\index{operator!exponentiation}, \ie $x^y$, and instead use a routine, like \Indexc{pow(x,y)}, to perform the exponentiation operation. 596 \CFA extends the basic operators with the exponentiation operator ©?©\R{©\\©}©?©\index{?\\?@©?@\@?©} and ©?©\R{©\\©}©=?©\index{?\\=?@©@\@=?©}, as in, ©x ©\R{©\\©}© y© and ©x ©\R{©\\©}©= y©, which means $x^y$ and $x \leftarrow x^y$. 597 The priority of the exponentiation operator is between the cast and multiplicative operators, so that ©w * (int)x \ (int)y * z© is parenthesized as ©(w * (((int)x) \ ((int)y))) * z©. 583 598 584 599 There are exponentiation operators for integral and floating types, including the builtin \Index{complex} types. … … 587 602 Floating exponentiation\index{exponentiation!floating} is performed using \Index{logarithm}s\index{exponentiation!logarithm}, so the exponent cannot be negative. 588 603 \begin{cfa} 589 sout | 1 ®\® 0 | 1 ®\® 1 | 2 ®\® 8 | -4 ®\® 3 | 5 ®\® 3 | 5 ®\® 32 | 5L ®\® 32 | 5L ®\® 64 | -4 ®\® -3 | -4.0 ®\® -3 | 4.0 ®\®2.1590 | (1.0f+2.0fi) ®\®(3.0f+2.0fi);591 1 1 256 -64 125 ®0® 3273344365508751233 ®0® ®0®-0.015625 18.3791736799526 0.264715-1.1922i604 sout | 1 @\@ 0 | 1 @\@ 1 | 2 @\@ 8 | -4 @\@ 3 | 5 @\@ 3 | 5 @\@ 32 | 5L @\@ 32 | 5L @\@ 64 | -4 @\@ -3 | -4.0 @\@ -3 | 4.0 @\@ 2.1 605 | (1.0f+2.0fi) @\@ (3.0f+2.0fi); 606 1 1 256 -64 125 @0@ 3273344365508751233 @0@ @0@ -0.015625 18.3791736799526 0.264715-1.1922i 592 607 \end{cfa} 593 608 Note, ©5 \ 32© and ©5L \ 64© overflow, and ©-4 \ -3© is a fraction but stored in an integer so all three computations generate an integral zero. 594 Parenthesis are necessary for complex constants or the expression is parsed as ©1.0f+®(®2.0fi \ 3.0f®)®+2.0fi©. 609 Because exponentiation has higher priority than ©+©, parenthesis are necessary for exponentiation of \Index{complex constant}s or the expression is parsed as ©1.0f+©\R{©(©}©2.0fi \ 3.0f©\R{©)©}©+2.0fi©, requiring \R{©(©}©1.0f+2.0fi©\R{©)©}© \ ©\R{©(©}©3.0f+2.0fi©\R{©)©}. 610 595 611 The exponentiation operator is available for all the basic types, but for user-defined types, only the integral-computation version is available. 596 612 \begin{cfa} 597 forall( otype OT | { void ?{}( OT & this, one_t ); OT ?*?( OT, OT ); } )598 OT ?®\®?( OT ep, unsigned int y );599 forall( otype OT | { void ?{}( OT & this, one_t ); OT ?*?( OT, OT ); } )600 OT ?®\®?( OT ep, unsigned long int y );613 forall( otype T | { void ?{}( T & this, one_t ); T ?*?( T, T ); } ) 614 T ?@\@?( T ep, unsigned int y ); 615 forall( otype T | { void ?{}( T & this, one_t ); T ?*?( T, T ); } ) 616 T ?@\@?( T ep, unsigned long int y ); 601 617 \end{cfa} 602 618 The user type ©T© must define multiplication, one (©1©), and ©*©. … … 609 625 610 626 %\subsection{\texorpdfstring{\protect\lstinline@if@/\protect\lstinline@while@ Statement}{if Statement}} 611 \subsection{\texorpdfstring{\LstKeywordStyle{if}/\LstKeywordStyle{while} Statement}{if/while Statement}} 612 613 The ©if©/©while© expression allows declarations, similar to ©for© declaration expression. 614 (Does not make sense for ©do©-©while©.) 615 \begin{cfa} 616 if ( ®int x = f()® ) ... §\C{// x != 0}§ 617 if ( ®int x = f(), y = g()® ) ... §\C{// x != 0 \&\& y != 0}§ 618 if ( ®int x = f(), y = g(); x < y® ) ... §\C{// relational expression}§ 619 if ( ®struct S { int i; } x = { f() }; x.i < 4® ) §\C{// relational expression}§ 620 621 while ( ®int x = f()® ) ... §\C{// x != 0}§ 622 while ( ®int x = f(), y = g()® ) ... §\C{// x != 0 \&\& y != 0}§ 623 while ( ®int x = f(), y = g(); x < y® ) ... §\C{// relational expression}§ 624 while ( ®struct S { int i; } x = { f() }; x.i < 4® ) ... §\C{// relational expression}§ 625 \end{cfa} 626 Unless a relational expression is specified, each variable is compared not equal to 0, which is the standard semantics for the ©if©/©while© expression, and the results are combined using the logical ©&&© operator.\footnote{\CC only provides a single declaration always compared not equal to 0.} 627 The scope of the declaration(s) is local to the @if@ statement but exist within both the ``then'' and ``else'' clauses. 627 \subsection{\texorpdfstring{\LstKeywordStyle{if} / \LstKeywordStyle{while} Statement}{if / while Statement}} 628 629 The ©if©/©while© expression allows declarations, similar to ©for© declaration expression.\footnote{ 630 Declarations in the ©do©-©while© condition are not useful because they appear after the loop body.} 631 \begin{cfa} 632 if ( @int x = f()@ ) ... $\C{// x != 0}$ 633 if ( @int x = f(), y = g()@ ) ... $\C{// x != 0 \&\& y != 0}$ 634 if ( @int x = f(), y = g(); x < y@ ) ... $\C{// relational expression}$ 635 if ( @struct S { int i; } x = { f() }; x.i < 4@ ) $\C{// relational expression}$ 636 637 while ( @int x = f()@ ) ... $\C{// x != 0}$ 638 while ( @int x = f(), y = g()@ ) ... $\C{// x != 0 \&\& y != 0}$ 639 while ( @int x = f(), y = g(); x < y@ ) ... $\C{// relational expression}$ 640 while ( @struct S { int i; } x = { f() }; x.i < 4@ ) ... $\C{// relational expression}$ 641 \end{cfa} 642 Unless a relational expression is specified, each variable is compared not equal to 0, which is the standard semantics for the ©if©/©while© expression, and the results are combined using the logical ©&&© operator. 643 The scope of the declaration(s) is local to the ©if© statement but exist within both the \emph{then} and \emph{else} clauses. 644 \CC only provides a single declaration always compared ©!=© to 0. 628 645 629 646 630 647 %\section{\texorpdfstring{\protect\lstinline@case@ Clause}{case Clause}} 631 648 \subsection{\texorpdfstring{\LstKeywordStyle{case} Clause}{case Clause}} 649 \label{s:caseClause} 632 650 633 651 C restricts the ©case© clause of a ©switch© statement to a single value. … … 640 658 \begin{cfa} 641 659 switch ( i ) { 642 case ®1, 3, 5®:660 case @1, 3, 5@: 643 661 ... 644 case ®2, 4, 6®:662 case @2, 4, 6@: 645 663 ... 646 664 } … … 670 688 \begin{cfa} 671 689 switch ( i ) { 672 case ®1~5:® §\C{// 1, 2, 3, 4, 5}§690 case @1~5:@ $\C{// 1, 2, 3, 4, 5}$ 673 691 ... 674 case ®10~15:® §\C{// 10, 11, 12, 13, 14, 15}§692 case @10~15:@ $\C{// 10, 11, 12, 13, 14, 15}$ 675 693 ... 676 694 } … … 678 696 Lists of subranges are also allowed. 679 697 \begin{cfa} 680 case ®1~5, 12~21, 35~42®:698 case @1~5, 12~21, 35~42@: 681 699 \end{cfa} 682 700 … … 722 740 if ( argc == 3 ) { 723 741 // open output file 724 ®// open input file725 ®} else if ( argc == 2 ) {726 ®// open input file (duplicate)727 728 ®} else {742 @// open input file 743 @} else if ( argc == 2 ) { 744 @// open input file (duplicate) 745 746 @} else { 729 747 // usage message 730 748 } … … 733 751 \end{cquote} 734 752 In this example, case 2 is always done if case 3 is done. 735 This control flow is difficult to simulate with ifstatements or a ©switch© statement without fall-through as code must be duplicated or placed in a separate routine.753 This control flow is difficult to simulate with ©if© statements or a ©switch© statement without fall-through as code must be duplicated or placed in a separate routine. 736 754 C also uses fall-through to handle multiple case-values resulting in the same action: 737 755 \begin{cfa} 738 756 switch ( i ) { 739 ®case 1: case 3: case 5:®// odd values757 @case 1: case 3: case 5:@ // odd values 740 758 // odd action 741 759 break; 742 ®case 2: case 4: case 6:®// even values760 @case 2: case 4: case 6:@ // even values 743 761 // even action 744 762 break; 745 763 } 746 764 \end{cfa} 747 However, this situation is handled in other languages without fall-through by allowing a list of case values.748 While fall-through itself is not a problem, the problem occurs when fall-through is the default, as this semantics is unintuitive to many programmers and is different from virtually all otherprogramming languages with a ©switch© statement.765 This situation better handled without fall-through by allowing a list of case values \see{\VRef{s:caseClause}}. 766 While fall-through itself is not a problem, the problem occurs when fall-through is the default, as this semantics is unintuitive to many programmers and is different from most programming languages with a ©switch© statement. 749 767 Hence, default fall-through semantics results in a large number of programming errors as programmers often \emph{forget} the ©break© statement at the end of a ©case© clause, resulting in inadvertent fall-through. 750 768 … … 756 774 if ( j < k ) { 757 775 ... 758 ®case 1:®// transfer into "if" statement776 @case 1:@ // transfer into "if" statement 759 777 ... 760 778 } // if … … 762 780 while ( j < 5 ) { 763 781 ... 764 ®case 3:®// transfer into "while" statement782 @case 3:@ // transfer into "while" statement 765 783 ... 766 784 } // while 767 785 } // switch 768 786 \end{cfa} 769 Th e problem with this usage is branchinginto control structures, which is known to cause both comprehension and technical difficulties.770 The comprehension problem occurs from the inability to determine how control reaches a particular point due to the number of branches leading to it.787 This usage branches into control structures, which is known to cause both comprehension and technical difficulties. 788 The comprehension problem results from the inability to determine how control reaches a particular point due to the number of branches leading to it. 771 789 The technical problem results from the inability to ensure declaration and initialization of variables when blocks are not entered at the beginning. 772 There are no positivearguments for this kind of control flow, and therefore, there is a strong impetus to eliminate it.790 There are few arguments for this kind of control flow, and therefore, there is a strong impetus to eliminate it. 773 791 Nevertheless, C does have an idiom where this capability is used, known as ``\Index*{Duff's device}''~\cite{Duff83}: 774 792 \begin{cfa} … … 794 812 \item 795 813 It is possible to place the ©default© clause anywhere in the list of labelled clauses for a ©switch© statement, rather than only at the end. 796 Virtually allprogramming languages with a ©switch© statement require the ©default© clause to appear last in the case-clause list.814 Most programming languages with a ©switch© statement require the ©default© clause to appear last in the case-clause list. 797 815 The logic for this semantics is that after checking all the ©case© clauses without success, the ©default© clause is selected; 798 816 hence, physically placing the ©default© clause at the end of the ©case© clause list matches with this semantics. … … 803 821 \begin{cfa} 804 822 switch ( x ) { 805 ®int y = 1;® §\C{// unreachable initialization}§806 ®x = 7;® §\C{// unreachable code without label/branch}§823 @int y = 1;@ $\C{// unreachable initialization}$ 824 @x = 7;@ $\C{// unreachable code without label/branch}$ 807 825 case 0: ... 808 826 ... 809 ®int z = 0;® §\C{// unreachable initialization, cannot appear after case}§827 @int z = 0;@ $\C{// unreachable initialization, cannot appear after case}$ 810 828 z = 2; 811 829 case 1: 812 ®x = z;® §\C{// without fall through, z is uninitialized}§830 @x = z;@ $\C{// without fall through, z is uninitialized}$ 813 831 } 814 832 \end{cfa} 815 833 While the declaration of the local variable ©y© is useful with a scope across all ©case© clauses, the initialization for such a variable is defined to never be executed because control always transfers over it. 816 Furthermore, any statements before the first ©case© clause can only be executed if labelled and transferred to using a ©goto©, either from outside or inside of the ©switch©, both of which are problematic.817 As well, the declaration of ©z© cannot occur after the ©case© because a label can only be attached to a statement, and without a fall 818 The key observation is that the ©switch© statement branches into control structure, \ie there are multiple entry points into its statement body.834 Furthermore, any statements before the first ©case© clause can only be executed if labelled and transferred to using a ©goto©, either from outside or inside of the ©switch©, where both are problematic. 835 As well, the declaration of ©z© cannot occur after the ©case© because a label can only be attached to a statement, and without a fall-through to case 3, ©z© is uninitialized. 836 The key observation is that the ©switch© statement branches into a control structure, \ie there are multiple entry points into its statement body. 819 837 \end{enumerate} 820 838 … … 842 860 Therefore, to preserve backwards compatibility, it is necessary to introduce a new kind of ©switch© statement, called ©choose©, with no implicit fall-through semantics and an explicit fall-through if the last statement of a case-clause ends with the new keyword ©fallthrough©/©fallthru©, \eg: 843 861 \begin{cfa} 844 ®choose®( i ) {862 @choose@ ( i ) { 845 863 case 1: case 2: case 3: 846 864 ... 847 ®// implicit end of switch (break)848 ®case 5:865 @// implicit end of switch (break) 866 @case 5: 849 867 ... 850 ®fallthru®; §\C{// explicit fall through}§868 @fallthru@; $\C{// explicit fall through}$ 851 869 case 7: 852 870 ... 853 ®break® §\C{// explicit end of switch (redundant)}§871 @break@ $\C{// explicit end of switch (redundant)}$ 854 872 default: 855 873 j = 3; 856 874 } 857 875 \end{cfa} 858 Like the ©switch© statement, the ©choose© statement retains the fall-through semantics for a list of ©case© clauses ;876 Like the ©switch© statement, the ©choose© statement retains the fall-through semantics for a list of ©case© clauses. 859 877 An implicit ©break© is applied only at the end of the \emph{statements} following a ©case© clause. 860 878 An explicit ©fallthru© is retained because it is a C-idiom most C programmers expect, and its absence might discourage programmers from using the ©choose© statement. … … 872 890 \begin{cfa} 873 891 switch ( x ) { 874 ®int i = 0;® §\C{// allowed only at start}§892 @int i = 0;@ $\C{// allowed only at start}$ 875 893 case 0: 876 894 ... 877 ®int j = 0;® §\C{// disallowed}§895 @int j = 0;@ $\C{// disallowed}$ 878 896 case 1: 879 897 { 880 ®int k = 0;® §\C{// allowed at different nesting levels}§898 @int k = 0;@ $\C{// allowed at different nesting levels}$ 881 899 ... 882 ®case 2:® §\C{// disallow case in nested statements}§900 @case 2:@ $\C{// disallow case in nested statements}$ 883 901 } 884 902 ... … … 897 915 case 3: 898 916 if ( ... ) { 899 ... ®fallthru;®// goto case 4917 ... @fallthru;@ // goto case 4 900 918 } else { 901 919 ... … … 912 930 choose ( ... ) { 913 931 case 3: 914 ... ®fallthrough common;®932 ... @fallthrough common;@ 915 933 case 4: 916 ... ®fallthrough common;®917 918 ®common:®// below fallthrough934 ... @fallthrough common;@ 935 936 @common:@ // below fallthrough 919 937 // at case-clause level 920 938 ... // common code for cases 3/4 … … 932 950 for ( ... ) { 933 951 // multi-level transfer 934 ... ®fallthru common;®952 ... @fallthru common;@ 935 953 } 936 954 ... 937 955 } 938 956 ... 939 ®common:®// below fallthrough957 @common:@ // below fallthrough 940 958 // at case-clause level 941 959 \end{cfa} … … 948 966 949 967 \begin{figure} 950 \begin{tabular}{@{}l |l@{}}951 \multicolumn{1}{ c|}{loop control} & \multicolumn{1}{c}{output} \\968 \begin{tabular}{@{}l@{\hspace{25pt}}|l@{}} 969 \multicolumn{1}{@{}c@{\hspace{25pt}}|}{loop control} & \multicolumn{1}{c@{}}{output} \\ 952 970 \hline 953 \begin{cfa} [xleftmargin=0pt]954 while ®()®{ sout | "empty"; break; }955 do { sout | "empty"; break; } while ®()®;956 for ®()®{ sout | "empty"; break; }957 for ( ®0®) { sout | "A"; } sout | "zero";958 for ( ®1®) { sout | "A"; }959 for ( ®10®) { sout | "A"; }960 for ( ®= 10®) { sout | "A"; }961 for ( ®1 ~= 10 ~ 2®) { sout | "B"; }962 for ( ®10 -~= 1 ~ 2®) { sout | "C"; }963 for ( ®0.5 ~ 5.5®) { sout | "D"; }964 for ( ®5.5 -~ 0.5®) { sout | "E"; }965 for ( ®i; 10®) { sout | i; }966 for ( ®i; = 10®) { sout | i; }967 for ( ®i; 1 ~= 10 ~ 2®) { sout | i; }968 for ( ®i; 10 -~= 1 ~ 2®) { sout | i; }969 for ( ®i; 0.5 ~ 5.5®) { sout | i; }970 for ( ®i; 5.5 -~ 0.5®) { sout | i; }971 for ( ®ui; 2u ~= 10u ~ 2u®) { sout | ui; }972 for ( ®ui; 10u -~= 2u ~ 2u®) { sout | ui; }971 \begin{cfa} 972 while @($\,$)@ { sout | "empty"; break; } 973 do { sout | "empty"; break; } while @($\,$)@; 974 for @($\,$)@ { sout | "empty"; break; } 975 for ( @0@ ) { sout | "A"; } sout | "zero"; 976 for ( @1@ ) { sout | "A"; } 977 for ( @10@ ) { sout | "A"; } 978 for ( @= 10@ ) { sout | "A"; } 979 for ( @1 ~= 10 ~ 2@ ) { sout | "B"; } 980 for ( @10 -~= 1 ~ 2@ ) { sout | "C"; } 981 for ( @0.5 ~ 5.5@ ) { sout | "D"; } 982 for ( @5.5 -~ 0.5@ ) { sout | "E"; } 983 for ( @i; 10@ ) { sout | i; } 984 for ( @i; = 10@ ) { sout | i; } 985 for ( @i; 1 ~= 10 ~ 2@ ) { sout | i; } 986 for ( @i; 10 -~= 1 ~ 2@ ) { sout | i; } 987 for ( @i; 0.5 ~ 5.5@ ) { sout | i; } 988 for ( @i; 5.5 -~ 0.5@ ) { sout | i; } 989 for ( @ui; 2u ~= 10u ~ 2u@ ) { sout | ui; } 990 for ( @ui; 10u -~= 2u ~ 2u@ ) { sout | ui; } 973 991 enum { N = 10 }; 974 for ( ®N®) { sout | "N"; }975 for ( ®i; N®) { sout | i; }976 for ( ®i; N -~ 0®) { sout | i; }992 for ( @N@ ) { sout | "N"; } 993 for ( @i; N@ ) { sout | i; } 994 for ( @i; N -~ 0@ ) { sout | i; } 977 995 const int start = 3, comp = 10, inc = 2; 978 for ( ®i; start ~ comp ~ inc + 1®) { sout | i; }979 for ( i; 1 ~ ®@®) { if ( i > 10 ) break; sout | i; }980 for ( i; 10 -~ ®@®) { if ( i < 0 ) break; sout | i; }981 for ( i; 2 ~ ®@®~ 2 ) { if ( i > 10 ) break; sout | i; }982 for ( i; 2.1 ~ ®@® ~ ®@®) { if ( i > 10.5 ) break; sout | i; i += 1.7; }983 for ( i; 10 -~ ®@®~ 2 ) { if ( i < 0 ) break; sout | i; }984 for ( i; 12.1 ~ ®@® ~ ®@®) { if ( i < 2.5 ) break; sout | i; i -= 1.7; }985 for ( i; 5 ®:® j; -5 ~ @) { sout | i | j; }986 for ( i; 5 ®:® j; -5 -~ @) { sout | i | j; }987 for ( i; 5 ®:® j; -5 ~ @~ 2 ) { sout | i | j; }988 for ( i; 5 ®:® j; -5 -~ @~ 2 ) { sout | i | j; }989 for ( i; 5 ®:® j; -5 ~ @) { sout | i | j; }990 for ( i; 5 ®:® j; -5 -~ @) { sout | i | j; }991 for ( i; 5 ®:® j; -5 ~ @~ 2 ) { sout | i | j; }992 for ( i; 5 ®:® j; -5 -~ @~ 2 ) { sout | i | j; }993 for ( i; 5 ®:® j; -5 -~ @ ~ 2 ®:® k; 1.5 ~ @) { sout | i | j | k; }994 for ( i; 5 ®:® j; -5 -~ @ ~ 2 ®:® k; 1.5 ~ @) { sout | i | j | k; }995 for ( i; 5 ®:® k; 1.5 ~ @ ®:® j; -5 -~ @~ 2 ) { sout | i | j | k; }996 for ( @i; start ~ comp ~ inc + 1@ ) { sout | i; } 997 for ( i; 1 ~ $\R{@}$ ) { if ( i > 10 ) break; sout | i; } 998 for ( i; 10 -~ $\R{@}$ ) { if ( i < 0 ) break; sout | i; } 999 for ( i; 2 ~ $\R{@}$ ~ 2 ) { if ( i > 10 ) break; sout | i; } 1000 for ( i; 2.1 ~ $\R{@}$ ~ $\R{@}$ ) { if ( i > 10.5 ) break; sout | i; i += 1.7; } 1001 for ( i; 10 -~ $\R{@}$ ~ 2 ) { if ( i < 0 ) break; sout | i; } 1002 for ( i; 12.1 ~ $\R{@}$ ~ $\R{@}$ ) { if ( i < 2.5 ) break; sout | i; i -= 1.7; } 1003 for ( i; 5 @:@ j; -5 ~ $@$ ) { sout | i | j; } 1004 for ( i; 5 @:@ j; -5 -~ $@$ ) { sout | i | j; } 1005 for ( i; 5 @:@ j; -5 ~ $@$ ~ 2 ) { sout | i | j; } 1006 for ( i; 5 @:@ j; -5 -~ $@$ ~ 2 ) { sout | i | j; } 1007 for ( i; 5 @:@ j; -5 ~ $@$ ) { sout | i | j; } 1008 for ( i; 5 @:@ j; -5 -~ $@$ ) { sout | i | j; } 1009 for ( i; 5 @:@ j; -5 ~ $@$ ~ 2 ) { sout | i | j; } 1010 for ( i; 5 @:@ j; -5 -~ $@$ ~ 2 ) { sout | i | j; } 1011 for ( i; 5 @:@ j; -5 -~ $@$ ~ 2 @:@ k; 1.5 ~ $@$ ) { sout | i | j | k; } 1012 for ( i; 5 @:@ j; -5 -~ $@$ ~ 2 @:@ k; 1.5 ~ $@$ ) { sout | i | j | k; } 1013 for ( i; 5 @:@ k; 1.5 ~ $@$ @:@ j; -5 -~ $@$ ~ 2 ) { sout | i | j | k; } 996 1014 \end{cfa} 997 1015 & … … 1056 1074 \subsection{Loop Control} 1057 1075 1058 The ©for©/©while©/©do-while© loop-control allows empty or simplified ranges (see Figure~\ref{f:LoopControlExamples}). 1059 \begin{itemize} 1076 Looping a fixed number of times, possibly with a loop index, occurs frequently. 1077 \CFA condenses simply looping to facilitate coding speed and safety. 1078 The ©for©/©while©/©do-while© loop-control is augmented as follows \see{examples in \VRef[Figure]{f:LoopControlExamples}}: 1079 \begin{itemize}[itemsep=0pt] 1080 \item 1081 ©0© is the implicit start value; 1082 \item 1083 ©1© is the implicit increment value. 1084 \item 1085 The up-to range uses operator ©+=© for increment; 1086 \item 1087 The down-to range uses operator ©-=© for decrement. 1060 1088 \item 1061 1089 The loop index is polymorphic in the type of the comparison value N (when the start value is implicit) or the start value M. 1090 \begin{cfa} 1091 for ( i; @5@ ) $\C[2.5in]{// typeof(5) i; 5 is comparison value}$ 1092 for ( i; @1.5@~5.5~0.5 ) $\C{// typeof(1.5) i; 1.5 is start value}$ 1093 \end{cfa} 1062 1094 \item 1063 1095 An empty conditional implies comparison value of ©1© (true). 1064 \item 1065 A comparison N is implicit up-to exclusive range [0,N©®)®©. 1066 \item 1067 A comparison ©=© N is implicit up-to inclusive range [0,N©®]®©. 1068 \item 1069 The up-to range M ©~©\index{~@©~©} N means exclusive range [M,N©®)®©. 1070 \item 1071 The up-to range M ©~=©\index{~=@©~=©} N means inclusive range [M,N©®]®©. 1072 \item 1073 The down-to range M ©-~©\index{-~@©-~©} N means exclusive range [N,M©®)®©. 1074 \item 1075 The down-to range M ©-~=©\index{-~=@©-~=©} N means inclusive range [N,M©®]®©. 1076 \item 1077 ©0© is the implicit start value; 1078 \item 1079 ©1© is the implicit increment value. 1080 \item 1081 The up-to range uses operator ©+=© for increment; 1082 \item 1083 The down-to range uses operator ©-=© for decrement. 1096 \begin{cfa} 1097 while ( $\R{/*empty*/}$ ) $\C{// while ( true )}$ 1098 for ( $\R{/*empty*/}$ ) $\C{// for ( ; true; )}$ 1099 do ... while ( $\R{/*empty*/}$ ) $\C{// do ... while ( true )}$ 1100 \end{cfa} 1101 \item 1102 A comparison N is implicit up-to exclusive range [0,N\R{)}. 1103 \begin{cfa} 1104 for ( @5@ ) $\C{// for ( typeof(5) i; i < 5; i += 1 )}$ 1105 \end{cfa} 1106 \item 1107 A comparison ©=© N is implicit up-to inclusive range [0,N\R{]}. 1108 \begin{cfa} 1109 for ( @=@5 ) $\C{// for ( typeof(5) i; i <= 5; i += 1 )}$ 1110 \end{cfa} 1111 \item 1112 The up-to range M ©~©\index{~@©~©} N means exclusive range [M,N\R{)}. 1113 \begin{cfa} 1114 for ( 1@~@5 ) $\C{// for ( typeof(1) i = 1; i < 5; i += 1 )}$ 1115 \end{cfa} 1116 \item 1117 The up-to range M ©~=©\index{~=@©~=©} N means inclusive range [M,N\R{]}. 1118 \begin{cfa} 1119 for ( 1@~=@5 ) $\C{// for ( typeof(1) i = 1; i <= 5; i += 1 )}$ 1120 \end{cfa} 1121 \item 1122 The down-to range M ©-~©\index{-~@©-~©} N means exclusive range [N,M\R{)}. 1123 \begin{cfa} 1124 for ( 1@-~@5 ) $\C{// for ( typeof(1) i = 5; i > 0; i -= 1 )}$ 1125 \end{cfa} 1126 \item 1127 The down-to range M ©-~=©\index{-~=@©-~=©} N means inclusive range [N,M\R{]}. 1128 \begin{cfa} 1129 for ( 1@-~=@5 ) $\C{// for ( typeof(1) i = 5; i >= 0; i -= 1 )}$ 1130 \end{cfa} 1084 1131 \item 1085 1132 ©@© means put nothing in this field. 1133 \begin{cfa} 1134 for ( 1~$\R{@}$~2 ) $\C{// for ( typeof(1) i = 1; /*empty*/; i += 2 )}$ 1135 \end{cfa} 1086 1136 \item 1087 1137 ©:© means start another index. 1138 \begin{cfa} 1139 for ( i; 5 @:@ j; 2~12~3 ) $\C{// for ( typeof(i) i = 1, j = 2; i < 5 \&\& j < 12; i += 1, j += 3 )}\CRT$ 1140 \end{cfa} 1088 1141 \end{itemize} 1089 1142 … … 1092 1145 \subsection{\texorpdfstring{Labelled \LstKeywordStyle{continue} / \LstKeywordStyle{break} Statement}{Labelled continue / break Statement}} 1093 1146 1094 While C provides ©continue© and ©break© statements for altering control flow, bothare restricted to one level of nesting for a particular control structure.1095 Unfortunately, this restriction forces programmers to use \Indexc{goto} to achieve the equivalent control-flow for more than one level of nesting.1147 C ©continue© and ©break© statements, for altering control flow, are restricted to one level of nesting for a particular control structure. 1148 This restriction forces programmers to use \Indexc{goto} to achieve the equivalent control-flow for more than one level of nesting. 1096 1149 To prevent having to switch to the ©goto©, \CFA extends the \Indexc{continue}\index{continue@©continue©!labelled}\index{labelled!continue@©continue©} and \Indexc{break}\index{break@©break©!labelled}\index{labelled!break@©break©} with a target label to support static multi-level exit\index{multi-level exit}\index{static multi-level exit}~\cite{Buhr85}, as in Java. 1097 1150 For both ©continue© and ©break©, the target label must be directly associated with a ©for©, ©while© or ©do© statement; 1098 1151 for ©break©, the target label can also be associated with a ©switch©, ©if© or compound (©{}©) statement. 1099 \VRef[Figure]{f:MultiLevelExit} shows ©continue© and ©break© indicating the specific control structure, and the corresponding C program using only©goto© and labels.1152 \VRef[Figure]{f:MultiLevelExit} shows a comparison between labelled ©continue© and ©break© and the corresponding C equivalent using ©goto© and labels. 1100 1153 The innermost loop has 8 exit points, which cause continuation or termination of one or more of the 7 \Index{nested control-structure}s. 1101 1154 … … 1104 1157 \begin{lrbox}{\myboxA} 1105 1158 \begin{cfa}[tabsize=3] 1106 ®Compound:®{1107 ®Try:®try {1108 ®For:®for ( ... ) {1109 ®While:®while ( ... ) {1110 ®Do:®do {1111 ®If:®if ( ... ) {1112 ®Switch:®switch ( ... ) {1159 @Compound:@ { 1160 @Try:@ try { 1161 @For:@ for ( ... ) { 1162 @While:@ while ( ... ) { 1163 @Do:@ do { 1164 @If:@ if ( ... ) { 1165 @Switch:@ switch ( ... ) { 1113 1166 case 3: 1114 ®break Compound®;1115 ®break Try®;1116 ®break For®; /* or */ ®continue For®;1117 ®break While®; /* or */ ®continue While®;1118 ®break Do®; /* or */ ®continue Do®;1119 ®break If®;1120 ®break Switch®;1167 @break Compound@; 1168 @break Try@; 1169 @break For@; /* or */ @continue For@; 1170 @break While@; /* or */ @continue While@; 1171 @break Do@; /* or */ @continue Do@; 1172 @break If@; 1173 @break Switch@; 1121 1174 } // switch 1122 1175 } else { 1123 ... ®break If®; ... // terminate if1176 ... @break If@; ... // terminate if 1124 1177 } // if 1125 1178 } while ( ... ); // do 1126 1179 } // while 1127 1180 } // for 1128 } ®finally®{ // always executed1181 } @finally@ { // always executed 1129 1182 } // try 1130 1183 } // compound … … 1136 1189 { 1137 1190 1138 ®ForC:®for ( ... ) {1139 ®WhileC:®while ( ... ) {1140 ®DoC:®do {1191 @ForC:@ for ( ... ) { 1192 @WhileC:@ while ( ... ) { 1193 @DoC:@ do { 1141 1194 if ( ... ) { 1142 1195 switch ( ... ) { 1143 1196 case 3: 1144 ®goto Compound®;1145 ®goto Try®;1146 ®goto ForB®; /* or */ ®goto ForC®;1147 ®goto WhileB®; /* or */ ®goto WhileC®;1148 ®goto DoB®; /* or */ ®goto DoC®;1149 ®goto If®;1150 ®goto Switch®;1151 } ®Switch:®;1197 @goto Compound@; 1198 @goto Try@; 1199 @goto ForB@; /* or */ @goto ForC@; 1200 @goto WhileB@; /* or */ @goto WhileC@; 1201 @goto DoB@; /* or */ @goto DoC@; 1202 @goto If@; 1203 @goto Switch@; 1204 } @Switch:@ ; 1152 1205 } else { 1153 ... ®goto If®; ... // terminate if1154 } ®If:®;1155 } while ( ... ); ®DoB:®;1156 } ®WhileB:®;1157 } ®ForB:®;1158 1159 1160 } ®Compound:®;1206 ... @goto If@; ... // terminate if 1207 } @If:@; 1208 } while ( ... ); @DoB:@ ; 1209 } @WhileB:@ ; 1210 } @ForB:@ ; 1211 1212 1213 } @Compound:@ ; 1161 1214 \end{cfa} 1162 1215 \end{lrbox} 1163 1216 1164 1217 \subfloat[\CFA]{\label{f:CFibonacci}\usebox\myboxA} 1165 \hspace{ 2pt}1218 \hspace{3pt} 1166 1219 \vrule 1167 \hspace{ 2pt}1220 \hspace{3pt} 1168 1221 \subfloat[C]{\label{f:CFAFibonacciGen}\usebox\myboxB} 1169 1222 \caption{Multi-level Exit} … … 1180 1233 This restriction prevents missing declarations and/or initializations at the start of a control structure resulting in undefined behaviour. 1181 1234 \end{itemize} 1182 The advantage of the labelled ©continue©/©break© is allowing static multi-level exits without having to use the ©goto© statement, and tying control flow to the target control structure rather than an arbitrary point in a program .1235 The advantage of the labelled ©continue©/©break© is allowing static multi-level exits without having to use the ©goto© statement, and tying control flow to the target control structure rather than an arbitrary point in a program via a label. 1183 1236 Furthermore, the location of the label at the \emph{beginning} of the target control structure informs the reader (\Index{eye candy}) that complex control-flow is occurring in the body of the control structure. 1184 1237 With ©goto©, the label is at the end of the control structure, which fails to convey this important clue early enough to the reader. … … 1187 1240 1188 1241 1189 %\s ection{\texorpdfstring{\protect\lstinline@with@ Statement}{with Statement}}1190 \s ection{\texorpdfstring{\LstKeywordStyle{with} Statement}{with Statement}}1242 %\subsection{\texorpdfstring{\protect\lstinline@with@ Statement}{with Statement}} 1243 \subsection{\texorpdfstring{\LstKeywordStyle{with} Statement}{with Statement}} 1191 1244 \label{s:WithStatement} 1192 1245 1193 Grouping heterogeneous data into \newterm{aggregate}s (structure/union) is a common programming practice, and an aggregate can be further organized into more complex structures, such as arrays and containers:1194 \begin{cfa} 1195 struct S { §\C{// aggregate}§1196 char c; §\C{// fields}§1197 int i;1198 double d;1246 Grouping heterogeneous data into an \newterm{aggregate} (structure/union) is a common programming practice, and aggregates may be nested: 1247 \begin{cfa} 1248 struct Person { $\C{// aggregate}$ 1249 struct Name { char first[20], last[20]; } name $\C{// nesting}$ 1250 struct Address { ... } address $\C{// nesting}$ 1251 int sex; 1199 1252 }; 1200 S s, as[10]; 1201 \end{cfa} 1202 However, functions manipulating aggregates must repeat the aggregate name to access its containing fields: 1203 \begin{cfa} 1204 void f( S s ) { 1205 ®s.®c; ®s.®i; ®s.®d; §\C{// access containing fields}§ 1206 } 1207 \end{cfa} 1208 which extends to multiple levels of qualification for nested aggregates. 1209 A similar situation occurs in object-oriented programming, \eg \CC: 1253 \end{cfa} 1254 Functions manipulating aggregates must repeat the aggregate name to access its containing fields. 1255 \begin{cfa} 1256 Person p 1257 @p.@name; @p.@address; @p.@sex; $\C{// access containing fields}$ 1258 \end{cfa} 1259 which extends to multiple levels of qualification for nested aggregates and multiple aggregates. 1260 \begin{cfa} 1261 struct Ticket { ... } t; 1262 @p.name@.first; @p.address@.street; $\C{// access nested fields}$ 1263 @t.@departure; @t.@cost; $\C{// access multiple aggregate}$ 1264 \end{cfa} 1265 Repeated aggregate qualification is tedious and makes code difficult to read. 1266 Therefore, reducing aggregate qualification is a useful language design goal. 1267 1268 C allows unnamed nested aggregates that open their scope into the containing aggregate. 1269 This feature is used to group fields for attributes and/or with ©union© aggregates. 1270 \begin{cfa} 1271 struct S { 1272 struct { int g, h; } __attribute__(( aligned(64) )); 1273 int tag; 1274 union { 1275 struct { char c1, c2; } __attribute__(( aligned(128) )); 1276 struct { int i1, i2; }; 1277 struct { double d1, d2; }; 1278 }; 1279 }; 1280 s.g; s.h; s.tag; s.c1; s.c2; s.i1; s.i2; s.d1; s.d2; 1281 \end{cfa} 1282 1283 Object-oriented languages reduce qualification for class variables within member functions, \eg \CC: 1210 1284 \begin{C++} 1211 1285 struct S { 1212 char c; §\C{// fields}§ 1213 int i; 1214 double d; 1215 void f() { §\C{// implicit ``this'' aggregate}§ 1216 ®this->®c; ®this->®i; ®this->®d; §\C{// access containing fields}§ 1286 char @c@; int @i@; double @d@; 1287 void f( /* S * this */ ) { $\C{// implicit ``this'' parameter}$ 1288 @c@; @i@; @d@; $\C{// this->c; this->i; this->d;}$ 1217 1289 } 1218 1290 } 1219 1291 \end{C++} 1220 Object-oriented nesting of member functions in a \lstinline[language=C++]@class/struct@ allows eliding \lstinline[language=C++]@this->@ because of lexical scoping. 1221 However, for other aggregate parameters, qualification is necessary: 1222 \begin{cfa} 1223 struct T { double m, n; }; 1224 int S::f( T & t ) { §\C{// multiple aggregate parameters}§ 1225 c; i; d; §\C{\color{red}// this--{\textgreater}.c, this--{\textgreater}.i, this--{\textgreater}.d}§ 1226 ®t.®m; ®t.®n; §\C{// must qualify}§ 1227 } 1228 \end{cfa} 1229 1230 To simplify the programmer experience, \CFA provides a ©with© statement (see Pascal~\cite[\S~4.F]{Pascal}) to elide aggregate qualification to fields by opening a scope containing the field identifiers. 1231 Hence, the qualified fields become variables with the side-effect that it is easier to optimizing field references in a block. 1232 \begin{cfa} 1233 void f( S & this ) ®with ( this )® { §\C{// with statement}§ 1234 c; i; d; §\C{\color{red}// this.c, this.i, this.d}§ 1292 In general, qualification is elided for the variables and functions in the lexical scopes visible from a member function. 1293 However, qualification is necessary for name shadowing and explicit aggregate parameters. 1294 \begin{cfa} 1295 struct T { 1296 char @m@; int @i@; double @n@; $\C{// derived class variables}$ 1297 }; 1298 struct S : public T { 1299 char @c@; int @i@; double @d@; $\C{// class variables}$ 1300 void g( double @d@, T & t ) { 1301 d; @t@.m; @t@.i; @t@.n; $\C{// function parameter}$ 1302 c; i; @this->@d; @S::@d; $\C{// class S variables}$ 1303 m; @T::@i; n; $\C{// class T variables}$ 1304 } 1305 }; 1306 \end{cfa} 1307 Note the three different forms of qualification syntax in \CC, ©.©, ©->©, ©::©, which is confusing. 1308 1309 Since \CFA in not object-oriented, it has no implicit parameter with its implicit qualification. 1310 Instead \CFA introduces a general mechanism using the ©with© statement \see{Pascal~\cite[\S~4.F]{Pascal}} to explicitly elide aggregate qualification by opening a scope containing the field identifiers. 1311 Hence, the qualified fields become variables with the side-effect that it is simpler to write, easier to read, and optimize field references in a block. 1312 \begin{cfa} 1313 void f( S & this ) @with ( this )@ { $\C{// with statement}$ 1314 @c@; @i@; @d@; $\C{// this.c, this.i, this.d}$ 1235 1315 } 1236 1316 \end{cfa} 1237 1317 with the generality of opening multiple aggregate-parameters: 1238 1318 \begin{cfa} 1239 void f( S & s, T & t ) ®with ( s, t )® { §\C{// multiple aggregate parameters}§ 1240 c; i; d; §\C{\color{red}// s.c, s.i, s.d}§ 1241 m; n; §\C{\color{red}// t.m, t.n}§ 1242 } 1243 \end{cfa} 1244 1245 In detail, the ©with© statement has the form: 1246 \begin{cfa} 1247 §\emph{with-statement}§: 1248 'with' '(' §\emph{expression-list}§ ')' §\emph{compound-statement}§ 1249 \end{cfa} 1250 and may appear as the body of a function or nested within a function body. 1251 Each expression in the expression-list provides a type and object. 1252 The type must be an aggregate type. 1319 void g( S & s, T & t ) @with ( s, t )@ { $\C{// multiple aggregate parameters}$ 1320 c; @s.@i; d; $\C{// s.c, s.i, s.d}$ 1321 m; @t.@i; n; $\C{// t.m, t.i, t.n}$ 1322 } 1323 \end{cfa} 1324 where qualification is only necessary to disambiguate the shadowed variable ©i©. 1325 1326 In detail, the ©with© statement may appear as the body of a function or nested within a function body. 1327 The ©with© clause takes a list of expressions, where each expression provides an aggregate type and object. 1253 1328 (Enumerations are already opened.) 1254 The object is the implicit qualifier for the open structure-fields. 1255 1329 To open a pointer type, the pointer must be dereferenced to obtain a reference to the aggregate type. 1330 \begin{cfa} 1331 S * sp; 1332 with ( *sp ) { ... } 1333 \end{cfa} 1334 The expression object is the implicit qualifier for the open structure-fields. 1335 \CFA's ability to overload variables \see{\VRef{s:VariableOverload}} and use the left-side of assignment in type resolution means most fields with the same name but different types are automatically disambiguated, eliminating qualification. 1256 1336 All expressions in the expression list are open in parallel within the compound statement. 1257 1337 This semantic is different from Pascal, which nests the openings from left to right. 1258 1338 The difference between parallel and nesting occurs for fields with the same name and type: 1259 1339 \begin{cfa} 1260 struct S { int ®i®; int j; double m; } s, w; 1261 struct T { int ®i®; int k; int m; } t, w; 1262 with ( s, t ) { 1263 j + k; §\C{// unambiguous, s.j + t.k}§ 1264 m = 5.0; §\C{// unambiguous, t.m = 5.0}§ 1265 m = 1; §\C{// unambiguous, s.m = 1}§ 1266 int a = m; §\C{// unambiguous, a = s.i }§ 1267 double b = m; §\C{// unambiguous, b = t.m}§ 1268 int c = s.i + t.i; §\C{// unambiguous, qualification}§ 1269 (double)m; §\C{// unambiguous, cast}§ 1270 } 1271 \end{cfa} 1272 For parallel semantics, both ©s.i© and ©t.i© are visible, so ©i© is ambiguous without qualification; 1273 for nested semantics, ©t.i© hides ©s.i©, so ©i© implies ©t.i©. 1274 \CFA's ability to overload variables means fields with the same name but different types are automatically disambiguated, eliminating most qualification when opening multiple aggregates. 1275 Qualification or a cast is used to disambiguate. 1276 1277 There is an interesting problem between parameters and the function-body ©with©, \eg: 1278 \begin{cfa} 1279 void ?{}( S & s, int i ) with ( s ) { §\C{// constructor}§ 1280 ®s.i = i;® j = 3; m = 5.5; §\C{// initialize fields}§ 1340 struct Q { int @i@; int k; int @m@; } q, w; 1341 struct R { int @i@; int j; double @m@; } r, w; 1342 with ( r, q ) { 1343 j + k; $\C{// unambiguous, r.j + q.k}$ 1344 m = 5.0; $\C{// unambiguous, q.m = 5.0}$ 1345 m = 1; $\C{// unambiguous, r.m = 1}$ 1346 int a = m; $\C{// unambiguous, a = r.i }$ 1347 double b = m; $\C{// unambiguous, b = q.m}$ 1348 int c = r.i + q.i; $\C{// disambiguate with qualification}$ 1349 (double)m; $\C{// disambiguate with cast}$ 1350 } 1351 \end{cfa} 1352 For parallel semantics, both ©r.i© and ©q.i© are visible, so ©i© is ambiguous without qualification; 1353 for nested semantics, ©q.i© hides ©r.i©, so ©i© implies ©q.i©. 1354 Pascal nested-semantics is possible by nesting ©with© statements. 1355 \begin{cfa} 1356 with ( r ) { 1357 i; $\C{// unambiguous, r.i}$ 1358 with ( q ) { 1359 i; $\C{// unambiguous, q.i}$ 1360 } 1361 } 1362 \end{cfa} 1363 A cast or qualification can be used to disambiguate variables within a ©with© \emph{statement}. 1364 A cast can be used to disambiguate among overload variables in a ©with© \emph{expression}: 1365 \begin{cfa} 1366 with ( w ) { ... } $\C{// ambiguous, same name and no context}$ 1367 with ( (Q)w ) { ... } $\C{// unambiguous, cast}$ 1368 \end{cfa} 1369 Because there is no left-side in the ©with© expression to implicitly disambiguate between the ©w© variables, it is necessary to explicitly disambiguate by casting ©w© to type ©Q© or ©R©. 1370 1371 Finally, there is an interesting problem between parameters and the function-body ©with©, \eg: 1372 \begin{cfa} 1373 void ?{}( S & s, int i ) with ( s ) { $\C{// constructor}$ 1374 @s.i = i;@ j = 3; m = 5.5; $\C{// initialize fields}$ 1281 1375 } 1282 1376 \end{cfa} … … 1291 1385 and implicitly opened \emph{after} a function-body open, to give them higher priority: 1292 1386 \begin{cfa} 1293 void ?{}( S & s, int ®i® ) with ( s ) ®with( §\emph{\color{red}params}§ )® { 1294 s.i = ®i®; j = 3; m = 5.5; 1295 } 1296 \end{cfa} 1297 Finally, a cast may be used to disambiguate among overload variables in a ©with© expression: 1298 \begin{cfa} 1299 with ( w ) { ... } §\C{// ambiguous, same name and no context}§ 1300 with ( (S)w ) { ... } §\C{// unambiguous, cast}§ 1301 \end{cfa} 1302 and ©with© expressions may be complex expressions with type reference (see Section~\ref{s:References}) to aggregate: 1303 % \begin{cfa} 1304 % struct S { int i, j; } sv; 1305 % with ( sv ) { §\C{// implicit reference}§ 1306 % S & sr = sv; 1307 % with ( sr ) { §\C{// explicit reference}§ 1308 % S * sp = &sv; 1309 % with ( *sp ) { §\C{// computed reference}§ 1310 % i = 3; j = 4; §\C{\color{red}// sp--{\textgreater}i, sp--{\textgreater}j}§ 1311 % } 1312 % i = 2; j = 3; §\C{\color{red}// sr.i, sr.j}§ 1313 % } 1314 % i = 1; j = 2; §\C{\color{red}// sv.i, sv.j}§ 1315 % } 1316 % \end{cfa} 1317 1318 In \Index{object-oriented} programming, there is an implicit first parameter, often names \textbf{©self©} or \textbf{©this©}, which is elided. 1319 \begin{C++} 1320 class C { 1321 int i, j; 1322 int mem() { §\C{\color{red}// implicit "this" parameter}§ 1323 i = 1; §\C{\color{red}// this->i}§ 1324 j = 2; §\C{\color{red}// this->j}§ 1325 } 1326 } 1327 \end{C++} 1328 Since \CFA is non-object-oriented, the equivalent object-oriented program looks like: 1329 \begin{cfa} 1330 struct S { int i, j; }; 1331 int mem( S & ®this® ) { §\C{// explicit "this" parameter}§ 1332 ®this.®i = 1; §\C{// "this" is not elided}§ 1333 ®this.®j = 2; 1334 } 1335 \end{cfa} 1336 but it is cumbersome having to write ``©this.©'' many times in a member. 1337 1338 \CFA provides a ©with© clause/statement (see Pascal~\cite[\S~4.F]{Pascal}) to elided the "©this.©" by opening a scope containing field identifiers, changing the qualified fields into variables and giving an opportunity for optimizing qualified references. 1339 \begin{cfa} 1340 int mem( S & this ) ®with( this )® { §\C{// with clause}§ 1341 i = 1; §\C{\color{red}// this.i}§ 1342 j = 2; §\C{\color{red}// this.j}§ 1343 } 1344 \end{cfa} 1345 which extends to multiple routine parameters: 1346 \begin{cfa} 1347 struct T { double m, n; }; 1348 int mem2( S & this1, T & this2 ) ®with( this1, this2 )® { 1349 i = 1; j = 2; 1350 m = 1.0; n = 2.0; 1351 } 1352 \end{cfa} 1353 1354 The statement form is used within a block: 1355 \begin{cfa} 1356 int foo() { 1357 struct S1 { ... } s1; 1358 struct S2 { ... } s2; 1359 ®with( s1 )® { §\C{// with statement}§ 1360 // access fields of s1 without qualification 1361 ®with s2® { §\C{// nesting}§ 1362 // access fields of s1 and s2 without qualification 1363 } 1364 } 1365 ®with s1, s2® { 1366 // access unambiguous fields of s1 and s2 without qualification 1367 } 1368 } 1369 \end{cfa} 1370 1371 When opening multiple structures, fields with the same name and type are ambiguous and must be fully qualified. 1372 For fields with the same name but different type, context/cast can be used to disambiguate. 1373 \begin{cfa} 1374 struct S { int i; int j; double m; } a, c; 1375 struct T { int i; int k; int m } b, c; 1376 with( a, b ) 1377 { 1378 } 1379 \end{cfa} 1380 1381 \begin{comment} 1382 The components in the "with" clause 1383 1384 with a, b, c { ... } 1385 1386 serve 2 purposes: each component provides a type and object. The type must be a 1387 structure type. Enumerations are already opened, and I think a union is opened 1388 to some extent, too. (Or is that just unnamed unions?) The object is the target 1389 that the naked structure-fields apply to. The components are open in "parallel" 1390 at the scope of the "with" clause/statement, so opening "a" does not affect 1391 opening "b", etc. This semantic is different from Pascal, which nests the 1392 openings. 1393 1394 Having said the above, it seems reasonable to allow a "with" component to be an 1395 expression. The type is the static expression-type and the object is the result 1396 of the expression. Again, the type must be an aggregate. Expressions require 1397 parenthesis around the components. 1398 1399 with( a, b, c ) { ... } 1400 1401 Does this now make sense? 1402 1403 Having written more CFA code, it is becoming clear to me that I *really* want 1404 the "with" to be implemented because I hate having to type all those object 1405 names for fields. It's a great way to drive people away from the language. 1406 \end{comment} 1387 void ?{}( S & s, int @i@ ) with ( s ) @with( $\emph{\R{params}}$ )@ { // syntax not allowed, illustration only 1388 s.i = @i@; j = 3; m = 5.5; 1389 } 1390 \end{cfa} 1391 This implicit semantic matches with programmer expectation. 1392 1407 1393 1408 1394 … … 1414 1400 Non-local transfer can cause stack unwinding, \ie non-local routine termination, depending on the kind of raise. 1415 1401 \begin{cfa} 1416 exception_t E {}; §\C{// exception type}§1402 exception_t E {}; $\C{// exception type}$ 1417 1403 void f(...) { 1418 ... throw E{}; ... §\C{// termination}§1419 ... throwResume E{}; ... §\C{// resumption}§1404 ... throw E{}; ... $\C{// termination}$ 1405 ... throwResume E{}; ... $\C{// resumption}$ 1420 1406 } 1421 1407 try { 1422 1408 f(...); 1423 } catch( E e ; §boolean-predicate§ ) { §\C{// termination handler}§1409 } catch( E e ; $boolean-predicate$ ) { $\C{// termination handler}$ 1424 1410 // recover and continue 1425 } catchResume( E e ; §boolean-predicate§ ) { §\C{// resumption handler}§1411 } catchResume( E e ; $boolean-predicate$ ) { $\C{// resumption handler}$ 1426 1412 // repair and return 1427 1413 } finally { … … 1430 1416 \end{cfa} 1431 1417 The kind of raise and handler match: ©throw© with ©catch© and ©throwResume© with ©catchResume©. 1432 Then the exception type must match along with any addit onal predicate must be true.1418 Then the exception type must match along with any additional predicate must be true. 1433 1419 The ©catch© and ©catchResume© handlers may appear in any oder. 1434 1420 However, the ©finally© clause must appear at the end of the ©try© statement. … … 1483 1469 For example, a routine returning a \Index{pointer} to an array of integers is defined and used in the following way: 1484 1470 \begin{cfa} 1485 int ®(*®f®())[®5®]® {...}; §\C{// definition}§1486 ... ®(*®f®())[®3®]® += 1; §\C{// usage}§1471 int @(*@f@())[@5@]@ {...}; $\C{// definition}$ 1472 ... @(*@f@())[@3@]@ += 1; $\C{// usage}$ 1487 1473 \end{cfa} 1488 1474 Essentially, the return type is wrapped around the routine name in successive layers (like an \Index{onion}). … … 1499 1485 \begin{tabular}{@{}l@{\hspace{3em}}l@{}} 1500 1486 \multicolumn{1}{c@{\hspace{3em}}}{\textbf{\CFA}} & \multicolumn{1}{c}{\textbf{C}} \\ 1501 \begin{cfa} 1502 ß[5] *ß ®int®x1;1503 ß* [5]ß ®int®x2;1504 ß[* [5] int]ß f®( int p )®;1487 \begin{cfa}[moredelim={**[is][\color{blue}]{\#}{\#}}] 1488 #[5] *# @int@ x1; 1489 #* [5]# @int@ x2; 1490 #[* [5] int]# f@( int p )@; 1505 1491 \end{cfa} 1506 1492 & 1507 \begin{cfa} 1508 ®int® ß*ß x1 ß[5]ß;1509 ®int® ß(*ßx2ß)[5]ß;1510 ßint (*ßf®( int p )®ß)[5]ß;1493 \begin{cfa}[moredelim={**[is][\color{blue}]{\#}{\#}}] 1494 @int@ #*# x1 #[5]#; 1495 @int@ #(*#x2#)[5]#; 1496 #int (*#f@( int p )@#)[5]#; 1511 1497 \end{cfa} 1512 1498 \end{tabular} … … 1520 1506 \multicolumn{1}{c@{\hspace{3em}}}{\textbf{\CFA}} & \multicolumn{1}{c}{\textbf{C}} \\ 1521 1507 \begin{cfa} 1522 ®*®int x, y;1508 @*@ int x, y; 1523 1509 \end{cfa} 1524 1510 & 1525 1511 \begin{cfa} 1526 int ®*®x, ®*®y;1512 int @*@x, @*@y; 1527 1513 \end{cfa} 1528 1514 \end{tabular} … … 1533 1519 \multicolumn{1}{c@{\hspace{3em}}}{\textbf{\CFA}} & \multicolumn{1}{c}{\textbf{C}} \\ 1534 1520 \begin{cfa} 1535 ®*®int x;1521 @*@ int x; 1536 1522 int y; 1537 1523 \end{cfa} 1538 1524 & 1539 1525 \begin{cfa} 1540 int ®*®x, y;1526 int @*@x, y; 1541 1527 1542 1528 \end{cfa} … … 1647 1633 1648 1634 \section{Pointer / Reference} 1635 \label{s:PointerReference} 1649 1636 1650 1637 C provides a \newterm{pointer type}; … … 1673 1660 & 1674 1661 \begin{cfa} 1675 int * ®const®x = (int *)1001662 int * @const@ x = (int *)100 1676 1663 *x = 3; // implicit dereference 1677 int * ®const®y = (int *)104;1664 int * @const@ y = (int *)104; 1678 1665 *y = *x; // implicit dereference 1679 1666 \end{cfa} … … 1713 1700 \begin{tabular}{@{}l@{\hspace{2em}}l@{}} 1714 1701 \begin{cfa} 1715 int x, y, ®*® p1, ®*® p2, ®**®p3;1716 p1 = ®&®x; // p1 points to x1702 int x, y, @*@ p1, @*@ p2, @**@ p3; 1703 p1 = @&@x; // p1 points to x 1717 1704 p2 = p1; // p2 points to x 1718 p1 = ®&®y; // p1 points to y1705 p1 = @&@y; // p1 points to y 1719 1706 p3 = &p2; // p3 points to p2 1720 1707 \end{cfa} … … 1728 1715 For example, \Index*{Algol68}~\cite{Algol68} infers pointer dereferencing to select the best meaning for each pointer usage 1729 1716 \begin{cfa} 1730 p2 = p1 + x; §\C{// compiler infers *p2 = *p1 + x;}§1717 p2 = p1 + x; $\C{// compiler infers *p2 = *p1 + x;}$ 1731 1718 \end{cfa} 1732 1719 Algol68 infers the following dereferencing ©*p2 = *p1 + x©, because adding the arbitrary integer value in ©x© to the address of ©p1© and storing the resulting address into ©p2© is an unlikely operation. … … 1736 1723 In C, objects of pointer type always manipulate the pointer object's address: 1737 1724 \begin{cfa} 1738 p1 = p2; §\C{// p1 = p2\ \ rather than\ \ *p1 = *p2}§1739 p2 = p1 + x; §\C{// p2 = p1 + x\ \ rather than\ \ *p2 = *p1 + x}§1725 p1 = p2; $\C{// p1 = p2\ \ rather than\ \ *p1 = *p2}$ 1726 p2 = p1 + x; $\C{// p2 = p1 + x\ \ rather than\ \ *p2 = *p1 + x}$ 1740 1727 \end{cfa} 1741 1728 even though the assignment to ©p2© is likely incorrect, and the programmer probably meant: 1742 1729 \begin{cfa} 1743 p1 = p2; §\C{// pointer address assignment}§1744 ®*®p2 = ®*®p1 + x; §\C{// pointed-to value assignment / operation}§ 1730 p1 = p2; $\C{// pointer address assignment}$ 1731 @*@p2 = @*@p1 + x; $\C{// pointed-to value assignment / operation}$ 1745 1732 \end{cfa} 1746 1733 The C semantics work well for situations where manipulation of addresses is the primary meaning and data is rarely accessed, such as storage management (©malloc©/©free©). … … 1758 1745 To support this common case, a reference type is introduced in \CFA, denoted by ©&©, which is the opposite dereference semantics to a pointer type, making the value at the pointed-to location the implicit semantics for dereferencing (similar but not the same as \CC \Index{reference type}s). 1759 1746 \begin{cfa} 1760 int x, y, ®&® r1, ®&® r2, ®&&®r3;1761 ®&®r1 = &x; §\C{// r1 points to x}§ 1762 ®&®r2 = &r1; §\C{// r2 points to x}§ 1763 ®&®r1 = &y; §\C{// r1 points to y}§ 1764 ®&&®r3 = ®&®&r2; §\C{// r3 points to r2}§ 1765 r2 = ((r1 + r2) * (r3 - r1)) / (r3 - 15); §\C{// implicit dereferencing}§1747 int x, y, @&@ r1, @&@ r2, @&&@ r3; 1748 @&@r1 = &x; $\C{// r1 points to x}$ 1749 @&@r2 = &r1; $\C{// r2 points to x}$ 1750 @&@r1 = &y; $\C{// r1 points to y}$ 1751 @&&@r3 = @&@&r2; $\C{// r3 points to r2}$ 1752 r2 = ((r1 + r2) * (r3 - r1)) / (r3 - 15); $\C{// implicit dereferencing}$ 1766 1753 \end{cfa} 1767 1754 Except for auto-dereferencing by the compiler, this reference example is the same as the previous pointer example. … … 1769 1756 One way to conceptualize a reference is via a rewrite rule, where the compiler inserts a dereference operator before the reference variable for each reference qualifier in a declaration, so the previous example becomes: 1770 1757 \begin{cfa} 1771 ®*®r2 = ((®*®r1 + ®*®r2) ®*® (®**®r3 - ®*®r1)) / (®**®r3 - 15);1758 @*@r2 = ((@*@r1 + @*@r2) @*@ (@**@r3 - @*@r1)) / (@**@r3 - 15); 1772 1759 \end{cfa} 1773 1760 When a reference operation appears beside a dereference operation, \eg ©&*©, they cancel out. … … 1778 1765 For a \CFA reference type, the cancellation on the left-hand side of assignment leaves the reference as an address (\Index{lvalue}): 1779 1766 \begin{cfa} 1780 (& ®*®)r1 = &x; §\C{// (\&*) cancel giving address in r1 not variable pointed-to by r1}§1767 (&@*@)r1 = &x; $\C{// (\&*) cancel giving address in r1 not variable pointed-to by r1}$ 1781 1768 \end{cfa} 1782 1769 Similarly, the address of a reference can be obtained for assignment or computation (\Index{rvalue}): 1783 1770 \begin{cfa} 1784 (&(& ®*®)®*®)r3 = &(&®*®)r2; §\C{// (\&*) cancel giving address in r2, (\&(\&*)*) cancel giving address in r3}§1771 (&(&@*@)@*@)r3 = &(&@*@)r2; $\C{// (\&*) cancel giving address in r2, (\&(\&*)*) cancel giving address in r3}$ 1785 1772 \end{cfa} 1786 1773 Cancellation\index{cancellation!pointer/reference}\index{pointer!cancellation} works to arbitrary depth. … … 1790 1777 int x, *p1 = &x, **p2 = &p1, ***p3 = &p2, 1791 1778 &r1 = x, &&r2 = r1, &&&r3 = r2; 1792 ***p3 = 3; §\C{// change x}§1793 r3 = 3; §\C{// change x, ***r3}§1794 **p3 = ...; §\C{// change p1}§1795 &r3 = ...; §\C{// change r1, (\&*)**r3, 1 cancellation}§1796 *p3 = ...; §\C{// change p2}§1797 &&r3 = ...; §\C{// change r2, (\&(\&*)*)*r3, 2 cancellations}§1798 &&&r3 = p3; §\C{// change r3 to p3, (\&(\&(\&*)*)*)r3, 3 cancellations}§1779 ***p3 = 3; $\C{// change x}$ 1780 r3 = 3; $\C{// change x, ***r3}$ 1781 **p3 = ...; $\C{// change p1}$ 1782 &r3 = ...; $\C{// change r1, (\&*)**r3, 1 cancellation}$ 1783 *p3 = ...; $\C{// change p2}$ 1784 &&r3 = ...; $\C{// change r2, (\&(\&*)*)*r3, 2 cancellations}$ 1785 &&&r3 = p3; $\C{// change r3 to p3, (\&(\&(\&*)*)*)r3, 3 cancellations}$ 1799 1786 \end{cfa} 1800 1787 Furthermore, both types are equally performant, as the same amount of dereferencing occurs for both types. … … 1803 1790 As for a pointer type, a reference type may have qualifiers: 1804 1791 \begin{cfa} 1805 const int cx = 5; §\C{// cannot change cx;}§1806 const int & cr = cx; §\C{// cannot change what cr points to}§1807 ®&®cr = &cx; §\C{// can change cr}§ 1808 cr = 7; §\C{// error, cannot change cx}§1809 int & const rc = x; §\C{// must be initialized}§1810 ®&®rc = &x; §\C{// error, cannot change rc}§ 1811 const int & const crc = cx; §\C{// must be initialized}§1812 crc = 7; §\C{// error, cannot change cx}§1813 ®&®crc = &cx; §\C{// error, cannot change crc}§ 1792 const int cx = 5; $\C{// cannot change cx;}$ 1793 const int & cr = cx; $\C{// cannot change what cr points to}$ 1794 @&@cr = &cx; $\C{// can change cr}$ 1795 cr = 7; $\C{// error, cannot change cx}$ 1796 int & const rc = x; $\C{// must be initialized}$ 1797 @&@rc = &x; $\C{// error, cannot change rc}$ 1798 const int & const crc = cx; $\C{// must be initialized}$ 1799 crc = 7; $\C{// error, cannot change cx}$ 1800 @&@crc = &cx; $\C{// error, cannot change crc}$ 1814 1801 \end{cfa} 1815 1802 Hence, for type ©& const©, there is no pointer assignment, so ©&rc = &x© is disallowed, and \emph{the address value cannot be the null pointer unless an arbitrary pointer is coerced\index{coercion} into the reference}: 1816 1803 \begin{cfa} 1817 int & const cr = *0; §\C{// where 0 is the int * zero}§1804 int & const cr = *0; $\C{// where 0 is the int * zero}$ 1818 1805 \end{cfa} 1819 1806 Note, constant reference-types do not prevent \Index{addressing errors} because of explicit storage-management: … … 1822 1809 cr = 5; 1823 1810 free( &cr ); 1824 cr = 7; §\C{// unsound pointer dereference}§1811 cr = 7; $\C{// unsound pointer dereference}$ 1825 1812 \end{cfa} 1826 1813 1827 1814 The position of the ©const© qualifier \emph{after} the pointer/reference qualifier causes confuse for C programmers. 1828 1815 The ©const© qualifier cannot be moved before the pointer/reference qualifier for C style-declarations; 1829 \CFA-style declarations (see \VRef{s:AlternativeDeclarations})attempt to address this issue:1816 \CFA-style declarations \see{\VRef{s:AlternativeDeclarations}} attempt to address this issue: 1830 1817 \begin{cquote} 1831 1818 \begin{tabular}{@{}l@{\hspace{3em}}l@{}} 1832 1819 \multicolumn{1}{c@{\hspace{3em}}}{\textbf{\CFA}} & \multicolumn{1}{c}{\textbf{C}} \\ 1833 1820 \begin{cfa} 1834 ®const® * ®const®* const int ccp;1835 ®const® & ®const®& const int ccr;1821 @const@ * @const@ * const int ccp; 1822 @const@ & @const@ & const int ccr; 1836 1823 \end{cfa} 1837 1824 & 1838 1825 \begin{cfa} 1839 const int * ®const® * ®const®ccp;1826 const int * @const@ * @const@ ccp; 1840 1827 1841 1828 \end{cfa} … … 1846 1833 Finally, like pointers, references are usable and composable with other type operators and generators. 1847 1834 \begin{cfa} 1848 int w, x, y, z, & ar[3] = { x, y, z }; §\C{// initialize array of references}§1849 &ar[1] = &w; §\C{// change reference array element}§1850 typeof( ar[1] ) p; §\C{// (gcc) is int, \ie the type of referenced object}§1851 typeof( &ar[1] ) q; §\C{// (gcc) is int \&, \ie the type of reference}§1852 sizeof( ar[1] ) == sizeof( int ); §\C{// is true, \ie the size of referenced object}§1853 sizeof( &ar[1] ) == sizeof( int *) §\C{// is true, \ie the size of a reference}§1835 int w, x, y, z, & ar[3] = { x, y, z }; $\C{// initialize array of references}$ 1836 &ar[1] = &w; $\C{// change reference array element}$ 1837 typeof( ar[1] ) p; $\C{// (gcc) is int, \ie the type of referenced object}$ 1838 typeof( &ar[1] ) q; $\C{// (gcc) is int \&, \ie the type of reference}$ 1839 sizeof( ar[1] ) == sizeof( int ); $\C{// is true, \ie the size of referenced object}$ 1840 sizeof( &ar[1] ) == sizeof( int *) $\C{// is true, \ie the size of a reference}$ 1854 1841 \end{cfa} 1855 1842 1856 1843 In contrast to \CFA reference types, \Index*[C++]{\CC{}}'s reference types are all ©const© references, preventing changes to the reference address, so only value assignment is possible, which eliminates half of the \Index{address duality}. 1857 1844 Also, \CC does not allow \Index{array}s\index{array!reference} of reference\footnote{ 1858 The reason for disallowing arrays of reference is unknown, but possibly comes from references being ethereal (like a textual macro), and hence, replaceable by the refer ant object.}1845 The reason for disallowing arrays of reference is unknown, but possibly comes from references being ethereal (like a textual macro), and hence, replaceable by the referent object.} 1859 1846 \Index*{Java}'s reference types to objects (all Java objects are on the heap) are like C pointers, which always manipulate the address, and there is no (bit-wise) object assignment, so objects are explicitly cloned by shallow or deep copying, which eliminates half of the address duality. 1860 1847 … … 1868 1855 Therefore, for pointer/reference initialization, the initializing value must be an address not a value. 1869 1856 \begin{cfa} 1870 int * p = &x; §\C{// assign address of x}§1871 ®int * p = x;® §\C{// assign value of x}§ 1872 int & r = x; §\C{// must have address of x}§1857 int * p = &x; $\C{// assign address of x}$ 1858 @int * p = x;@ $\C{// assign value of x}$ 1859 int & r = x; $\C{// must have address of x}$ 1873 1860 \end{cfa} 1874 1861 Like the previous example with C pointer-arithmetic, it is unlikely assigning the value of ©x© into a pointer is meaningful (again, a warning is usually given). … … 1879 1866 Similarly, when a reference type is used for a parameter/return type, the call-site argument does not require a reference operator for the same reason. 1880 1867 \begin{cfa} 1881 int & f( int & r ); §\C{// reference parameter and return}§1882 z = f( x ) + f( y ); §\C{// reference operator added, temporaries needed for call results}§1868 int & f( int & r ); $\C{// reference parameter and return}$ 1869 z = f( x ) + f( y ); $\C{// reference operator added, temporaries needed for call results}$ 1883 1870 \end{cfa} 1884 1871 Within routine ©f©, it is possible to change the argument by changing the corresponding parameter, and parameter ©r© can be locally reassigned within ©f©. … … 1893 1880 When a pointer/reference parameter has a ©const© value (immutable), it is possible to pass literals and expressions. 1894 1881 \begin{cfa} 1895 void f( ®const®int & cr );1896 void g( ®const®int * cp );1897 f( 3 ); g( ®&®3 );1898 f( x + y ); g( ®&®(x + y) );1882 void f( @const@ int & cr ); 1883 void g( @const@ int * cp ); 1884 f( 3 ); g( @&@3 ); 1885 f( x + y ); g( @&@(x + y) ); 1899 1886 \end{cfa} 1900 1887 Here, the compiler passes the address to the literal 3 or the temporary for the expression ©x + y©, knowing the argument cannot be changed through the parameter. … … 1907 1894 void f( int & r ); 1908 1895 void g( int * p ); 1909 f( 3 ); g( ®&®3 ); §\C{// compiler implicit generates temporaries}§1910 f( x + y ); g( ®&®(x + y) ); §\C{// compiler implicit generates temporaries}§1896 f( 3 ); g( @&@3 ); $\C{// compiler implicit generates temporaries}$ 1897 f( x + y ); g( @&@(x + y) ); $\C{// compiler implicit generates temporaries}$ 1911 1898 \end{cfa} 1912 1899 Essentially, there is an implicit \Index{rvalue} to \Index{lvalue} conversion in this case.\footnote{ … … 1919 1906 \begin{cfa} 1920 1907 void f( int i ); 1921 void (* fp)( int ); §\C{// routine pointer}§1922 fp = f; §\C{// reference initialization}§1923 fp = &f; §\C{// pointer initialization}§1924 fp = *f; §\C{// reference initialization}§1925 fp(3); §\C{// reference invocation}§1926 (*fp)(3); §\C{// pointer invocation}§1908 void (* fp)( int ); $\C{// routine pointer}$ 1909 fp = f; $\C{// reference initialization}$ 1910 fp = &f; $\C{// pointer initialization}$ 1911 fp = *f; $\C{// reference initialization}$ 1912 fp(3); $\C{// reference invocation}$ 1913 (*fp)(3); $\C{// pointer invocation}$ 1927 1914 \end{cfa} 1928 1915 While C's treatment of routine objects has similarity to inferring a reference type in initialization contexts, the examples are assignment not initialization, and all possible forms of assignment are possible (©f©, ©&f©, ©*f©) without regard for type. 1929 1916 Instead, a routine object should be referenced by a ©const© reference: 1930 1917 \begin{cfa} 1931 ®const® void (®&® fr)( int ) = f; §\C{// routine reference}§ 1932 fr = ... §\C{// error, cannot change code}§1933 &fr = ...; §\C{// changing routine reference}§1934 fr( 3 ); §\C{// reference call to f}§1935 (*fr)(3); §\C{// error, incorrect type}§1918 @const@ void (@&@ fr)( int ) = f; $\C{// routine reference}$ 1919 fr = ... $\C{// error, cannot change code}$ 1920 &fr = ...; $\C{// changing routine reference}$ 1921 fr( 3 ); $\C{// reference call to f}$ 1922 (*fr)(3); $\C{// error, incorrect type}$ 1936 1923 \end{cfa} 1937 1924 because the value of the routine object is a routine literal, \ie the routine code is normally immutable during execution.\footnote{ … … 1946 1933 \begin{itemize} 1947 1934 \item 1948 if ©R© is an \Index{rvalue} of type ©T &©$_1\cdots$ ©&©$_r$, where $r \ge 1$ references (©&© symbols), than ©&R© has type ©T ®*®&©$_{\color{red}2}\cdots$ ©&©$_{\color{red}r}$, \ie ©T© pointer with $r-1$ references (©&© symbols).1949 1950 \item 1951 if ©L© is an \Index{lvalue} of type ©T &©$_1\cdots$ ©&©$_l$, where $l \ge 0$ references (©&© symbols), than ©&L© has type ©T ®*®&©$_{\color{red}1}\cdots$ ©&©$_{\color{red}l}$, \ie ©T© pointer with $l$ references (©&© symbols).1935 if ©R© is an \Index{rvalue} of type ©T &©$_1\cdots$ ©&©$_r$, where $r \ge 1$ references (©&© symbols), than ©&R© has type ©T ©\R{©*©}©&©\R{$_2$}$\cdots$ ©&©\R{$_r$}, \ie ©T© pointer with $r-1$ references (©&© symbols). 1936 1937 \item 1938 if ©L© is an \Index{lvalue} of type ©T &©$_1\cdots$ ©&©$_l$, where $l \ge 0$ references (©&© symbols), than ©&L© has type ©T ©\R{©*©}©&©\R{$_1$}$\cdots$ ©&©\R{$_l$}, \ie ©T© pointer with $l$ references (©&© symbols). 1952 1939 \end{itemize} 1953 1940 The following example shows the first rule applied to different \Index{rvalue} contexts: … … 1955 1942 int x, * px, ** ppx, *** pppx, **** ppppx; 1956 1943 int & rx = x, && rrx = rx, &&& rrrx = rrx ; 1957 x = rrrx; §\C[2.0in]{// rrrx is an lvalue with type int \&\&\& (equivalent to x)}§1958 px = &rrrx; §\C{// starting from rrrx, \&rrrx is an rvalue with type int *\&\&\& (\&x)}§1959 ppx = &&rrrx; §\C{// starting from \&rrrx, \&\&rrrx is an rvalue with type int **\&\& (\&rx)}§1960 pppx = &&&rrrx; §\C{// starting from \&\&rrrx, \&\&\&rrrx is an rvalue with type int ***\& (\&rrx)}§1961 ppppx = &&&&rrrx; §\C{// starting from \&\&\&rrrx, \&\&\&\&rrrx is an rvalue with type int **** (\&rrrx)}§1944 x = rrrx; $\C[2.0in]{// rrrx is an lvalue with type int \&\&\& (equivalent to x)}$ 1945 px = &rrrx; $\C{// starting from rrrx, \&rrrx is an rvalue with type int *\&\&\& (\&x)}$ 1946 ppx = &&rrrx; $\C{// starting from \&rrrx, \&\&rrrx is an rvalue with type int **\&\& (\&rx)}$ 1947 pppx = &&&rrrx; $\C{// starting from \&\&rrrx, \&\&\&rrrx is an rvalue with type int ***\& (\&rrx)}$ 1948 ppppx = &&&&rrrx; $\C{// starting from \&\&\&rrrx, \&\&\&\&rrrx is an rvalue with type int **** (\&rrrx)}$ 1962 1949 \end{cfa} 1963 1950 The following example shows the second rule applied to different \Index{lvalue} contexts: … … 1965 1952 int x, * px, ** ppx, *** pppx; 1966 1953 int & rx = x, && rrx = rx, &&& rrrx = rrx ; 1967 rrrx = 2; §\C{// rrrx is an lvalue with type int \&\&\& (equivalent to x)}§1968 &rrrx = px; §\C{// starting from rrrx, \&rrrx is an rvalue with type int *\&\&\& (rx)}§1969 &&rrrx = ppx; §\C{// starting from \&rrrx, \&\&rrrx is an rvalue with type int **\&\& (rrx)}§1970 &&&rrrx = pppx; §\C{// starting from \&\&rrrx, \&\&\&rrrx is an rvalue with type int ***\& (rrrx)}\CRT§1954 rrrx = 2; $\C{// rrrx is an lvalue with type int \&\&\& (equivalent to x)}$ 1955 &rrrx = px; $\C{// starting from rrrx, \&rrrx is an rvalue with type int *\&\&\& (rx)}$ 1956 &&rrrx = ppx; $\C{// starting from \&rrrx, \&\&rrrx is an rvalue with type int **\&\& (rrx)}$ 1957 &&&rrrx = pppx; $\C{// starting from \&\&rrrx, \&\&\&rrrx is an rvalue with type int ***\& (rrrx)}\CRT$ 1971 1958 \end{cfa} 1972 1959 … … 1981 1968 \begin{cfa} 1982 1969 int x; 1983 x + 1; §\C[2.0in]{// lvalue variable (int) converts to rvalue for expression}§1970 x + 1; $\C[2.0in]{// lvalue variable (int) converts to rvalue for expression}$ 1984 1971 \end{cfa} 1985 1972 An rvalue has no type qualifiers (©cv©), so the lvalue qualifiers are dropped. … … 1991 1978 \begin{cfa} 1992 1979 int x, &r = x, f( int p ); 1993 x = ®r® + f( ®r® ); §\C{// lvalue reference converts to rvalue}§1980 x = @r@ + f( @r@ ); $\C{// lvalue reference converts to rvalue}$ 1994 1981 \end{cfa} 1995 1982 An rvalue has no type qualifiers (©cv©), so the reference qualifiers are dropped. … … 1998 1985 lvalue to reference conversion: \lstinline[deletekeywords=lvalue]@lvalue-type cv1 T@ converts to ©cv2 T &©, which allows implicitly converting variables to references. 1999 1986 \begin{cfa} 2000 int x, &r = ®x®, f( int & p ); §\C{// lvalue variable (int) convert to reference (int \&)}§2001 f( ®x® ); §\C{// lvalue variable (int) convert to reference (int \&)}§1987 int x, &r = @x@, f( int & p ); $\C{// lvalue variable (int) convert to reference (int \&)}$ 1988 f( @x@ ); $\C{// lvalue variable (int) convert to reference (int \&)}$ 2002 1989 \end{cfa} 2003 1990 Conversion can restrict a type, where ©cv1© $\le$ ©cv2©, \eg passing an ©int© to a ©const volatile int &©, which has low cost. … … 2009 1996 \begin{cfa} 2010 1997 int x, & f( int & p ); 2011 f( ®x + 3® ); §\C[1.5in]{// rvalue parameter (int) implicitly converts to lvalue temporary reference (int \&)}§2012 ®&f®(...) = &x; §\C{// rvalue result (int \&) implicitly converts to lvalue temporary reference (int \&)}\CRT§ 1998 f( @x + 3@ ); $\C[1.5in]{// rvalue parameter (int) implicitly converts to lvalue temporary reference (int \&)}$ 1999 @&f@(...) = &x; $\C{// rvalue result (int \&) implicitly converts to lvalue temporary reference (int \&)}\CRT$ 2013 2000 \end{cfa} 2014 2001 In both case, modifications to the temporary are inaccessible (\Index{warning}). … … 2182 2169 The point of the new syntax is to allow returning multiple values from a routine~\cite{Galletly96,CLU}, \eg: 2183 2170 \begin{cfa} 2184 ®[ int o1, int o2, char o3 ]®f( int i1, char i2, char i3 ) {2185 §\emph{routine body}§2171 @[ int o1, int o2, char o3 ]@ f( int i1, char i2, char i3 ) { 2172 $\emph{routine body}$ 2186 2173 } 2187 2174 \end{cfa} … … 2194 2181 Declaration qualifiers can only appear at the start of a routine definition, \eg: 2195 2182 \begin{cfa} 2196 ®extern® [ int x ] g( int y ) {§\,§}2183 @extern@ [ int x ] g( int y ) {$\,$} 2197 2184 \end{cfa} 2198 2185 Lastly, if there are no output parameters or input parameters, the brackets and/or parentheses must still be specified; 2199 2186 in both cases the type is assumed to be void as opposed to old style C defaults of int return type and unknown parameter types, respectively, as in: 2200 2187 \begin{cfa} 2201 [ §\,§] g(); §\C{// no input or output parameters}§2202 [ void ] g( void ); §\C{// no input or output parameters}§2188 [$\,$] g(); $\C{// no input or output parameters}$ 2189 [ void ] g( void ); $\C{// no input or output parameters}$ 2203 2190 \end{cfa} 2204 2191 … … 2218 2205 \begin{cfa} 2219 2206 typedef int foo; 2220 int f( int (* foo) ); §\C{// foo is redefined as a parameter name}§2207 int f( int (* foo) ); $\C{// foo is redefined as a parameter name}$ 2221 2208 \end{cfa} 2222 2209 The string ``©int (* foo)©'' declares a C-style named-parameter of type pointer to an integer (the parenthesis are superfluous), while the same string declares a \CFA style unnamed parameter of type routine returning integer with unnamed parameter of type pointer to foo. … … 2226 2213 C-style declarations can be used to declare parameters for \CFA style routine definitions, \eg: 2227 2214 \begin{cfa} 2228 [ int ] f( * int, int * ); §\C{// returns an integer, accepts 2 pointers to integers}§2229 [ * int, int * ] f( int ); §\C{// returns 2 pointers to integers, accepts an integer}§2215 [ int ] f( * int, int * ); $\C{// returns an integer, accepts 2 pointers to integers}$ 2216 [ * int, int * ] f( int ); $\C{// returns 2 pointers to integers, accepts an integer}$ 2230 2217 \end{cfa} 2231 2218 The reason for allowing both declaration styles in the new context is for backwards compatibility with existing preprocessor macros that generate C-style declaration-syntax, as in: 2232 2219 \begin{cfa} 2233 2220 #define ptoa( n, d ) int (*n)[ d ] 2234 int f( ptoa( p, 5 ) ) ... §\C{// expands to int f( int (*p)[ 5 ] )}§2235 [ int ] f( ptoa( p, 5 ) ) ... §\C{// expands to [ int ] f( int (*p)[ 5 ] )}§2221 int f( ptoa( p, 5 ) ) ... $\C{// expands to int f( int (*p)[ 5 ] )}$ 2222 [ int ] f( ptoa( p, 5 ) ) ... $\C{// expands to [ int ] f( int (*p)[ 5 ] )}$ 2236 2223 \end{cfa} 2237 2224 Again, programmers are highly encouraged to use one declaration form or the other, rather than mixing the forms. … … 2252 2239 \begin{minipage}{\linewidth} 2253 2240 \begin{cfa} 2254 ®[ int x, int y ]®f() {2241 @[ int x, int y ]@ f() { 2255 2242 int z; 2256 2243 ... x = 0; ... y = z; ... 2257 ®return;® §\C{// implicitly return x, y}§2244 @return;@ $\C{// implicitly return x, y}$ 2258 2245 } 2259 2246 \end{cfa} … … 2265 2252 [ int x, int y ] f() { 2266 2253 ... 2267 } §\C{// implicitly return x, y}§2254 } $\C{// implicitly return x, y}$ 2268 2255 \end{cfa} 2269 2256 In this case, the current values of ©x© and ©y© are returned to the calling routine just as if a ©return© had been encountered. … … 2274 2261 [ int x, int y ] f( int, x, int y ) { 2275 2262 ... 2276 } §\C{// implicitly return x, y}§2263 } $\C{// implicitly return x, y}$ 2277 2264 \end{cfa} 2278 2265 This notation allows the compiler to eliminate temporary variables in nested routine calls. 2279 2266 \begin{cfa} 2280 [ int x, int y ] f( int, x, int y ); §\C{// prototype declaration}§2267 [ int x, int y ] f( int, x, int y ); $\C{// prototype declaration}$ 2281 2268 int a, b; 2282 2269 [a, b] = f( f( f( a, b ) ) ); … … 2292 2279 as well, parameter names are optional, \eg: 2293 2280 \begin{cfa} 2294 [ int x ] f (); §\C{// returning int with no parameters}§2295 [ * int ] g (int y); §\C{// returning pointer to int with int parameter}§2296 [ ] h ( int, char ); §\C{// returning no result with int and char parameters}§2297 [ * int, int ] j ( int ); §\C{// returning pointer to int and int, with int parameter}§2281 [ int x ] f (); $\C{// returning int with no parameters}$ 2282 [ * int ] g (int y); $\C{// returning pointer to int with int parameter}$ 2283 [ ] h ( int, char ); $\C{// returning no result with int and char parameters}$ 2284 [ * int, int ] j ( int ); $\C{// returning pointer to int and int, with int parameter}$ 2298 2285 \end{cfa} 2299 2286 This syntax allows a prototype declaration to be created by cutting and pasting source text from the routine definition header (or vice versa). 2300 Like C, it is possible to declare multiple routine-prototypes in a single declaration, where the return type is distributed across \emph{all} routine names in the declaration list (see~\VRef{s:AlternativeDeclarations}), \eg:2287 Like C, it is possible to declare multiple routine-prototypes in a single declaration, where the return type is distributed across \emph{all} routine names in the declaration list \see{\VRef{s:AlternativeDeclarations}}, \eg: 2301 2288 \begin{cfa} 2302 2289 C : const double bar1(), bar2( int ), bar3( double ); 2303 §\CFA§: [const double] foo(), foo( int ), foo( double ) { return 3.0; }2290 $\CFA$: [const double] foo(), foo( int ), foo( double ) { return 3.0; } 2304 2291 \end{cfa} 2305 2292 \CFA allows the last routine in the list to define its body. … … 2316 2303 The syntax for pointers to \CFA routines specifies the pointer name on the right, \eg: 2317 2304 \begin{cfa} 2318 * [ int x ] () fp; §\C{// pointer to routine returning int with no parameters}§2319 * [ * int ] (int y) gp; §\C{// pointer to routine returning pointer to int with int parameter}§2320 * [ ] (int,char) hp; §\C{// pointer to routine returning no result with int and char parameters}§2321 * [ * int,int ] ( int ) jp; §\C{// pointer to routine returning pointer to int and int, with int parameter}§2305 * [ int x ] () fp; $\C[2.25in]{// pointer to routine returning int with no parameters}$ 2306 * [ * int ] (int y) gp; $\C{// pointer to routine returning pointer to int with int parameter}$ 2307 * [ ] (int,char) hp; $\C{// pointer to routine returning no result with int and char parameters}$ 2308 * [ * int,int ] ( int ) jp; $\C{// pointer to routine returning pointer to int and int, with int parameter}\CRT$ 2322 2309 \end{cfa} 2323 2310 While parameter names are optional, \emph{a routine name cannot be specified}; 2324 2311 for example, the following is incorrect: 2325 2312 \begin{cfa} 2326 * [ int x ] f () fp; §\C{// routine name "f" is not allowed}§2313 * [ int x ] f () fp; $\C{// routine name "f" is not allowed}$ 2327 2314 \end{cfa} 2328 2315 … … 2347 2334 whereas a named (keyword) call may be: 2348 2335 \begin{cfa} 2349 p( z : 3, x : 4, y : 7 ); §\C{// rewrite $\Rightarrow$ p( 4, 7, 3 )}§2336 p( z : 3, x : 4, y : 7 ); $\C{// rewrite \(\Rightarrow\) p( 4, 7, 3 )}$ 2350 2337 \end{cfa} 2351 2338 Here the order of the arguments is unimportant, and the names of the parameters are used to associate argument values with the corresponding parameters. … … 2364 2351 For example, the following routine prototypes and definition are all valid. 2365 2352 \begin{cfa} 2366 void p( int, int, int ); §\C{// equivalent prototypes}§2353 void p( int, int, int ); $\C{// equivalent prototypes}$ 2367 2354 void p( int x, int y, int z ); 2368 2355 void p( int y, int x, int z ); 2369 2356 void p( int z, int y, int x ); 2370 void p( int q, int r, int s ) {} §\C{// match with this definition}§2357 void p( int q, int r, int s ) {} $\C{// match with this definition}$ 2371 2358 \end{cfa} 2372 2359 Forcing matching parameter names in routine prototypes with corresponding routine definitions is possible, but goes against a strong tradition in C programming. … … 2380 2367 int f( int x, double y ); 2381 2368 2382 f( j : 3, i : 4 ); §\C{// 1st f}§2383 f( x : 7, y : 8.1 ); §\C{// 2nd f}§2384 f( 4, 5 ); §\C{// ambiguous call}§2369 f( j : 3, i : 4 ); $\C{// 1st f}$ 2370 f( x : 7, y : 8.1 ); $\C{// 2nd f}$ 2371 f( 4, 5 ); $\C{// ambiguous call}$ 2385 2372 \end{cfa} 2386 2373 However, named arguments compound routine resolution in conjunction with conversions: 2387 2374 \begin{cfa} 2388 f( i : 3, 5.7 ); §\C{// ambiguous call ?}§2375 f( i : 3, 5.7 ); $\C{// ambiguous call ?}$ 2389 2376 \end{cfa} 2390 2377 Depending on the cost associated with named arguments, this call could be resolvable or ambiguous. … … 2400 2387 the allowable positional calls are: 2401 2388 \begin{cfa} 2402 p(); §\C{// rewrite $\Rightarrow$ p( 1, 2, 3 )}§2403 p( 4 ); §\C{// rewrite $\Rightarrow$ p( 4, 2, 3 )}§2404 p( 4, 4 ); §\C{// rewrite $\Rightarrow$ p( 4, 4, 3 )}§2405 p( 4, 4, 4 ); §\C{// rewrite $\Rightarrow$ p( 4, 4, 4 )}§2389 p(); $\C{// rewrite \(\Rightarrow\) p( 1, 2, 3 )}$ 2390 p( 4 ); $\C{// rewrite \(\Rightarrow\) p( 4, 2, 3 )}$ 2391 p( 4, 4 ); $\C{// rewrite \(\Rightarrow\) p( 4, 4, 3 )}$ 2392 p( 4, 4, 4 ); $\C{// rewrite \(\Rightarrow\) p( 4, 4, 4 )}$ 2406 2393 // empty arguments 2407 p( , 4, 4 ); §\C{// rewrite $\Rightarrow$ p( 1, 4, 4 )}§2408 p( 4, , 4 ); §\C{// rewrite $\Rightarrow$ p( 4, 2, 4 )}§2409 p( 4, 4, ); §\C{// rewrite $\Rightarrow$ p( 4, 4, 3 )}§2410 p( 4, , ); §\C{// rewrite $\Rightarrow$ p( 4, 2, 3 )}§2411 p( , 4, ); §\C{// rewrite $\Rightarrow$ p( 1, 4, 3 )}§2412 p( , , 4 ); §\C{// rewrite $\Rightarrow$ p( 1, 2, 4 )}§2413 p( , , ); §\C{// rewrite $\Rightarrow$ p( 1, 2, 3 )}§2394 p( , 4, 4 ); $\C{// rewrite \(\Rightarrow\) p( 1, 4, 4 )}$ 2395 p( 4, , 4 ); $\C{// rewrite \(\Rightarrow\) p( 4, 2, 4 )}$ 2396 p( 4, 4, ); $\C{// rewrite \(\Rightarrow\) p( 4, 4, 3 )}$ 2397 p( 4, , ); $\C{// rewrite \(\Rightarrow\) p( 4, 2, 3 )}$ 2398 p( , 4, ); $\C{// rewrite \(\Rightarrow\) p( 1, 4, 3 )}$ 2399 p( , , 4 ); $\C{// rewrite \(\Rightarrow\) p( 1, 2, 4 )}$ 2400 p( , , ); $\C{// rewrite \(\Rightarrow\) p( 1, 2, 3 )}$ 2414 2401 \end{cfa} 2415 2402 Here the missing arguments are inserted from the default values in the parameter list. … … 2435 2422 Default values may only appear in a prototype versus definition context: 2436 2423 \begin{cfa} 2437 void p( int x, int y = 2, int z = 3 ); §\C{// prototype: allowed}§2438 void p( int, int = 2, int = 3 ); §\C{// prototype: allowed}§2439 void p( int x, int y = 2, int z = 3 ) {} §\C{// definition: not allowed}§2424 void p( int x, int y = 2, int z = 3 ); $\C{// prototype: allowed}$ 2425 void p( int, int = 2, int = 3 ); $\C{// prototype: allowed}$ 2426 void p( int x, int y = 2, int z = 3 ) {} $\C{// definition: not allowed}$ 2440 2427 \end{cfa} 2441 2428 The reason for this restriction is to allow separate compilation. … … 2452 2439 \begin{cfa} 2453 2440 p( int x, int y, int z, ... ); 2454 p( 1, 4, 5, 6, z : 3, y : 2 ); §\C{// assume p( /* positional */, ... , /* named */ );}§2455 p( 1, z : 3, y : 2, 4, 5, 6 ); §\C{// assume p( /* positional */, /* named */, ... );}§2441 p( 1, 4, 5, 6, z : 3, y : 2 ); $\C{// assume p( /* positional */, ... , /* named */ );}$ 2442 p( 1, z : 3, y : 2, 4, 5, 6 ); $\C{// assume p( /* positional */, /* named */, ... );}$ 2456 2443 \end{cfa} 2457 2444 In the first call, it is necessary for the programmer to conceptually rewrite the call, changing named arguments into positional, before knowing where the ellipse arguments begin. … … 2462 2449 \begin{cfa} 2463 2450 void p( int x, int y = 2, int z = 3... ); 2464 p( 1, 4, 5, 6, z : 3 ); §\C{// assume p( /* positional */, ... , /* named */ );}§2465 p( 1, z : 3, 4, 5, 6 ); §\C{// assume p( /* positional */, /* named */, ... );}§2451 p( 1, 4, 5, 6, z : 3 ); $\C{// assume p( /* positional */, ... , /* named */ );}$ 2452 p( 1, z : 3, 4, 5, 6 ); $\C{// assume p( /* positional */, /* named */, ... );}$ 2466 2453 \end{cfa} 2467 2454 The first call is an error because arguments 4 and 5 are actually positional not ellipse arguments; … … 2469 2456 In the second call, the default value for y is implicitly inserted after argument 1 and the named arguments separate the positional and ellipse arguments, making it trivial to read the call. 2470 2457 For these reasons, \CFA requires named arguments before ellipse arguments. 2471 Finally, while ellipse arguments are needed for a small set of existing C routines, like printf, the extended \CFA type system largely eliminates the need for ellipse arguments (see Section 24), making much of this discussion moot.2472 2473 Default arguments and overloading (see Section 24)are complementary.2458 Finally, while ellipse arguments are needed for a small set of existing C routines, like ©printf©, the extended \CFA type system largely eliminates the need for ellipse arguments \see{\VRef{s:Overloading}}, making much of this discussion moot. 2459 2460 Default arguments and overloading \see{\VRef{s:Overloading}} are complementary. 2474 2461 While in theory default arguments can be simulated with overloading, as in: 2475 2462 \begin{cquote} … … 2493 2480 Furthermore, overloading cannot handle accessing default arguments in the middle of a positional list, via a missing argument, such as: 2494 2481 \begin{cfa} 2495 p( 1, /* default */, 5 ); §\C{// rewrite $\Rightarrow$ p( 1, 2, 5 )}§2482 p( 1, /* default */, 5 ); $\C{// rewrite \(\Rightarrow\) p( 1, 2, 5 )}$ 2496 2483 \end{cfa} 2497 2484 … … 2506 2493 \begin{cfa} 2507 2494 struct { 2508 int f1; §\C{// named field}§2509 int f2 : 4; §\C{// named field with bit field size}§2510 int : 3; §\C{// unnamed field for basic type with bit field size}§2511 int ; §\C{// disallowed, unnamed field}§2512 int *; §\C{// disallowed, unnamed field}§2513 int (*)( int ); §\C{// disallowed, unnamed field}§2495 int f1; $\C{// named field}$ 2496 int f2 : 4; $\C{// named field with bit field size}$ 2497 int : 3; $\C{// unnamed field for basic type with bit field size}$ 2498 int ; $\C{// disallowed, unnamed field}$ 2499 int *; $\C{// disallowed, unnamed field}$ 2500 int (*)( int ); $\C{// disallowed, unnamed field}$ 2514 2501 }; 2515 2502 \end{cfa} … … 2519 2506 \begin{cfa} 2520 2507 struct { 2521 int , , ; §\C{// 3 unnamed fields}§2508 int , , ; $\C{// 3 unnamed fields}$ 2522 2509 } 2523 2510 \end{cfa} … … 2531 2518 \subsection{Type Nesting} 2532 2519 2533 \CFA allows \Index{type nesting}, and type qualification of the nested types (see \VRef[Figure]{f:TypeNestingQualification}), where as C hoists\index{type hoisting} (refactors) nested types into the enclosing scope and has no type qualification.2520 \CFA allows \Index{type nesting}, and type qualification of the nested types \see{\VRef[Figure]{f:TypeNestingQualification}}, where as C hoists\index{type hoisting} (refactors) nested types into the enclosing scope and has no type qualification. 2534 2521 \begin{figure} 2535 2522 \centering … … 2587 2574 2588 2575 int fred() { 2589 s.t.c = ®S.®R; // type qualification2590 struct ®S.®T t = { ®S.®R, 1, 2 };2591 enum ®S.®C c;2592 union ®S.T.®U u;2576 s.t.c = @S.@R; // type qualification 2577 struct @S.@T t = { @S.@R, 1, 2 }; 2578 enum @S.@C c; 2579 union @S.T.@U u; 2593 2580 } 2594 2581 \end{cfa} … … 2613 2600 const unsigned int size = 5; 2614 2601 int ia[size]; 2615 ... §\C{// assign values to array ia}§2616 qsort( ia, size ); §\C{// sort ascending order using builtin ?<?}§2602 ... $\C{// assign values to array ia}$ 2603 qsort( ia, size ); $\C{// sort ascending order using builtin ?<?}$ 2617 2604 { 2618 ®int ?<?( int x, int y ) { return x > y; }® §\C{// nested routine}§2619 qsort( ia, size ); §\C{// sort descending order by local redefinition}§2605 @int ?<?( int x, int y ) { return x > y; }@ $\C{// nested routine}$ 2606 qsort( ia, size ); $\C{// sort descending order by local redefinition}$ 2620 2607 } 2621 2608 \end{cfa} … … 2625 2612 The following program in undefined in \CFA (and Indexc{gcc}) 2626 2613 \begin{cfa} 2627 [* [int]( int )] foo() { §\C{// int (* foo())( int )}§2628 int ®i®= 7;2614 [* [int]( int )] foo() { $\C{// int (* foo())( int )}$ 2615 int @i@ = 7; 2629 2616 int bar( int p ) { 2630 ®i® += 1; §\C{// dependent on local variable}§2631 sout | ®i®;2617 @i@ += 1; $\C{// dependent on local variable}$ 2618 sout | @i@; 2632 2619 } 2633 return bar; §\C{// undefined because of local dependence}§2620 return bar; $\C{// undefined because of local dependence}$ 2634 2621 } 2635 2622 int main() { 2636 * [int]( int ) fp = foo(); §\C{// int (* fp)( int )}§2623 * [int]( int ) fp = foo(); $\C{// int (* fp)( int )}$ 2637 2624 sout | fp( 3 ); 2638 2625 } … … 2647 2634 In C and \CFA, lists of elements appear in several contexts, such as the parameter list of a routine call. 2648 2635 \begin{cfa} 2649 f( ®2, x, 3 + i® ); §\C{// element list}§2636 f( @2, x, 3 + i@ ); $\C{// element list}$ 2650 2637 \end{cfa} 2651 2638 A list of elements is called a \newterm{tuple}, and is different from a \Index{comma expression}. … … 2656 2643 2657 2644 In C and most programming languages, functions return at most one value; 2658 however, many operations have multiple outcomes, some exceptional (see~\VRef{s:ExceptionHandling}).2645 however, many operations have multiple outcomes, some exceptional \see{\VRef{s:ExceptionHandling}}. 2659 2646 To emulate functions with multiple return values, \emph{\Index{aggregation}} and/or \emph{\Index{aliasing}} is used. 2660 2647 … … 2662 2649 For example, consider C's \Indexc{div} function, which returns the quotient and remainder for a division of an integer value. 2663 2650 \begin{cfa} 2664 typedef struct { int quot, rem; } div_t; §\C[7cm]{// from include stdlib.h}§2651 typedef struct { int quot, rem; } div_t; $\C[7cm]{// from include stdlib.h}$ 2665 2652 div_t div( int num, int den ); 2666 div_t qr = div( 13, 5 ); §\C{// return quotient/remainder aggregate}§2667 printf( "%d %d\n", qr.quot, qr.rem ); §\C{// print quotient/remainder}§2653 div_t qr = div( 13, 5 ); $\C{// return quotient/remainder aggregate}$ 2654 printf( "%d %d\n", qr.quot, qr.rem ); $\C{// print quotient/remainder}$ 2668 2655 \end{cfa} 2669 2656 This approach requires a name for the return type and fields, where \Index{naming} is a common programming-language issue. … … 2675 2662 For example, consider C's \Indexc{modf} function, which returns the integral and fractional part of a floating value. 2676 2663 \begin{cfa} 2677 double modf( double x, double * i ); §\C{// from include math.h}§2678 double intp, frac = modf( 13.5, &intp ); §\C{// return integral and fractional components}§2679 printf( "%g %g\n", intp, frac ); §\C{// print integral/fractional components}§2664 double modf( double x, double * i ); $\C{// from include math.h}$ 2665 double intp, frac = modf( 13.5, &intp ); $\C{// return integral and fractional components}$ 2666 printf( "%g %g\n", intp, frac ); $\C{// print integral/fractional components}$ 2680 2667 \end{cfa} 2681 2668 This approach requires allocating storage for the return values, which complicates the call site with a sequence of variable declarations leading to the call. … … 2704 2691 When a function call is passed as an argument to another call, the best match of actual arguments to formal parameters is evaluated given all possible expression interpretations in the current scope. 2705 2692 \begin{cfa} 2706 void g( int, int ); §\C{// 1}§2707 void g( double, double ); §\C{// 2}§2708 g( div( 13, 5 ) ); §\C{// select 1}§2709 g( modf( 13.5 ) ); §\C{// select 2}§2693 void g( int, int ); $\C{// 1}$ 2694 void g( double, double ); $\C{// 2}$ 2695 g( div( 13, 5 ) ); $\C{// select 1}$ 2696 g( modf( 13.5 ) ); $\C{// select 2}$ 2710 2697 \end{cfa} 2711 2698 In this case, there are two overloaded ©g© routines. … … 2716 2703 The previous examples can be rewritten passing the multiple returned-values directly to the ©printf© function call. 2717 2704 \begin{cfa} 2718 [ int, int ] div( int x, int y ); §\C{// from include stdlib}§2719 printf( "%d %d\n", div( 13, 5 ) ); §\C{// print quotient/remainder}§2720 2721 [ double, double ] modf( double x ); §\C{// from include math}§2722 printf( "%g %g\n", modf( 13.5 ) ); §\C{// print integral/fractional components}§2705 [ int, int ] div( int x, int y ); $\C{// from include stdlib}$ 2706 printf( "%d %d\n", div( 13, 5 ) ); $\C{// print quotient/remainder}$ 2707 2708 [ double, double ] modf( double x ); $\C{// from include math}$ 2709 printf( "%g %g\n", modf( 13.5 ) ); $\C{// print integral/fractional components}$ 2723 2710 \end{cfa} 2724 2711 This approach provides the benefits of compile-time checking for appropriate return statements as in aggregation, but without the required verbosity of declaring a new named type. … … 2730 2717 \begin{cfa} 2731 2718 int quot, rem; 2732 [ quot, rem ] = div( 13, 5 ); §\C{// assign multiple variables}§2733 printf( "%d %d\n", quot, rem ); §\C{// print quotient/remainder}\CRT§2719 [ quot, rem ] = div( 13, 5 ); $\C{// assign multiple variables}$ 2720 printf( "%d %d\n", quot, rem ); $\C{// print quotient/remainder}\CRT$ 2734 2721 \end{cfa} 2735 2722 Here, the multiple return-values are matched in much the same way as passing multiple return-values to multiple parameters in a call. … … 2760 2747 In \CFA, it is possible to overcome this restriction by declaring a \newterm{tuple variable}. 2761 2748 \begin{cfa} 2762 [int, int] ®qr® = div( 13, 5 ); §\C{// initialize tuple variable}§2763 printf( "%d %d\n", ®qr® ); §\C{// print quotient/remainder}§2749 [int, int] @qr@ = div( 13, 5 ); $\C{// initialize tuple variable}$ 2750 printf( "%d %d\n", @qr@ ); $\C{// print quotient/remainder}$ 2764 2751 \end{cfa} 2765 2752 It is now possible to match the multiple return-values to a single variable, in much the same way as \Index{aggregation}. … … 2767 2754 One way to access the individual components of a tuple variable is with assignment. 2768 2755 \begin{cfa} 2769 [ quot, rem ] = qr; §\C{// assign multiple variables}§2756 [ quot, rem ] = qr; $\C{// assign multiple variables}$ 2770 2757 \end{cfa} 2771 2758 … … 2790 2777 [int, double] * p; 2791 2778 2792 int y = x.0; §\C{// access int component of x}§2793 y = f().1; §\C{// access int component of f}§2794 p->0 = 5; §\C{// access int component of tuple pointed-to by p}§2795 g( x.1, x.0 ); §\C{// rearrange x to pass to g}§2796 double z = [ x, f() ].0.1; §\C{// access second component of first component of tuple expression}§2779 int y = x.0; $\C{// access int component of x}$ 2780 y = f().1; $\C{// access int component of f}$ 2781 p->0 = 5; $\C{// access int component of tuple pointed-to by p}$ 2782 g( x.1, x.0 ); $\C{// rearrange x to pass to g}$ 2783 double z = [ x, f() ].0.1; $\C{// access second component of first component of tuple expression}$ 2797 2784 \end{cfa} 2798 2785 Tuple-index expressions can occur on any tuple-typed expression, including tuple-returning functions, square-bracketed tuple expressions, and other tuple-index expressions, provided the retrieved component is also a tuple. … … 2801 2788 2802 2789 \subsection{Flattening and Structuring} 2790 \label{s:FlatteningStructuring} 2803 2791 2804 2792 As evident in previous examples, tuples in \CFA do not have a rigid structure. … … 2861 2849 double y; 2862 2850 [int, double] z; 2863 [y, x] = 3.14; §\C{// mass assignment}§2864 [x, y] = z; §\C{// multiple assignment}§2865 z = 10; §\C{// mass assignment}§2866 z = [x, y]; §\C{// multiple assignment}§2851 [y, x] = 3.14; $\C{// mass assignment}$ 2852 [x, y] = z; $\C{// multiple assignment}$ 2853 z = 10; $\C{// mass assignment}$ 2854 z = [x, y]; $\C{// multiple assignment}$ 2867 2855 \end{cfa} 2868 2856 Let $L_i$ for $i$ in $[0, n)$ represent each component of the flattened left side, $R_i$ represent each component of the flattened right side of a multiple assignment, and $R$ represent the right side of a mass assignment. … … 2872 2860 \begin{cfa} 2873 2861 [ int, int ] x, y, z; 2874 [ x, y ] = z; §\C{// multiple assignment, invalid 4 != 2}§2862 [ x, y ] = z; $\C{// multiple assignment, invalid 4 != 2}$ 2875 2863 \end{cfa} 2876 2864 Multiple assignment assigns $R_i$ to $L_i$ for each $i$. … … 2908 2896 double c, d; 2909 2897 [ void ] f( [ int, int ] ); 2910 f( [ c, a ] = [ b, d ] = 1.5 ); §\C{// assignments in parameter list}§2898 f( [ c, a ] = [ b, d ] = 1.5 ); $\C{// assignments in parameter list}$ 2911 2899 \end{cfa} 2912 2900 The tuple expression begins with a mass assignment of ©1.5© into ©[b, d]©, which assigns ©1.5© into ©b©, which is truncated to ©1©, and ©1.5© into ©d©, producing the tuple ©[1, 1.5]© as a result. … … 2921 2909 \begin{cfa} 2922 2910 struct S; 2923 void ?{}(S *); §\C{// (1)}§2924 void ?{}(S *, int); §\C{// (2)}§2925 void ?{}(S * double); §\C{// (3)}§2926 void ?{}(S *, S); §\C{// (4)}§2927 2928 [S, S] x = [3, 6.28]; §\C{// uses (2), (3), specialized constructors}§2929 [S, S] y; §\C{// uses (1), (1), default constructor}§2930 [S, S] z = x.0; §\C{// uses (4), (4), copy constructor}§2911 void ?{}(S *); $\C{// (1)}$ 2912 void ?{}(S *, int); $\C{// (2)}$ 2913 void ?{}(S * double); $\C{// (3)}$ 2914 void ?{}(S *, S); $\C{// (4)}$ 2915 2916 [S, S] x = [3, 6.28]; $\C{// uses (2), (3), specialized constructors}$ 2917 [S, S] y; $\C{// uses (1), (1), default constructor}$ 2918 [S, S] z = x.0; $\C{// uses (4), (4), copy constructor}$ 2931 2919 \end{cfa} 2932 2920 In this example, ©x© is initialized by the multiple constructor calls ©?{}(&x.0, 3)© and ©?{}(&x.1, 6.28)©, while ©y© is initialized by two default constructor calls ©?{}(&y.0)© and ©?{}(&y.1)©. … … 2969 2957 A member-access tuple may be used anywhere a tuple can be used, \eg: 2970 2958 \begin{cfa} 2971 s.[ y, z, x ] = [ 3, 3.2, 'x' ]; §\C{// equivalent to s.x = 'x', s.y = 3, s.z = 3.2}§2972 f( s.[ y, z ] ); §\C{// equivalent to f( s.y, s.z )}§2959 s.[ y, z, x ] = [ 3, 3.2, 'x' ]; $\C{// equivalent to s.x = 'x', s.y = 3, s.z = 3.2}$ 2960 f( s.[ y, z ] ); $\C{// equivalent to f( s.y, s.z )}$ 2973 2961 \end{cfa} 2974 2962 Note, the fields appearing in a record-field tuple may be specified in any order; … … 2980 2968 void f( double, long ); 2981 2969 2982 f( x.[ 0, 3 ] ); §\C{// f( x.0, x.3 )}§2983 x.[ 0, 1 ] = x.[ 1, 0 ]; §\C{// [ x.0, x.1 ] = [ x.1, x.0 ]}§2970 f( x.[ 0, 3 ] ); $\C{// f( x.0, x.3 )}$ 2971 x.[ 0, 1 ] = x.[ 1, 0 ]; $\C{// [ x.0, x.1 ] = [ x.1, x.0 ]}$ 2984 2972 [ long, int, long ] y = x.[ 2, 0, 2 ]; 2985 2973 \end{cfa} … … 2998 2986 \begin{cfa} 2999 2987 [ int, float, double ] f(); 3000 [ double, float ] x = f().[ 2, 1 ]; §\C{// f() called once}§2988 [ double, float ] x = f().[ 2, 1 ]; $\C{// f() called once}$ 3001 2989 \end{cfa} 3002 2990 … … 3011 2999 That is, a cast can be used to select the type of an expression when it is ambiguous, as in the call to an overloaded function. 3012 3000 \begin{cfa} 3013 int f(); §\C{// (1)}§3014 double f(); §\C{// (2)}§3015 3016 f(); §\C{// ambiguous - (1),(2) both equally viable}§3017 (int)f(); §\C{// choose (2)}§3001 int f(); $\C{// (1)}$ 3002 double f(); $\C{// (2)}$ 3003 3004 f(); $\C{// ambiguous - (1),(2) both equally viable}$ 3005 (int)f(); $\C{// choose (2)}$ 3018 3006 \end{cfa} 3019 3007 Since casting is a fundamental operation in \CFA, casts need to be given a meaningful interpretation in the context of tuples. … … 3023 3011 void g(); 3024 3012 3025 (void)f(); §\C{// valid, ignore results}§3026 (int)g(); §\C{// invalid, void cannot be converted to int}§3013 (void)f(); $\C{// valid, ignore results}$ 3014 (int)g(); $\C{// invalid, void cannot be converted to int}$ 3027 3015 3028 3016 struct A { int x; }; 3029 (struct A)f(); §\C{// invalid, int cannot be converted to A}§3017 (struct A)f(); $\C{// invalid, int cannot be converted to A}$ 3030 3018 \end{cfa} 3031 3019 In C, line 4 is a valid cast, which calls ©f© and discards its result. … … 3043 3031 [int, [int, int], int] g(); 3044 3032 3045 ([int, double])f(); §\C{// (1) valid}§3046 ([int, int, int])g(); §\C{// (2) valid}§3047 ([void, [int, int]])g(); §\C{// (3) valid}§3048 ([int, int, int, int])g(); §\C{// (4) invalid}§3049 ([int, [int, int, int]])g(); §\C{// (5) invalid}§3033 ([int, double])f(); $\C{// (1) valid}$ 3034 ([int, int, int])g(); $\C{// (2) valid}$ 3035 ([void, [int, int]])g(); $\C{// (3) valid}$ 3036 ([int, int, int, int])g(); $\C{// (4) invalid}$ 3037 ([int, [int, int, int]])g(); $\C{// (5) invalid}$ 3050 3038 \end{cfa} 3051 3039 … … 3107 3095 void f([int, int], int, int); 3108 3096 3109 f([0, 0], 0, 0); §\C{// no cost}§3110 f(0, 0, 0, 0); §\C{// cost for structuring}§3111 f([0, 0,], [0, 0]); §\C{// cost for flattening}§3112 f([0, 0, 0], 0); §\C{// cost for flattening and structuring}§3097 f([0, 0], 0, 0); $\C{// no cost}$ 3098 f(0, 0, 0, 0); $\C{// cost for structuring}$ 3099 f([0, 0,], [0, 0]); $\C{// cost for flattening}$ 3100 f([0, 0, 0], 0); $\C{// cost for flattening and structuring}$ 3113 3101 \end{cfa} 3114 3102 … … 3146 3134 The general syntax of a lexical list is: 3147 3135 \begin{cfa} 3148 [ §\emph{exprlist}§]3136 [ $\emph{exprlist}$ ] 3149 3137 \end{cfa} 3150 3138 where ©$\emph{exprlist}$© is a list of one or more expressions separated by commas. … … 3158 3146 Tuples are permitted to contain sub-tuples (\ie nesting), such as ©[ [ 14, 21 ], 9 ]©, which is a 2-element tuple whose first element is itself a tuple. 3159 3147 Note, a tuple is not a record (structure); 3160 a record denotes a single value with substructure, whereas a tuple is multiple values with no substructure (see flattening coercion in Section 12.1).3148 a record denotes a single value with substructure, whereas a tuple is multiple values with no substructure \see{flattening coercion in \VRef{s:FlatteningStructuring}}. 3161 3149 In essence, tuples are largely a compile time phenomenon, having little or no runtime presence. 3162 3150 … … 3166 3154 The general syntax of a tuple type is: 3167 3155 \begin{cfa} 3168 [ §\emph{typelist}§]3156 [ $\emph{typelist}$ ] 3169 3157 \end{cfa} 3170 3158 where ©$\emph{typelist}$© is a list of one or more legal \CFA or C type specifications separated by commas, which may include other tuple type specifications. … … 3173 3161 [ unsigned int, char ] 3174 3162 [ double, double, double ] 3175 [ * int, int * ] §\C{// mix of CFA and ANSI}§3163 [ * int, int * ] $\C{// mix of CFA and ANSI}$ 3176 3164 [ * [ 5 ] int, * * char, * [ [ int, int ] ] (int, int) ] 3177 3165 \end{cfa} … … 3180 3168 Examples of declarations using tuple types are: 3181 3169 \begin{cfa} 3182 [ int, int ] x; §\C{// 2 element tuple, each element of type int}§3183 * [ char, char ] y; §\C{// pointer to a 2 element tuple}§3170 [ int, int ] x; $\C{// 2 element tuple, each element of type int}$ 3171 * [ char, char ] y; $\C{// pointer to a 2 element tuple}$ 3184 3172 [ [ int, int ] ] z ([ int, int ]); 3185 3173 \end{cfa} … … 3198 3186 [ int, int ] w1; 3199 3187 [ int, int, int ] w2; 3200 [ void ] f (int, int, int); §\C{// three input parameters of type int}§3201 [ void ] g ([ int, int, int ]); §\C{3 element tuple as input}§3188 [ void ] f (int, int, int); $\C{// three input parameters of type int}$ 3189 [ void ] g ([ int, int, int ]); $\C{3 element tuple as input}$ 3202 3190 f( [ 1, 2, 3 ] ); 3203 3191 f( w1, 3 ); … … 3279 3267 [ int, int, int, int ] w = [ 1, 2, 3, 4 ]; 3280 3268 int x = 5; 3281 [ x, w ] = [ w, x ]; §\C{// all four tuple coercions}§3269 [ x, w ] = [ w, x ]; $\C{// all four tuple coercions}$ 3282 3270 \end{cfa} 3283 3271 Starting on the right-hand tuple in the last assignment statement, w is opened, producing a tuple of four values; … … 3285 3273 This tuple is then flattened, yielding ©[ 1, 2, 3, 4, 5 ]©, which is structured into ©[ 1, [ 2, 3, 4, 5 ] ]© to match the tuple type of the left-hand side. 3286 3274 The tuple ©[ 2, 3, 4, 5 ]© is then closed to create a tuple value. 3287 Finally, ©x© is assigned ©1© and ©w© is assigned the tuple value using multiple assignment (see Section 14).3275 Finally, ©x© is assigned ©1© and ©w© is assigned the tuple value using \Index{multiple assignment} \see{\VRef{s:TupleAssignment}}. 3288 3276 \begin{rationale} 3289 3277 A possible additional language extension is to use the structuring coercion for tuples to initialize a complex record with a tuple. … … 3296 3284 Mass assignment has the following form: 3297 3285 \begin{cfa} 3298 [ §\emph{lvalue}§, ... , §\emph{lvalue}§ ] = §\emph{expr}§;3286 [ $\emph{lvalue}$, ... , $\emph{lvalue}$ ] = $\emph{expr}$; 3299 3287 \end{cfa} 3300 3288 \index{lvalue} … … 3336 3324 Multiple assignment has the following form: 3337 3325 \begin{cfa} 3338 [ §\emph{lvalue}§, ... , §\emph{lvalue}§ ] = [ §\emph{expr}§, ... , §\emph{expr}§];3326 [ $\emph{lvalue}$, ... , $\emph{lvalue}$ ] = [ $\emph{expr}$, ... , $\emph{expr}$ ]; 3339 3327 \end{cfa} 3340 3328 \index{lvalue} … … 3367 3355 both these examples produce indeterminate results: 3368 3356 \begin{cfa} 3369 f( x++, x++ ); §\C{// C routine call with side effects in arguments}§3370 [ v1, v2 ] = [ x++, x++ ]; §\C{// side effects in righthand side of multiple assignment}§3357 f( x++, x++ ); $\C{// C routine call with side effects in arguments}$ 3358 [ v1, v2 ] = [ x++, x++ ]; $\C{// side effects in right-hand side of multiple assignment}$ 3371 3359 \end{cfa} 3372 3360 … … 3377 3365 Cascade assignment has the following form: 3378 3366 \begin{cfa} 3379 §\emph{tuple}§ = §\emph{tuple}§ = ... = §\emph{tuple}§;3367 $\emph{tuple}$ = $\emph{tuple}$ = ... = $\emph{tuple}$; 3380 3368 \end{cfa} 3381 3369 and it has the same parallel semantics as for mass and multiple assignment. … … 3424 3412 \begin{cfa} 3425 3413 int x = 1, y = 2, z = 3; 3426 sout | x ®|® y ®|®z;3414 sout | x @|@ y @|@ z; 3427 3415 \end{cfa} 3428 3416 & 3429 3417 \begin{cfa} 3430 3418 3431 cout << x ®<< " "® << y ®<< " "®<< z << endl;3419 cout << x @<< " "@ << y @<< " "@ << z << endl; 3432 3420 \end{cfa} 3433 3421 & … … 3438 3426 \\ 3439 3427 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 3440 1 ® ®2® ®33428 1@ @2@ @3 3441 3429 \end{cfa} 3442 3430 & 3443 3431 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 3444 1 ® ®2® ®33432 1@ @2@ @3 3445 3433 \end{cfa} 3446 3434 & 3447 3435 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 3448 1 ® ®2® ®33436 1@ @2@ @3 3449 3437 \end{cfa} 3450 3438 \end{tabular} … … 3454 3442 \begin{cfa} 3455 3443 [int, [ int, int ] ] t1 = [ 1, [ 2, 3 ] ], t2 = [ 4, [ 5, 6 ] ]; 3456 sout | t1 | t2; §\C{// print tuples}§3444 sout | t1 | t2; $\C{// print tuples}$ 3457 3445 \end{cfa} 3458 3446 \begin{cfa}[showspaces=true,aboveskip=0pt] 3459 1 ®, ®2®, ®3 4®, ®5®, ®63447 1@, @2@, @3 4@, @5@, @6 3460 3448 \end{cfa} 3461 3449 Finally, \CFA uses the logical-or operator for I/O as it is the lowest-priority \emph{overloadable} operator, other than assignment. … … 3466 3454 & 3467 3455 \begin{cfa} 3468 sout | x * 3 | y + 1 | z << 2 | x == y | ®(®x | y®)® | ®(®x || y®)® | ®(®x > z ? 1 : 2®)®;3456 sout | x * 3 | y + 1 | z << 2 | x == y | @(@x | y@)@ | @(@x || y@)@ | @(@x > z ? 1 : 2@)@; 3469 3457 \end{cfa} 3470 3458 \\ … … 3472 3460 & 3473 3461 \begin{cfa} 3474 cout << x * 3 << y + 1 << ®(®z << 2®)® << ®(®x == y®)® << ®(®x | y®)® << ®(®x || y®)® << ®(®x > z ? 1 : 2®)®<< endl;3462 cout << x * 3 << y + 1 << @(@z << 2@)@ << @(@x == y@)@ << @(@x | y@)@ << @(@x || y@)@ << @(@x > z ? 1 : 2@)@ << endl; 3475 3463 \end{cfa} 3476 3464 \\ … … 3507 3495 \\ 3508 3496 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 3509 ®1® ®2.5® ®A® 3497 @1@ @2.5@ @A@ 3510 3498 3511 3499 … … 3513 3501 & 3514 3502 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 3515 ®1® ®2.5® ®A® 3503 @1@ @2.5@ @A@ 3516 3504 3517 3505 … … 3519 3507 & 3520 3508 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 3521 ®1® 3522 ®2.5® 3523 ®A® 3509 @1@ 3510 @2.5@ 3511 @A@ 3524 3512 \end{cfa} 3525 3513 \end{tabular} … … 3557 3545 3558 3546 \item 3559 {\lstset{language=CFA,deletedelim=**[is][]{¢}{¢}} 3560 A separator does not appear before a C string starting with the (extended) \Index*{ASCII}\index{ASCII!extended} characters: \lstinline[basicstyle=\tt]@,.;!?)]}%¢»@, where \lstinline[basicstyle=\tt]@»@ is a closing citation mark. 3561 \begin{cfa}[belowskip=0pt] 3547 A separator does not appear before a C string starting with the (extended) \Index*{ASCII}\index{ASCII!extended} characters: \LstStringStyle{,.;!?)]\}\%\textcent\guillemotright}, where \LstStringStyle{\guillemotright} a closing citation mark. 3548 \begin{cfa} 3562 3549 sout | 1 | ", x" | 2 | ". x" | 3 | "; x" | 4 | "! x" | 5 | "? x" | 6 | "% x" 3563 | 7 | "¢ x" | 8 | "»x" | 9 | ") x" | 10 | "] x" | 11 | "} x";3564 \end{cfa} 3565 \begin{cfa}[ basicstyle=\tt,showspaces=true,aboveskip=0pt,belowskip=0pt]3566 1 ®,® x 2®.® x 3®;® x 4®!® x 5®?® x 6®%® x 7§\color{red}\textcent§ x 8®»® x 9®)® x 10®]® x 11®}®x3567 \end{cfa} }%3568 3569 \item 3570 A separator does not appear after a C string ending with the (extended) \Index*{ASCII}\index{ASCII!extended} characters: \ lstinline[mathescape=off,basicstyle=\tt]@([{=$£¥¡¿«@, where \lstinline[basicstyle=\tt]@¡¿@ are inverted opening exclamation and question marks, and \lstinline[basicstyle=\tt]@«@is an opening citation mark.3550 | 7 | "$\LstStringStyle{\textcent}$ x" | 8 | "$\LstStringStyle{\guillemotright}$ x" | 9 | ") x" | 10 | "] x" | 11 | "} x"; 3551 \end{cfa} 3552 \begin{cfa}[showspaces=true] 3553 1@,@ x 2@.@ x 3@;@ x 4@!@ x 5@?@ x 6@%@ x 7$\R{\LstStringStyle{\textcent}}$ x 8$\R{\LstStringStyle{\guillemotright}}$ x 9@)@ x 10@]@ x 11@}@ x 3554 \end{cfa} 3555 3556 \item 3557 A separator does not appear after a C string ending with the (extended) \Index*{ASCII}\index{ASCII!extended} characters: \LstStringStyle{([\{=\$\textsterling\textyen\textexclamdown\textquestiondown\guillemotleft}, where \LstStringStyle{\textexclamdown\textquestiondown} are inverted opening exclamation and question marks, and \LstStringStyle{\guillemotleft} is an opening citation mark. 3571 3558 %$ 3572 \begin{cfa} [mathescape=off]3573 sout | "x (" | 1 | "x [" | 2 | "x {" | 3 | "x =" | 4 | "x $" | 5 | "x £" | 6 | "x ¥"3574 | 7 | "x ¡" | 8 | "x ¿" | 9 | "x «" | 10;3559 \begin{cfa} 3560 sout | "x (" | 1 | "x [" | 2 | "x {" | 3 | "x =" | 4 | "x $" | 5 | "x $\LstStringStyle{\textsterling}$" | 6 | "x $\LstStringStyle{\textyen}$" 3561 | 7 | "x $\LstStringStyle{\textexclamdown}$" | 8 | "x $\LstStringStyle{\textquestiondown}$" | 9 | "x $\LstStringStyle{\guillemotleft}$" | 10; 3575 3562 \end{cfa} 3576 3563 %$ 3577 \begin{cfa}[ mathescape=off,basicstyle=\tt,showspaces=true,aboveskip=0pt,belowskip=0pt]3578 x ®(®1 x ®[®2 x ®{®3 x ®=®4 x ®$®5 x ®£®6 x ®¥®7 x ®¡®8 x ®¿®9 x ®«®103564 \begin{cfa}[showspaces=true] 3565 x @(@1 x @[@2 x @{@3 x @=@4 x $\LstStringStyle{\textdollar}$5 x $\R{\LstStringStyle{\textsterling}}$6 x $\R{\LstStringStyle{\textyen}}$7 x $\R{\LstStringStyle{\textexclamdown}}$8 x $\R{\LstStringStyle{\textquestiondown}}$9 x $\R{\LstStringStyle{\guillemotleft}}$10 3579 3566 \end{cfa} 3580 3567 %$ 3581 3568 3582 3569 \item 3583 A seperator does not appear before/after a C string starting/ending with the \Index*{ASCII} quote or whitespace characters: \lstinline[basicstyle=\tt,showspaces=true] @`'": \t\v\f\r\n@3584 \begin{cfa} [belowskip=0pt]3570 A seperator does not appear before/after a C string starting/ending with the \Index*{ASCII} quote or whitespace characters: \lstinline[basicstyle=\tt,showspaces=true]{`'": \t\v\f\r\n} 3571 \begin{cfa} 3585 3572 sout | "x`" | 1 | "`x'" | 2 | "'x\"" | 3 | "\"x:" | 4 | ":x " | 5 | " x\t" | 6 | "\tx"; 3586 3573 \end{cfa} 3587 \begin{cfa}[ basicstyle=\tt,showspaces=true,showtabs=true,aboveskip=0pt,belowskip=0pt]3588 x ®`®1®`®x§\color{red}\texttt{'}§2§\color{red}\texttt{'}§x§\color{red}\texttt{"}§3§\color{red}\texttt{"}§x®:®4®:®x® ®5® ®x® ®6® ®x3574 \begin{cfa}[showspaces=true,showtabs=true] 3575 x@`@1@`@x$\R{\texttt{'}}$2$\R{\texttt{'}}$x$\R{\texttt{"}}$3$\R{\texttt{"}}$x@:@4@:@x@ @5@ @x@ @6@ @x 3589 3576 \end{cfa} 3590 3577 3591 3578 \item 3592 3579 If a space is desired before or after one of the special string start/end characters, simply insert a space. 3593 \begin{cfa} [belowskip=0pt]3594 sout | "x ( §\color{red}\texttt{\textvisiblespace}§" | 1 | "§\color{red}\texttt{\textvisiblespace}§) x" | 2 | "§\color{red}\texttt{\textvisiblespace}§, x" | 3 | "§\color{red}\texttt{\textvisiblespace}§:x:§\color{red}\texttt{\textvisiblespace}§" | 4;3595 \end{cfa} 3596 \begin{cfa}[ basicstyle=\tt,showspaces=true,showtabs=true,aboveskip=0pt,belowskip=0pt]3597 x ( ® ®1® ®) x 2® ®, x 3® ®:x:® ®43580 \begin{cfa} 3581 sout | "x ($\R{\texttt{\textvisiblespace}}$" | 1 | "$\R{\texttt{\textvisiblespace}}$) x" | 2 | "$\R{\texttt{\textvisiblespace}}$, x" | 3 | "$\R{\texttt{\textvisiblespace}}$:x:$\R{\texttt{\textvisiblespace}}$" | 4; 3582 \end{cfa} 3583 \begin{cfa}[showspaces=true,showtabs=true] 3584 x (@ @1@ @) x 2@ @, x 3@ @:x:@ @4 3598 3585 \end{cfa} 3599 3586 \end{enumerate} … … 3608 3595 \Indexc{sepSet}\index{manipulator!sepSet@©sepSet©} and \Indexc{sep}\index{manipulator!sep@©sep©}/\Indexc{sepGet}\index{manipulator!sepGet@©sepGet©} set and get the separator string. 3609 3596 The separator string can be at most 16 characters including the ©'\0'© string terminator (15 printable characters). 3610 \begin{cfa}[ mathescape=off,belowskip=0pt]3611 sepSet( sout, ", $" ); §\C{// set separator from " " to ", \$"}§3612 sout | 1 | 2 | 3 | " \"" | ®sep®| "\"";3597 \begin{cfa}[escapechar=off,belowskip=0pt] 3598 sepSet( sout, ", $" ); $\C{// set separator from " " to ", \$"}$ 3599 sout | 1 | 2 | 3 | " \"" | @sep@ | "\""; 3613 3600 \end{cfa} 3614 3601 %$ 3615 3602 \begin{cfa}[mathescape=off,showspaces=true,aboveskip=0pt] 3616 1 ®, $®2®, $®3 ®", $"®3603 1@, $@2@, $@3 @", $"@ 3617 3604 \end{cfa} 3618 3605 %$ 3619 3606 \begin{cfa}[belowskip=0pt] 3620 sepSet( sout, " " ); §\C{// reset separator to " "}§3621 sout | 1 | 2 | 3 | " \"" | ®sepGet( sout )®| "\"";3607 sepSet( sout, " " ); $\C{// reset separator to " "}$ 3608 sout | 1 | 2 | 3 | " \"" | @sepGet( sout )@ | "\""; 3622 3609 \end{cfa} 3623 3610 \begin{cfa}[showspaces=true,aboveskip=0pt] 3624 1 ® ®2® ®3 ®" "®3611 1@ @2@ @3 @" "@ 3625 3612 \end{cfa} 3626 3613 ©sepGet© can be used to store a separator and then restore it: 3627 3614 \begin{cfa}[belowskip=0pt] 3628 char store[ ®sepSize®]; §\C{// sepSize is the maximum separator size}§3629 strcpy( store, sepGet( sout ) ); §\C{// copy current separator}§3630 sepSet( sout, "_" ); §\C{// change separator to underscore}§3615 char store[@sepSize@]; $\C{// sepSize is the maximum separator size}$ 3616 strcpy( store, sepGet( sout ) ); $\C{// copy current separator}$ 3617 sepSet( sout, "_" ); $\C{// change separator to underscore}$ 3631 3618 sout | 1 | 2 | 3; 3632 3619 \end{cfa} 3633 3620 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 3634 1 ®_®2®_®33621 1@_@2@_@3 3635 3622 \end{cfa} 3636 3623 \begin{cfa}[belowskip=0pt] 3637 sepSet( sout, store ); §\C{// change separator back to original}§3624 sepSet( sout, store ); $\C{// change separator back to original}$ 3638 3625 sout | 1 | 2 | 3; 3639 3626 \end{cfa} 3640 3627 \begin{cfa}[showspaces=true,aboveskip=0pt] 3641 1 ® ®2® ®33628 1@ @2@ @3 3642 3629 \end{cfa} 3643 3630 … … 3646 3633 The tuple separator-string can be at most 16 characters including the ©'\0'© string terminator (15 printable characters). 3647 3634 \begin{cfa}[belowskip=0pt] 3648 sepSetTuple( sout, " " ); §\C{// set tuple separator from ", " to " "}§3649 sout | t1 | t2 | " \"" | ®sepTuple®| "\"";3635 sepSetTuple( sout, " " ); $\C{// set tuple separator from ", " to " "}$ 3636 sout | t1 | t2 | " \"" | @sepTuple@ | "\""; 3650 3637 \end{cfa} 3651 3638 \begin{cfa}[showspaces=true,aboveskip=0pt] 3652 1 2 3 4 5 6 ®" "®3639 1 2 3 4 5 6 @" "@ 3653 3640 \end{cfa} 3654 3641 \begin{cfa}[belowskip=0pt] 3655 sepSetTuple( sout, ", " ); §\C{// reset tuple separator to ", "}§3656 sout | t1 | t2 | " \"" | ®sepGetTuple( sout )®| "\"";3642 sepSetTuple( sout, ", " ); $\C{// reset tuple separator to ", "}$ 3643 sout | t1 | t2 | " \"" | @sepGetTuple( sout )@ | "\""; 3657 3644 \end{cfa} 3658 3645 \begin{cfa}[showspaces=true,aboveskip=0pt] 3659 1, 2, 3 4, 5, 6 ®", "®3646 1, 2, 3 4, 5, 6 @", "@ 3660 3647 \end{cfa} 3661 3648 As for ©sepGet©, ©sepGetTuple© can be use to store a tuple separator and then restore it. … … 3664 3651 \Indexc{sepDisable}\index{manipulator!sepDisable@©sepDisable©} and \Indexc{sepEnable}\index{manipulator!sepEnable@©sepEnable©} toggle printing the separator. 3665 3652 \begin{cfa}[belowskip=0pt] 3666 sout | sepDisable | 1 | 2 | 3; §\C{// turn off implicit separator}§3653 sout | sepDisable | 1 | 2 | 3; $\C{// turn off implicit separator}$ 3667 3654 \end{cfa} 3668 3655 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] … … 3670 3657 \end{cfa} 3671 3658 \begin{cfa}[belowskip=0pt] 3672 sout | sepEnable | 1 | 2 | 3; §\C{// turn on implicit separator}§3659 sout | sepEnable | 1 | 2 | 3; $\C{// turn on implicit separator}$ 3673 3660 \end{cfa} 3674 3661 \begin{cfa}[mathescape=off,showspaces=true,aboveskip=0pt,belowskip=0pt] … … 3679 3666 \Indexc{sepOn}\index{manipulator!sepOn@©sepOn©} and \Indexc{sepOff}\index{manipulator!sepOff@©sepOff©} toggle printing the separator with respect to the next printed item, and then return to the global seperator setting. 3680 3667 \begin{cfa}[belowskip=0pt] 3681 sout | 1 | sepOff | 2 | 3; §\C{// turn off implicit separator for the next item}§3668 sout | 1 | sepOff | 2 | 3; $\C{// turn off implicit separator for the next item}$ 3682 3669 \end{cfa} 3683 3670 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] … … 3685 3672 \end{cfa} 3686 3673 \begin{cfa}[belowskip=0pt] 3687 sout | sepDisable | 1 | sepOn | 2 | 3; §\C{// turn on implicit separator for the next item}§3674 sout | sepDisable | 1 | sepOn | 2 | 3; $\C{// turn on implicit separator for the next item}$ 3688 3675 \end{cfa} 3689 3676 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] … … 3692 3679 The tuple separator also responses to being turned on and off. 3693 3680 \begin{cfa}[belowskip=0pt] 3694 sout | t1 | sepOff | t2; §\C{// turn off implicit separator for the next item}§3681 sout | t1 | sepOff | t2; $\C{// turn off implicit separator for the next item}$ 3695 3682 \end{cfa} 3696 3683 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] … … 3700 3687 use ©sep© to accomplish this functionality. 3701 3688 \begin{cfa}[belowskip=0pt] 3702 sout | sepOn | 1 | 2 | 3 | sepOn; §\C{// sepOn does nothing at start/end of line}§3689 sout | sepOn | 1 | 2 | 3 | sepOn; $\C{// sepOn does nothing at start/end of line}$ 3703 3690 \end{cfa} 3704 3691 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] … … 3706 3693 \end{cfa} 3707 3694 \begin{cfa}[belowskip=0pt] 3708 sout | sep | 1 | 2 | 3 | sep ; §\C{// use sep to print separator at start/end of line}§3695 sout | sep | 1 | 2 | 3 | sep ; $\C{// use sep to print separator at start/end of line}$ 3709 3696 \end{cfa} 3710 3697 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 3711 ® ®1 2 3® ® 3698 @ @1 2 3@ @ 3712 3699 \end{cfa} 3713 3700 \end{enumerate} … … 3721 3708 \begin{enumerate}[parsep=0pt] 3722 3709 \item 3723 \Indexc{nl}\index{manipulator!nl@©nl©} scans characters until the next newline character, i.e.,ignore the remaining characters in the line.3710 \Indexc{nl}\index{manipulator!nl@©nl©} scans characters until the next newline character, \ie ignore the remaining characters in the line. 3724 3711 \item 3725 3712 \Indexc{nlOn}\index{manipulator!nlOn@©nlOn©} reads the newline character, when reading single characters. … … 3729 3716 For example, in: 3730 3717 \begin{cfa} 3731 sin | i | ®nl®| j;3732 1 ®2®3718 sin | i | @nl@ | j; 3719 1 @2@ 3733 3720 3 3734 3721 \end{cfa} … … 3740 3727 \Indexc{nl}\index{manipulator!nl@©nl©} inserts a newline. 3741 3728 \begin{cfa} 3742 sout | nl; §\C{// only print newline}§3743 sout | 2; §\C{// implicit newline}§3744 sout | 3 | nl | 4 | nl; §\C{// terminating nl merged with implicit newline}§3745 sout | 5 | nl | nl; §\C{// again terminating nl merged with implicit newline}§3746 sout | 6; §\C{// implicit newline}§3729 sout | nl; $\C{// only print newline}$ 3730 sout | 2; $\C{// implicit newline}$ 3731 sout | 3 | nl | 4 | nl; $\C{// terminating nl merged with implicit newline}$ 3732 sout | 5 | nl | nl; $\C{// again terminating nl merged with implicit newline}$ 3733 sout | 6; $\C{// implicit newline}$ 3747 3734 3748 3735 2 … … 3771 3758 0b0 0b11011 0b11011 0b11011 0b11011 3772 3759 sout | bin( -27HH ) | bin( -27H ) | bin( -27 ) | bin( -27L ); 3773 0b11100101 0b1111111111100101 0b11111111111111111111111111100101 0b ®(58 1s)®1001013760 0b11100101 0b1111111111100101 0b11111111111111111111111111100101 0b@(58 1s)@100101 3774 3761 \end{cfa} 3775 3762 … … 3810 3797 \begin{cfa}[belowskip=0pt] 3811 3798 sout | upcase( bin( 27 ) ) | upcase( hex( 27 ) ) | upcase( 27.5e-10 ) | upcase( hex( 27.5 ) ); 3812 0 ®B®11011 0®X®1®B® 2.75®E®-09 0®X®1.®B®8®P®+43799 0@B@11011 0@X@1@B@ 2.75@E@-09 0@X@1.@B@8@P@+4 3813 3800 \end{cfa} 3814 3801 … … 3826 3813 \begin{cfa}[belowskip=0pt] 3827 3814 sout | 0. | nodp( 0. ) | 27.0 | nodp( 27.0 ) | nodp( 27.5 ); 3828 0.0 ®0® 27.0 ®27®27.53815 0.0 @0@ 27.0 @27@ 27.5 3829 3816 \end{cfa} 3830 3817 … … 3833 3820 \begin{cfa}[belowskip=0pt] 3834 3821 sout | sign( 27 ) | sign( -27 ) | sign( 27. ) | sign( -27. ) | sign( 27.5 ) | sign( -27.5 ); 3835 ®+®27 -27 ®+®27.0 -27.0 ®+®27.5 -27.53822 @+@27 -27 @+@27.0 -27.0 @+@27.5 -27.5 3836 3823 \end{cfa} 3837 3824 … … 3846 3833 \end{cfa} 3847 3834 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 3848 ® ®34 ® ®34 343849 ® ®4.000000 ® ®4.000000 4.0000003850 ® ®ab ® ®ab ab3835 @ @34 @ @34 34 3836 @ @4.000000 @ @4.000000 4.000000 3837 @ @ab @ @ab ab 3851 3838 \end{cfa} 3852 3839 If the value is larger, it is printed without truncation, ignoring the ©minimum©. … … 3857 3844 \end{cfa} 3858 3845 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 3859 3456 ®7® 345®67® 34®567®3860 3456 ®.® 345®6.® 34®56.®3861 abcd ®e® abc®de® ab®cde®3846 3456@7@ 345@67@ 34@567@ 3847 3456@.@ 345@6.@ 34@56.@ 3848 abcd@e@ abc@de@ ab@cde@ 3862 3849 \end{cfa} 3863 3850 … … 3868 3855 \end{cfa} 3869 3856 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 3870 ®0®34 ®00®34 ®00000000®343857 @0@34 @00@34 @00000000@34 3871 3858 \end{cfa} 3872 3859 If the value is larger, it is printed without truncation, ignoring the ©precision©. … … 3883 3870 \end{cfa} 3884 3871 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 3885 ® ® ®00000000®343872 @ @ @00000000@34 3886 3873 \end{cfa} 3887 3874 For floating-point types, ©precision© is the minimum number of digits after the decimal point. … … 3890 3877 \end{cfa} 3891 3878 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 3892 27. ®500® 27.®5® 28. 27.®50000000®3893 \end{cfa} 3894 For the C-string type, ©precision© is the maximum number of printed characters, so the string is trunca red if it exceeds the maximum.3879 27.@500@ 27.@5@ 28. 27.@50000000@ 3880 \end{cfa} 3881 For the C-string type, ©precision© is the maximum number of printed characters, so the string is truncated if it exceeds the maximum. 3895 3882 \begin{cfa}[belowskip=0pt] 3896 3883 sout | wd( 6,8, "abcd" ) | wd( 6,8, "abcdefghijk" ) | wd( 6,3, "abcd" ); … … 3908 3895 \end{cfa} 3909 3896 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 3910 234.567 234.5 ®7® 234.®6® 23®5®3897 234.567 234.5@7@ 234.@6@ 23@5@ 3911 3898 \end{cfa} 3912 3899 If a value's magnitude is greater than ©significant©, the value is printed in scientific notation with the specified number of significant digits. … … 3915 3902 \end{cfa} 3916 3903 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 3917 234567. 2.3457 ®e+05® 2.346®e+05® 2.35®e+05®3904 234567. 2.3457@e+05@ 2.346@e+05@ 2.35@e+05@ 3918 3905 \end{cfa} 3919 3906 If ©significant© is greater than ©minimum©, it defines the number of printed characters. … … 3931 3918 \end{cfa} 3932 3919 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 3933 27 ® ® 27.000000 27.500000 027 27.500® ®3920 27@ @ 27.000000 27.500000 027 27.500@ @ 3934 3921 \end{cfa} 3935 3922 … … 3938 3925 \begin{cfa}[belowskip=0pt] 3939 3926 sout | pad0( wd( 4, 27 ) ) | pad0( wd( 4,3, 27 ) ) | pad0( wd( 8,3, 27.5 ) ); 3940 ®00®27 ®0®27 ®00®27.5003927 @00@27 @0@27 @00@27.500 3941 3928 \end{cfa} 3942 3929 \end{enumerate} … … 4034 4021 \end{cfa} 4035 4022 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 4036 ®abc ® 4037 ®abc ® 4038 ®xx® 4023 @abc @ 4024 @abc @ 4025 @xx@ 4039 4026 \end{cfa} 4040 4027 … … 4047 4034 \end{cfa} 4048 4035 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 4049 ®abcd1233.456E+2® 4036 @abcd1233.456E+2@ 4050 4037 \end{cfa} 4051 4038 Note, input ©wdi© cannot be overloaded with output ©wd© because both have the same parameters but return different types. … … 4060 4047 \end{cfa} 4061 4048 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 4062 ® -75.35e-4®254049 @ -75.35e-4@ 25 4063 4050 \end{cfa} 4064 4051 … … 4072 4059 \end{cfa} 4073 4060 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 4074 ®bca®xyz4061 @bca@xyz 4075 4062 \end{cfa} 4076 4063 … … 4084 4071 \end{cfa} 4085 4072 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 4086 ®xyz®bca4073 @xyz@bca 4087 4074 \end{cfa} 4088 4075 \end{enumerate} … … 4101 4088 4102 4089 A type definition is different from a typedef in C because a typedef just creates an alias for a type, while Do.s type definition creates a distinct type. 4103 This means that users can define distinct function overloads for the new type (see Overloading for more information).4090 This means that users can define distinct function overloads for the new type \see{\VRef{s:Overloading} for more information}. 4104 4091 For example: 4105 4092 … … 4207 4194 \CFA supports C initialization of structures, but it also adds constructors for more advanced initialization. 4208 4195 Additionally, \CFA adds destructors that are called when a variable is deallocated (variable goes out of scope or object is deleted). 4209 These functions take a reference to the structure as a parameter (see References for more information).4196 These functions take a reference to the structure as a parameter \see{\VRef{s:PointerReference} for more information}. 4210 4197 4211 4198 \begin{figure} … … 4258 4245 4259 4246 \section{Overloading} 4247 \label{s:Overloading} 4260 4248 4261 4249 Overloading refers to the capability of a programmer to define and use multiple objects in a program with the same name. … … 4290 4278 4291 4279 4292 \subsection{ OverloadedConstant}4280 \subsection{Constant} 4293 4281 4294 4282 The constants 0 and 1 have special meaning. … … 4329 4317 4330 4318 4331 \subsection{Variable Overloading} 4319 \subsection{Variable} 4320 \label{s:VariableOverload} 4332 4321 4333 4322 The overload rules of \CFA allow a programmer to define multiple variables with the same name, but different types. … … 4372 4361 4373 4362 4374 \subsection{Operator Overloading}4363 \subsection{Operator} 4375 4364 4376 4365 \CFA also allows operators to be overloaded, to simplify the use of user-defined types. … … 4468 4457 For example, given 4469 4458 \begin{cfa} 4470 auto j = ®...®4459 auto j = @...@ 4471 4460 \end{cfa} 4472 4461 and the need to write a routine to compute using ©j© 4473 4462 \begin{cfa} 4474 void rtn( ®...®parm );4463 void rtn( @...@ parm ); 4475 4464 rtn( j ); 4476 4465 \end{cfa} … … 4713 4702 4714 4703 coroutine Fibonacci { 4715 int fn; §\C{// used for communication}§4704 int fn; $\C{// used for communication}$ 4716 4705 }; 4717 4706 void ?{}( Fibonacci * this ) { … … 4719 4708 } 4720 4709 void main( Fibonacci * this ) { 4721 int fn1, fn2; §\C{// retained between resumes}§4722 this->fn = 0; §\C{// case 0}§4710 int fn1, fn2; $\C{// retained between resumes}$ 4711 this->fn = 0; $\C{// case 0}$ 4723 4712 fn1 = this->fn; 4724 suspend(); §\C{// return to last resume}§4725 4726 this->fn = 1; §\C{// case 1}§4713 suspend(); $\C{// return to last resume}$ 4714 4715 this->fn = 1; $\C{// case 1}$ 4727 4716 fn2 = fn1; 4728 4717 fn1 = this->fn; 4729 suspend(); §\C{// return to last resume}§4730 4731 for ( ;; ) { §\C{// general case}§4718 suspend(); $\C{// return to last resume}$ 4719 4720 for ( ;; ) { $\C{// general case}$ 4732 4721 this->fn = fn1 + fn2; 4733 4722 fn2 = fn1; 4734 4723 fn1 = this->fn; 4735 suspend(); §\C{// return to last resume}§4724 suspend(); $\C{// return to last resume}$ 4736 4725 } // for 4737 4726 } 4738 4727 int next( Fibonacci * this ) { 4739 resume( this ); §\C{// transfer to last suspend}§4728 resume( this ); $\C{// transfer to last suspend}$ 4740 4729 return this->fn; 4741 4730 } … … 4964 4953 When building a \CFA module which needs to be callable from C code, users can use the tools to generate a header file suitable for including in these C files with all of the needed declarations. 4965 4954 4966 In order to interoperate with existing C code, \CFA files can still include header files, the contents of which will be enclosed in a C linkage section to indicate C calling conventions (see Interoperability for more information).4955 In order to interoperate with existing C code, \CFA files can still include header files, the contents of which will be enclosed in a C linkage section to indicate C calling conventions \see{\VRef{s:Interoperability} for more information}. 4967 4956 4968 4957 … … 5630 5619 \end{cfa} 5631 5620 & 5632 \begin{ lstlisting}[language=C++]5621 \begin{C++} 5633 5622 class Line { 5634 5623 float lnth; … … 5657 5646 Line line1; 5658 5647 Line line2( 3.4 ); 5659 \end{ lstlisting}5648 \end{C++} 5660 5649 & 5661 5650 \begin{lstlisting}[language=Golang] … … 6282 6271 In \CFA, there are ambiguous cases with dereference and operator identifiers, \eg ©int *?*?()©, where the string ©*?*?© can be interpreted as: 6283 6272 \begin{cfa} 6284 *? §\color{red}\textvisiblespace§*? §\C{// dereference operator, dereference operator}§6285 * §\color{red}\textvisiblespace§?*? §\C{// dereference, multiplication operator}§6273 *?$\R{\textvisiblespace}$*? $\C{// dereference operator, dereference operator}$ 6274 *$\R{\textvisiblespace}$?*? $\C{// dereference, multiplication operator}$ 6286 6275 \end{cfa} 6287 6276 By default, the first interpretation is selected, which does not yield a meaningful parse. … … 6292 6281 The ambiguity occurs when the deference operator has no parameters: 6293 6282 \begin{cfa} 6294 *?() §\color{red}\textvisiblespace...§;6295 *?() §\color{red}\textvisiblespace...§(...) ;6283 *?()$\R{\textvisiblespace...}$ ; 6284 *?()$\R{\textvisiblespace...}$(...) ; 6296 6285 \end{cfa} 6297 6286 requiring arbitrary whitespace look-ahead for the routine-call parameter-list to disambiguate. … … 6301 6290 The remaining cases are with the increment/decrement operators and conditional expression, \eg: 6302 6291 \begin{cfa} 6303 i++? §\color{red}\textvisiblespace...§(...);6304 i?++ §\color{red}\textvisiblespace...§(...);6292 i++?$\R{\textvisiblespace...}$(...); 6293 i?++$\R{\textvisiblespace...}$(...); 6305 6294 \end{cfa} 6306 6295 requiring arbitrary whitespace look-ahead for the operator parameter-list, even though that interpretation is an incorrect expression (juxtaposed identifiers). 6307 6296 Therefore, it is necessary to disambiguate these cases with a space: 6308 6297 \begin{cfa} 6309 i++ §\color{red}\textvisiblespace§? i : 0;6310 i? §\color{red}\textvisiblespace§++i : 0;6298 i++$\R{\textvisiblespace}$? i : 0; 6299 i?$\R{\textvisiblespace}$++i : 0; 6311 6300 \end{cfa} 6312 6301 … … 6321 6310 \begin{description} 6322 6311 \item[Change:] add new keywords \\ 6323 New keywords are added to \CFA (see~\VRef{s:CFAKeywords}).6312 New keywords are added to \CFA \see{\VRef{s:CFAKeywords}}. 6324 6313 \item[Rationale:] keywords added to implement new semantics of \CFA. 6325 6314 \item[Effect on original feature:] change to semantics of well-defined feature. \\ 6326 6315 Any \Celeven programs using these keywords as identifiers are invalid \CFA programs. 6327 \item[Difficulty of converting:] keyword clashes are accommodated by syntactic transformations using the \CFA backquote escape-mechanism (see~\VRef{s:BackquoteIdentifiers}).6316 \item[Difficulty of converting:] keyword clashes are accommodated by syntactic transformations using the \CFA backquote escape-mechanism \see{\VRef{s:BackquoteIdentifiers}}. 6328 6317 \item[How widely used:] clashes among new \CFA keywords and existing identifiers are rare. 6329 6318 \end{description} … … 6335 6324 \eg: 6336 6325 \begin{cfa} 6337 x; §\C{// int x}§6338 *y; §\C{// int *y}§6339 f( p1, p2 ); §\C{// int f( int p1, int p2 );}§6340 g( p1, p2 ) int p1, p2; §\C{// int g( int p1, int p2 );}§6326 x; $\C{// int x}$ 6327 *y; $\C{// int *y}$ 6328 f( p1, p2 ); $\C{// int f( int p1, int p2 );}$ 6329 g( p1, p2 ) int p1, p2; $\C{// int g( int p1, int p2 );}$ 6341 6330 \end{cfa} 6342 6331 \CFA continues to support K\&R routine definitions: 6343 6332 \begin{cfa} 6344 f( a, b, c ) §\C{// default int return}§6345 int a, b; char c §\C{// K\&R parameter declarations}§6333 f( a, b, c ) $\C{// default int return}$ 6334 int a, b; char c $\C{// K\&R parameter declarations}$ 6346 6335 { 6347 6336 ... … … 6362 6351 int rtn( int i ); 6363 6352 int rtn( char c ); 6364 rtn( 'x' ); §\C{// programmer expects 2nd rtn to be called}§6353 rtn( 'x' ); $\C{// programmer expects 2nd rtn to be called}$ 6365 6354 \end{cfa} 6366 6355 \item[Rationale:] it is more intuitive for the call to ©rtn© to match the second version of definition of ©rtn© rather than the first. … … 6384 6373 \item[Change:] make string literals ©const©: 6385 6374 \begin{cfa} 6386 char * p = "abc"; §\C{// valid in C, deprecated in \CFA}§6387 char * q = expr ? "abc" : "de"; §\C{// valid in C, invalid in \CFA}§6375 char * p = "abc"; $\C{// valid in C, deprecated in \CFA}$ 6376 char * q = expr ? "abc" : "de"; $\C{// valid in C, invalid in \CFA}$ 6388 6377 \end{cfa} 6389 6378 The type of a string literal is changed from ©[] char© to ©const [] char©. … … 6392 6381 \begin{cfa} 6393 6382 char * p = "abc"; 6394 p[0] = 'w'; §\C{// segment fault or change constant literal}§6383 p[0] = 'w'; $\C{// segment fault or change constant literal}$ 6395 6384 \end{cfa} 6396 6385 The same problem occurs when passing a string literal to a routine that changes its argument. … … 6404 6393 \item[Change:] remove \newterm{tentative definitions}, which only occurs at file scope: 6405 6394 \begin{cfa} 6406 int i; §\C{// forward definition}§6407 int *j = ®&i®; §\C{// forward reference, valid in C, invalid in \CFA}§6408 int i = 0; §\C{// definition}§6395 int i; $\C{// forward definition}$ 6396 int *j = @&i@; $\C{// forward reference, valid in C, invalid in \CFA}$ 6397 int i = 0; $\C{// definition}$ 6409 6398 \end{cfa} 6410 6399 is valid in C, and invalid in \CFA because duplicate overloaded object definitions at the same scope level are disallowed. … … 6412 6401 \begin{cfa} 6413 6402 struct X { int i; struct X *next; }; 6414 static struct X a; §\C{// forward definition}§6415 static struct X b = { 0, ®&a® };§\C{// forward reference, valid in C, invalid in \CFA}§6416 static struct X a = { 1, &b }; §\C{// definition}§6403 static struct X a; $\C{// forward definition}$ 6404 static struct X b = { 0, @&a@ };$\C{// forward reference, valid in C, invalid in \CFA}$ 6405 static struct X a = { 1, &b }; $\C{// definition}$ 6417 6406 \end{cfa} 6418 6407 \item[Rationale:] avoids having different initialization rules for builtin types and user-defined types. … … 6426 6415 \item[Change:] have ©struct© introduce a scope for nested types: 6427 6416 \begin{cfa} 6428 enum ®Colour®{ R, G, B, Y, C, M };6417 enum @Colour@ { R, G, B, Y, C, M }; 6429 6418 struct Person { 6430 enum ®Colour® { R, G, B }; §\C[7cm]{// nested type}§6431 struct Face { §\C{// nested type}§6432 ®Colour® Eyes, Hair; §\C{// type defined outside (1 level)}§6419 enum @Colour@ { R, G, B }; $\C[7cm]{// nested type}$ 6420 struct Face { $\C{// nested type}$ 6421 @Colour@ Eyes, Hair; $\C{// type defined outside (1 level)}$ 6433 6422 }; 6434 ®.Colour® shirt; §\C{// type defined outside (top level)}§6435 ®Colour® pants; §\C{// type defined same level}§6436 Face looks[10]; §\C{// type defined same level}§6423 @.Colour@ shirt; $\C{// type defined outside (top level)}$ 6424 @Colour@ pants; $\C{// type defined same level}$ 6425 Face looks[10]; $\C{// type defined same level}$ 6437 6426 }; 6438 ®Colour® c = R; §\C{// type/enum defined same level}§ 6439 Person ®.Colour® pc = Person®.®R;§\C{// type/enum defined inside}§6440 Person ®.®Face pretty; §\C{// type defined inside}\CRT§6427 @Colour@ c = R; $\C{// type/enum defined same level}$ 6428 Person@.Colour@ pc = Person@.@R;$\C{// type/enum defined inside}$ 6429 Person@.@Face pretty; $\C{// type defined inside}\CRT$ 6441 6430 \end{cfa} 6442 6431 In C, the name of the nested types belongs to the same scope as the name of the outermost enclosing structure, \ie the nested types are hoisted to the scope of the outer-most type, which is not useful and confusing. … … 6455 6444 \item[Difficulty of converting:] Semantic transformation. To make the struct type name visible in the scope of the enclosing struct, the struct tag could be declared in the scope of the enclosing struct, before the enclosing struct is defined. Example: 6456 6445 \begin{cfa} 6457 struct Y; §\C{// struct Y and struct X are at the same scope}§6446 struct Y; $\C{// struct Y and struct X are at the same scope}$ 6458 6447 struct X { 6459 6448 struct Y { /* ... */ } y; … … 6470 6459 \begin{cfa} 6471 6460 void foo() { 6472 int * b = malloc( sizeof(int) ); §\C{// implicitly convert void * to int *}§6473 char * c = b; §\C{// implicitly convert int * to void *, and then void * to char *}§6461 int * b = malloc( sizeof(int) ); $\C{// implicitly convert void * to int *}$ 6462 char * c = b; $\C{// implicitly convert int * to void *, and then void * to char *}$ 6474 6463 } 6475 6464 \end{cfa} 6476 6465 \item[Rationale:] increase type safety 6477 6466 \item[Effect on original feature:] deletion of semantically well-defined feature. 6478 \item[Difficulty of converting:] requires adding a cast (see \VRef{s:StorageManagement} for better alternatives):6467 \item[Difficulty of converting:] requires adding a cast \see{\VRef{s:StorageManagement} for better alternatives}: 6479 6468 \begin{cfa} 6480 6469 int * b = (int *)malloc( sizeof(int) ); … … 6586 6575 \end{cquote} 6587 6576 For the prescribed head-files, \CFA uses header interposition to wraps these includes in an ©extern "C"©; 6588 hence, names in these include files are not mangled\index{mangling!name} (see~\VRef{s:Interoperability}).6577 hence, names in these include files are not mangled\index{mangling!name} \see{\VRef{s:Interoperability}}. 6589 6578 All other C header files must be explicitly wrapped in ©extern "C"© to prevent name mangling. 6590 6579 This approach is different from \Index*[C++]{\CC{}} where the name-mangling issue is handled internally in C header-files through checks for preprocessor variable ©__cplusplus©, which adds appropriate ©extern "C"© qualifiers. … … 6649 6638 Type-safe allocation is provided for all C allocation routines and new \CFA allocation routines, \eg in 6650 6639 \begin{cfa} 6651 int * ip = (int *)malloc( sizeof(int) ); §\C{// C}§6652 int * ip = malloc(); §\C{// \CFA type-safe version of C malloc}§6653 int * ip = alloc(); §\C{// \CFA type-safe uniform alloc}§6640 int * ip = (int *)malloc( sizeof(int) ); $\C{// C}$ 6641 int * ip = malloc(); $\C{// \CFA type-safe version of C malloc}$ 6642 int * ip = alloc(); $\C{// \CFA type-safe uniform alloc}$ 6654 6643 \end{cfa} 6655 6644 the latter two allocations determine the allocation size from the type of ©p© (©int©) and cast the pointer to the allocated storage to ©int *©. … … 6658 6647 \begin{cfa} 6659 6648 struct S { int i; } __attribute__(( aligned( 128 ) )); // cache-line alignment 6660 S * sp = malloc(); §\C{// honour type alignment}§6649 S * sp = malloc(); $\C{// honour type alignment}$ 6661 6650 \end{cfa} 6662 6651 the storage allocation is implicitly aligned to 128 rather than the default 16. … … 6673 6662 \CFA memory management extends allocation to support constructors for initialization of allocated storage, \eg in 6674 6663 \begin{cfa} 6675 struct S { int i; }; §\C{// cache-line aglinment}§6664 struct S { int i; }; $\C{// cache-line alignment}$ 6676 6665 void ?{}( S & s, int i ) { s.i = i; } 6677 6666 // assume ?|? operator for printing an S 6678 6667 6679 S & sp = * ®new®( 3 ); §\C{// call constructor after allocation}§6668 S & sp = *@new@( 3 ); $\C{// call constructor after allocation}$ 6680 6669 sout | sp.i; 6681 ®delete®( &sp );6682 6683 S * spa = ®anew®( 10, 5 ); §\C{// allocate array and initialize each array element}§6670 @delete@( &sp ); 6671 6672 S * spa = @anew@( 10, 5 ); $\C{// allocate array and initialize each array element}$ 6684 6673 for ( i; 10 ) sout | spa[i] | nonl; 6685 6674 sout | nl; 6686 ®adelete®( 10, spa );6675 @adelete@( 10, spa ); 6687 6676 \end{cfa} 6688 6677 Allocation routines ©new©/©anew© allocate a variable/array and initialize storage using the allocated type's constructor. … … 6693 6682 extern "C" { 6694 6683 // C unsafe allocation 6695 void * malloc( size_t size ); §\indexc{malloc}§6696 void * calloc( size_t dim, size_t size ); §\indexc{calloc}§6697 void * realloc( void * ptr, size_t size ); §\indexc{realloc}§6698 void * memalign( size_t align, size_t size ); §\indexc{memalign}§6699 void * aligned_alloc( size_t align, size_t size ); §\indexc{aligned_alloc}§6700 int posix_memalign( void ** ptr, size_t align, size_t size ); §\indexc{posix_memalign}§6701 void * cmemalign( size_t alignment, size_t noOfElems, size_t elemSize ); §\indexc{cmemalign}§// CFA6684 void * malloc( size_t size );$\indexc{malloc}$ 6685 void * calloc( size_t dim, size_t size );$\indexc{calloc}$ 6686 void * realloc( void * ptr, size_t size );$\indexc{realloc}$ 6687 void * memalign( size_t align, size_t size );$\indexc{memalign}$ 6688 void * aligned_alloc( size_t align, size_t size );$\indexc{aligned_alloc}$ 6689 int posix_memalign( void ** ptr, size_t align, size_t size );$\indexc{posix_memalign}$ 6690 void * cmemalign( size_t alignment, size_t noOfElems, size_t elemSize );$\indexc{cmemalign}$ // CFA 6702 6691 6703 6692 // C unsafe initialization/copy 6704 void * memset( void * dest, int c, size_t size ); §\indexc{memset}§6705 void * memcpy( void * dest, const void * src, size_t size ); §\indexc{memcpy}§6693 void * memset( void * dest, int c, size_t size );$\indexc{memset}$ 6694 void * memcpy( void * dest, const void * src, size_t size );$\indexc{memcpy}$ 6706 6695 } 6707 6696 … … 6709 6698 6710 6699 forall( dtype T | sized(T) ) { 6711 // §\CFA§safe equivalents, i.e., implicit size specification6700 // $\CFA$ safe equivalents, i.e., implicit size specification 6712 6701 T * malloc( void ); 6713 6702 T * calloc( size_t dim ); … … 6718 6707 int posix_memalign( T ** ptr, size_t align ); 6719 6708 6720 // §\CFA§safe general allocation, fill, resize, alignment, array6721 T * alloc( void ); §\indexc{alloc}§ §\C[3.5in]{// variable, T size}§6722 T * alloc( size_t dim ); §\C{// array[dim], T size elements}§6723 T * alloc( T ptr[], size_t dim ); §\C{// realloc array[dim], T size elements}§6724 6725 T * alloc_set( char fill ); §\indexc{alloc_set}§ §\C{// variable, T size, fill bytes with value}§6726 T * alloc_set( T fill ); §\C{// variable, T size, fill with value}§6727 T * alloc_set( size_t dim, char fill ); §\C{// array[dim], T size elements, fill bytes with value}§6728 T * alloc_set( size_t dim, T fill ); §\C{// array[dim], T size elements, fill elements with value}§6729 T * alloc_set( size_t dim, const T fill[] ); §\C{// array[dim], T size elements, fill elements with array}§6730 T * alloc_set( T ptr[], size_t dim, char fill ); §\C{// realloc array[dim], T size elements, fill bytes with value}§6731 6732 T * alloc_align( size_t align ); §\C{// aligned variable, T size}§6733 T * alloc_align( size_t align, size_t dim ); §\C{// aligned array[dim], T size elements}§6734 T * alloc_align( T ptr[], size_t align ); §\C{// realloc new aligned array}§6735 T * alloc_align( T ptr[], size_t align, size_t dim ); §\C{// realloc new aligned array[dim]}§6736 6737 T * alloc_align_set( size_t align, char fill ); §\C{// aligned variable, T size, fill bytes with value}§6738 T * alloc_align_set( size_t align, T fill ); §\C{// aligned variable, T size, fill with value}§6739 T * alloc_align_set( size_t align, size_t dim, char fill ); §\C{// aligned array[dim], T size elements, fill bytes with value}§6740 T * alloc_align_set( size_t align, size_t dim, T fill ); §\C{// aligned array[dim], T size elements, fill elements with value}§6741 T * alloc_align_set( size_t align, size_t dim, const T fill[] ); §\C{// aligned array[dim], T size elements, fill elements with array}§6742 T * alloc_align_set( T ptr[], size_t align, size_t dim, char fill ); §\C{// realloc new aligned array[dim], fill new bytes with value}§6743 6744 // §\CFA§safe initialization/copy, i.e., implicit size specification6745 T * memset( T * dest, char fill ); §\indexc{memset}§6746 T * memcpy( T * dest, const T * src ); §\indexc{memcpy}§6747 6748 // §\CFA§safe initialization/copy, i.e., implicit size specification, array types6709 // $\CFA$ safe general allocation, fill, resize, alignment, array 6710 T * alloc( void );$\indexc{alloc}$ $\C[3.5in]{// variable, T size}$ 6711 T * alloc( size_t dim ); $\C{// array[dim], T size elements}$ 6712 T * alloc( T ptr[], size_t dim ); $\C{// realloc array[dim], T size elements}$ 6713 6714 T * alloc_set( char fill );$\indexc{alloc_set}$ $\C{// variable, T size, fill bytes with value}$ 6715 T * alloc_set( T fill ); $\C{// variable, T size, fill with value}$ 6716 T * alloc_set( size_t dim, char fill ); $\C{// array[dim], T size elements, fill bytes with value}$ 6717 T * alloc_set( size_t dim, T fill ); $\C{// array[dim], T size elements, fill elements with value}$ 6718 T * alloc_set( size_t dim, const T fill[] ); $\C{// array[dim], T size elements, fill elements with array}$ 6719 T * alloc_set( T ptr[], size_t dim, char fill ); $\C{// realloc array[dim], T size elements, fill bytes with value}$ 6720 6721 T * alloc_align( size_t align ); $\C{// aligned variable, T size}$ 6722 T * alloc_align( size_t align, size_t dim ); $\C{// aligned array[dim], T size elements}$ 6723 T * alloc_align( T ptr[], size_t align ); $\C{// realloc new aligned array}$ 6724 T * alloc_align( T ptr[], size_t align, size_t dim ); $\C{// realloc new aligned array[dim]}$ 6725 6726 T * alloc_align_set( size_t align, char fill ); $\C{// aligned variable, T size, fill bytes with value}$ 6727 T * alloc_align_set( size_t align, T fill ); $\C{// aligned variable, T size, fill with value}$ 6728 T * alloc_align_set( size_t align, size_t dim, char fill ); $\C{// aligned array[dim], T size elements, fill bytes with value}$ 6729 T * alloc_align_set( size_t align, size_t dim, T fill ); $\C{// aligned array[dim], T size elements, fill elements with value}$ 6730 T * alloc_align_set( size_t align, size_t dim, const T fill[] ); $\C{// aligned array[dim], T size elements, fill elements with array}$ 6731 T * alloc_align_set( T ptr[], size_t align, size_t dim, char fill ); $\C{// realloc new aligned array[dim], fill new bytes with value}$ 6732 6733 // $\CFA$ safe initialization/copy, i.e., implicit size specification 6734 T * memset( T * dest, char fill );$\indexc{memset}$ 6735 T * memcpy( T * dest, const T * src );$\indexc{memcpy}$ 6736 6737 // $\CFA$ safe initialization/copy, i.e., implicit size specification, array types 6749 6738 T * amemset( T dest[], char fill, size_t dim ); 6750 6739 T * amemcpy( T dest[], const T src[], size_t dim ); 6751 6740 } 6752 6741 6753 // §\CFA§allocation/deallocation and constructor/destructor, non-array types6754 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) T * new( Params p ); §\indexc{new}§6755 forall( dtype T | sized(T) | { void ^?{}( T & ); } ) void delete( T * ptr ); §\indexc{delete}§6742 // $\CFA$ allocation/deallocation and constructor/destructor, non-array types 6743 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) T * new( Params p );$\indexc{new}$ 6744 forall( dtype T | sized(T) | { void ^?{}( T & ); } ) void delete( T * ptr );$\indexc{delete}$ 6756 6745 forall( dtype T, ttype Params | sized(T) | { void ^?{}( T & ); void delete( Params ); } ) 6757 6746 void delete( T * ptr, Params rest ); 6758 6747 6759 // §\CFA§allocation/deallocation and constructor/destructor, array types6760 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) T * anew( size_t dim, Params p ); §\indexc{anew}§6761 forall( dtype T | sized(T) | { void ^?{}( T & ); } ) void adelete( size_t dim, T arr[] ); §\indexc{adelete}§6748 // $\CFA$ allocation/deallocation and constructor/destructor, array types 6749 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) T * anew( size_t dim, Params p );$\indexc{anew}$ 6750 forall( dtype T | sized(T) | { void ^?{}( T & ); } ) void adelete( size_t dim, T arr[] );$\indexc{adelete}$ 6762 6751 forall( dtype T | sized(T) | { void ^?{}( T & ); }, ttype Params | { void adelete( Params ); } ) 6763 6752 void adelete( size_t dim, T arr[], Params rest ); … … 6769 6758 \leavevmode 6770 6759 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 6771 int ato( const char * ptr ); §\indexc{ato}§6760 int ato( const char * ptr );$\indexc{ato}$ 6772 6761 unsigned int ato( const char * ptr ); 6773 6762 long int ato( const char * ptr ); … … 6801 6790 \leavevmode 6802 6791 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 6803 forall( otype T | { int ?<?( T, T ); } ) §\C{// location}§6804 T * bsearch( T key, const T * arr, size_t dim ); §\indexc{bsearch}§6805 6806 forall( otype T | { int ?<?( T, T ); } ) §\C{// position}§6792 forall( otype T | { int ?<?( T, T ); } ) $\C{// location}$ 6793 T * bsearch( T key, const T * arr, size_t dim );$\indexc{bsearch}$ 6794 6795 forall( otype T | { int ?<?( T, T ); } ) $\C{// position}$ 6807 6796 unsigned int bsearch( T key, const T * arr, size_t dim ); 6808 6797 6809 6798 forall( otype T | { int ?<?( T, T ); } ) 6810 void qsort( const T * arr, size_t dim ); §\indexc{qsort}§6799 void qsort( const T * arr, size_t dim );$\indexc{qsort}$ 6811 6800 6812 6801 forall( otype E | { int ?<?( E, E ); } ) { 6813 E * bsearch( E key, const E * vals, size_t dim ); §\indexc{bsearch}§ §\C{// location}§6814 size_t bsearch( E key, const E * vals, size_t dim ); §\C{// position}§6815 E * bsearchl( E key, const E * vals, size_t dim ); §\indexc{bsearchl}§6802 E * bsearch( E key, const E * vals, size_t dim );$\indexc{bsearch}$ $\C{// location}$ 6803 size_t bsearch( E key, const E * vals, size_t dim );$\C{// position}$ 6804 E * bsearchl( E key, const E * vals, size_t dim );$\indexc{bsearchl}$ 6816 6805 size_t bsearchl( E key, const E * vals, size_t dim ); 6817 E * bsearchu( E key, const E * vals, size_t dim ); §\indexc{bsearchu}§6806 E * bsearchu( E key, const E * vals, size_t dim );$\indexc{bsearchu}$ 6818 6807 size_t bsearchu( E key, const E * vals, size_t dim ); 6819 6808 } … … 6829 6818 6830 6819 forall( otype E | { int ?<?( E, E ); } ) { 6831 void qsort( E * vals, size_t dim ); §\indexc{qsort}§6820 void qsort( E * vals, size_t dim );$\indexc{qsort}$ 6832 6821 } 6833 6822 \end{cfa} … … 6838 6827 \leavevmode 6839 6828 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 6840 unsigned char abs( signed char ); §\indexc{abs}§6829 unsigned char abs( signed char );$\indexc{abs}$ 6841 6830 int abs( int ); 6842 6831 unsigned long int abs( long int ); … … 6857 6846 \leavevmode 6858 6847 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 6859 void srandom( unsigned int seed ); §\indexc{srandom}§6860 char random( void ); §\indexc{random}§6861 char random( char u ); §\C{// [0,u)}§6862 char random( char l, char u ); §\C{// [l,u)}§6848 void srandom( unsigned int seed );$\indexc{srandom}$ 6849 char random( void );$\indexc{random}$ 6850 char random( char u ); $\C{// [0,u)}$ 6851 char random( char l, char u ); $\C{// [l,u)}$ 6863 6852 int random( void ); 6864 int random( int u ); §\C{// [0,u)}§6865 int random( int l, int u ); §\C{// [l,u)}§6853 int random( int u ); $\C{// [0,u)}$ 6854 int random( int l, int u ); $\C{// [l,u)}$ 6866 6855 unsigned int random( void ); 6867 unsigned int random( unsigned int u ); §\C{// [0,u)}§6868 unsigned int random( unsigned int l, unsigned int u ); §\C{// [l,u)}§6856 unsigned int random( unsigned int u ); $\C{// [0,u)}$ 6857 unsigned int random( unsigned int l, unsigned int u ); $\C{// [l,u)}$ 6869 6858 long int random( void ); 6870 long int random( long int u ); §\C{// [0,u)}§6871 long int random( long int l, long int u ); §\C{// [l,u)}§6859 long int random( long int u ); $\C{// [0,u)}$ 6860 long int random( long int l, long int u ); $\C{// [l,u)}$ 6872 6861 unsigned long int random( void ); 6873 unsigned long int random( unsigned long int u ); §\C{// [0,u)}§6874 unsigned long int random( unsigned long int l, unsigned long int u ); §\C{// [l,u)}§6875 float random( void ); §\C{// [0.0, 1.0)}§6876 double random( void ); §\C{// [0.0, 1.0)}§6877 float _Complex random( void ); §\C{// [0.0, 1.0)+[0.0, 1.0)i}§6878 double _Complex random( void ); §\C{// [0.0, 1.0)+[0.0, 1.0)i}§6879 long double _Complex random( void ); §\C{// [0.0, 1.0)+[0.0, 1.0)i}§6862 unsigned long int random( unsigned long int u ); $\C{// [0,u)}$ 6863 unsigned long int random( unsigned long int l, unsigned long int u ); $\C{// [l,u)}$ 6864 float random( void ); $\C{// [0.0, 1.0)}$ 6865 double random( void ); $\C{// [0.0, 1.0)}$ 6866 float _Complex random( void ); $\C{// [0.0, 1.0)+[0.0, 1.0)i}$ 6867 double _Complex random( void ); $\C{// [0.0, 1.0)+[0.0, 1.0)i}$ 6868 long double _Complex random( void ); $\C{// [0.0, 1.0)+[0.0, 1.0)i}$ 6880 6869 \end{cfa} 6881 6870 … … 6885 6874 \leavevmode 6886 6875 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 6887 forall( otype T | { int ?<?( T, T ); } ) T min( T t1, T t2 ); §\indexc{min}§6888 forall( otype T | { int ?>?( T, T ); } ) T max( T t1, T t2 ); §\indexc{max}§6889 forall( otype T | { T min( T, T ); T max( T, T ); } ) T clamp( T value, T min_val, T max_val ); §\indexc{clamp}§6890 forall( otype T ) void swap( T * t1, T * t2 ); §\indexc{swap}§6876 forall( otype T | { int ?<?( T, T ); } ) T min( T t1, T t2 );$\indexc{min}$ 6877 forall( otype T | { int ?>?( T, T ); } ) T max( T t1, T t2 );$\indexc{max}$ 6878 forall( otype T | { T min( T, T ); T max( T, T ); } ) T clamp( T value, T min_val, T max_val );$\indexc{clamp}$ 6879 forall( otype T ) void swap( T * t1, T * t2 );$\indexc{swap}$ 6891 6880 \end{cfa} 6892 6881 … … 6902 6891 \leavevmode 6903 6892 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 6904 float ?%?( float, float ); §\indexc{fmod}§6893 float ?%?( float, float );$\indexc{fmod}$ 6905 6894 float fmod( float, float ); 6906 6895 double ?%?( double, double ); … … 6909 6898 long double fmod( long double, long double ); 6910 6899 6911 float remainder( float, float ); §\indexc{remainder}§6900 float remainder( float, float );$\indexc{remainder}$ 6912 6901 double remainder( double, double ); 6913 6902 long double remainder( long double, long double ); 6914 6903 6915 float remquo( float, float, int * ); §\indexc{remquo}§6904 float remquo( float, float, int * );$\indexc{remquo}$ 6916 6905 double remquo( double, double, int * ); 6917 6906 long double remquo( long double, long double, int * ); … … 6920 6909 [ int, long double ] remquo( long double, long double ); 6921 6910 6922 float div( float, float, int * ); §\indexc{div}§ §\C{// alternative name for remquo}§6911 float div( float, float, int * );$\indexc{div}$ $\C{// alternative name for remquo}$ 6923 6912 double div( double, double, int * ); 6924 6913 long double div( long double, long double, int * ); … … 6927 6916 [ int, long double ] div( long double, long double ); 6928 6917 6929 float fma( float, float, float ); §\indexc{fma}§6918 float fma( float, float, float );$\indexc{fma}$ 6930 6919 double fma( double, double, double ); 6931 6920 long double fma( long double, long double, long double ); 6932 6921 6933 float fdim( float, float ); §\indexc{fdim}§6922 float fdim( float, float );$\indexc{fdim}$ 6934 6923 double fdim( double, double ); 6935 6924 long double fdim( long double, long double ); 6936 6925 6937 float nan( const char * ); §\indexc{nan}§6926 float nan( const char * );$\indexc{nan}$ 6938 6927 double nan( const char * ); 6939 6928 long double nan( const char * ); … … 6945 6934 \leavevmode 6946 6935 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 6947 float exp( float ); §\indexc{exp}§6936 float exp( float );$\indexc{exp}$ 6948 6937 double exp( double ); 6949 6938 long double exp( long double ); … … 6952 6941 long double _Complex exp( long double _Complex ); 6953 6942 6954 float exp2( float ); §\indexc{exp2}§6943 float exp2( float );$\indexc{exp2}$ 6955 6944 double exp2( double ); 6956 6945 long double exp2( long double ); … … 6959 6948 // long double _Complex exp2( long double _Complex ); 6960 6949 6961 float expm1( float ); §\indexc{expm1}§6950 float expm1( float );$\indexc{expm1}$ 6962 6951 double expm1( double ); 6963 6952 long double expm1( long double ); 6964 6953 6965 float pow( float, float ); §\indexc{pow}§6954 float pow( float, float );$\indexc{pow}$ 6966 6955 double pow( double, double ); 6967 6956 long double pow( long double, long double ); … … 6976 6965 \leavevmode 6977 6966 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 6978 float log( float ); §\indexc{log}§6967 float log( float );$\indexc{log}$ 6979 6968 double log( double ); 6980 6969 long double log( long double ); … … 6983 6972 long double _Complex log( long double _Complex ); 6984 6973 6985 float log2( float ); §\indexc{log2}§6974 float log2( float );$\indexc{log2}$ 6986 6975 double log2( double ); 6987 6976 long double log2( long double ); … … 6990 6979 // long double _Complex log2( long double _Complex ); 6991 6980 6992 float log10( float ); §\indexc{log10}§6981 float log10( float );$\indexc{log10}$ 6993 6982 double log10( double ); 6994 6983 long double log10( long double ); … … 6997 6986 // long double _Complex log10( long double _Complex ); 6998 6987 6999 float log1p( float ); §\indexc{log1p}§6988 float log1p( float );$\indexc{log1p}$ 7000 6989 double log1p( double ); 7001 6990 long double log1p( long double ); 7002 6991 7003 int ilogb( float ); §\indexc{ilogb}§6992 int ilogb( float );$\indexc{ilogb}$ 7004 6993 int ilogb( double ); 7005 6994 int ilogb( long double ); 7006 6995 7007 float logb( float ); §\indexc{logb}§6996 float logb( float );$\indexc{logb}$ 7008 6997 double logb( double ); 7009 6998 long double logb( long double ); 7010 6999 7011 float sqrt( float ); §\indexc{sqrt}§7000 float sqrt( float );$\indexc{sqrt}$ 7012 7001 double sqrt( double ); 7013 7002 long double sqrt( long double ); … … 7016 7005 long double _Complex sqrt( long double _Complex ); 7017 7006 7018 float cbrt( float ); §\indexc{cbrt}§7007 float cbrt( float );$\indexc{cbrt}$ 7019 7008 double cbrt( double ); 7020 7009 long double cbrt( long double ); 7021 7010 7022 float hypot( float, float ); §\indexc{hypot}§7011 float hypot( float, float );$\indexc{hypot}$ 7023 7012 double hypot( double, double ); 7024 7013 long double hypot( long double, long double ); … … 7030 7019 \leavevmode 7031 7020 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 7032 float sin( float ); §\indexc{sin}§7021 float sin( float );$\indexc{sin}$ 7033 7022 double sin( double ); 7034 7023 long double sin( long double ); … … 7037 7026 long double _Complex sin( long double _Complex ); 7038 7027 7039 float cos( float ); §\indexc{cos}§7028 float cos( float );$\indexc{cos}$ 7040 7029 double cos( double ); 7041 7030 long double cos( long double ); … … 7044 7033 long double _Complex cos( long double _Complex ); 7045 7034 7046 float tan( float ); §\indexc{tan}§7035 float tan( float );$\indexc{tan}$ 7047 7036 double tan( double ); 7048 7037 long double tan( long double ); … … 7051 7040 long double _Complex tan( long double _Complex ); 7052 7041 7053 float asin( float ); §\indexc{asin}§7042 float asin( float );$\indexc{asin}$ 7054 7043 double asin( double ); 7055 7044 long double asin( long double ); … … 7058 7047 long double _Complex asin( long double _Complex ); 7059 7048 7060 float acos( float ); §\indexc{acos}§7049 float acos( float );$\indexc{acos}$ 7061 7050 double acos( double ); 7062 7051 long double acos( long double ); … … 7065 7054 long double _Complex acos( long double _Complex ); 7066 7055 7067 float atan( float ); §\indexc{atan}§7056 float atan( float );$\indexc{atan}$ 7068 7057 double atan( double ); 7069 7058 long double atan( long double ); … … 7072 7061 long double _Complex atan( long double _Complex ); 7073 7062 7074 float atan2( float, float ); §\indexc{atan2}§7063 float atan2( float, float );$\indexc{atan2}$ 7075 7064 double atan2( double, double ); 7076 7065 long double atan2( long double, long double ); 7077 7066 7078 float atan( float, float ); §\C{// alternative name for atan2}§7079 double atan( double, double ); §\indexc{atan}§7067 float atan( float, float ); $\C{// alternative name for atan2}$ 7068 double atan( double, double );$\indexc{atan}$ 7080 7069 long double atan( long double, long double ); 7081 7070 \end{cfa} … … 7086 7075 \leavevmode 7087 7076 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 7088 float sinh( float ); §\indexc{sinh}§7077 float sinh( float );$\indexc{sinh}$ 7089 7078 double sinh( double ); 7090 7079 long double sinh( long double ); … … 7093 7082 long double _Complex sinh( long double _Complex ); 7094 7083 7095 float cosh( float ); §\indexc{cosh}§7084 float cosh( float );$\indexc{cosh}$ 7096 7085 double cosh( double ); 7097 7086 long double cosh( long double ); … … 7100 7089 long double _Complex cosh( long double _Complex ); 7101 7090 7102 float tanh( float ); §\indexc{tanh}§7091 float tanh( float );$\indexc{tanh}$ 7103 7092 double tanh( double ); 7104 7093 long double tanh( long double ); … … 7107 7096 long double _Complex tanh( long double _Complex ); 7108 7097 7109 float asinh( float ); §\indexc{asinh}§7098 float asinh( float );$\indexc{asinh}$ 7110 7099 double asinh( double ); 7111 7100 long double asinh( long double ); … … 7114 7103 long double _Complex asinh( long double _Complex ); 7115 7104 7116 float acosh( float ); §\indexc{acosh}§7105 float acosh( float );$\indexc{acosh}$ 7117 7106 double acosh( double ); 7118 7107 long double acosh( long double ); … … 7121 7110 long double _Complex acosh( long double _Complex ); 7122 7111 7123 float atanh( float ); §\indexc{atanh}§7112 float atanh( float );$\indexc{atanh}$ 7124 7113 double atanh( double ); 7125 7114 long double atanh( long double ); … … 7134 7123 \leavevmode 7135 7124 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 7136 float erf( float ); §\indexc{erf}§7125 float erf( float );$\indexc{erf}$ 7137 7126 double erf( double ); 7138 7127 long double erf( long double ); … … 7141 7130 long double _Complex erf( long double _Complex ); 7142 7131 7143 float erfc( float ); §\indexc{erfc}§7132 float erfc( float );$\indexc{erfc}$ 7144 7133 double erfc( double ); 7145 7134 long double erfc( long double ); … … 7148 7137 long double _Complex erfc( long double _Complex ); 7149 7138 7150 float lgamma( float ); §\indexc{lgamma}§7139 float lgamma( float );$\indexc{lgamma}$ 7151 7140 double lgamma( double ); 7152 7141 long double lgamma( long double ); … … 7155 7144 long double lgamma( long double, int * ); 7156 7145 7157 float tgamma( float ); §\indexc{tgamma}§7146 float tgamma( float );$\indexc{tgamma}$ 7158 7147 double tgamma( double ); 7159 7148 long double tgamma( long double ); … … 7165 7154 \leavevmode 7166 7155 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 7167 float floor( float ); §\indexc{floor}§7156 float floor( float );$\indexc{floor}$ 7168 7157 double floor( double ); 7169 7158 long double floor( long double ); 7170 7159 7171 float ceil( float ); §\indexc{ceil}§7160 float ceil( float );$\indexc{ceil}$ 7172 7161 double ceil( double ); 7173 7162 long double ceil( long double ); 7174 7163 7175 float trunc( float ); §\indexc{trunc}§7164 float trunc( float );$\indexc{trunc}$ 7176 7165 double trunc( double ); 7177 7166 long double trunc( long double ); 7178 7167 7179 float rint( float ); §\indexc{rint}§7168 float rint( float );$\indexc{rint}$ 7180 7169 long double rint( long double ); 7181 7170 long int rint( float ); … … 7186 7175 long long int rint( long double ); 7187 7176 7188 long int lrint( float ); §\indexc{lrint}§7177 long int lrint( float );$\indexc{lrint}$ 7189 7178 long int lrint( double ); 7190 7179 long int lrint( long double ); … … 7193 7182 long long int llrint( long double ); 7194 7183 7195 float nearbyint( float ); §\indexc{nearbyint}§7184 float nearbyint( float );$\indexc{nearbyint}$ 7196 7185 double nearbyint( double ); 7197 7186 long double nearbyint( long double ); 7198 7187 7199 float round( float ); §\indexc{round}§7188 float round( float );$\indexc{round}$ 7200 7189 long double round( long double ); 7201 7190 long int round( float ); … … 7206 7195 long long int round( long double ); 7207 7196 7208 long int lround( float ); §\indexc{lround}§7197 long int lround( float );$\indexc{lround}$ 7209 7198 long int lround( double ); 7210 7199 long int lround( long double ); … … 7219 7208 \leavevmode 7220 7209 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 7221 float copysign( float, float ); §\indexc{copysign}§7210 float copysign( float, float );$\indexc{copysign}$ 7222 7211 double copysign( double, double ); 7223 7212 long double copysign( long double, long double ); 7224 7213 7225 float frexp( float, int * ); §\indexc{frexp}§7214 float frexp( float, int * );$\indexc{frexp}$ 7226 7215 double frexp( double, int * ); 7227 7216 long double frexp( long double, int * ); 7228 7217 7229 float ldexp( float, int ); §\indexc{ldexp}§7218 float ldexp( float, int );$\indexc{ldexp}$ 7230 7219 double ldexp( double, int ); 7231 7220 long double ldexp( long double, int ); 7232 7221 7233 [ float, float ] modf( float ); §\indexc{modf}§7222 [ float, float ] modf( float );$\indexc{modf}$ 7234 7223 float modf( float, float * ); 7235 7224 [ double, double ] modf( double ); … … 7238 7227 long double modf( long double, long double * ); 7239 7228 7240 float nextafter( float, float ); §\indexc{nextafter}§7229 float nextafter( float, float );$\indexc{nextafter}$ 7241 7230 double nextafter( double, double ); 7242 7231 long double nextafter( long double, long double ); 7243 7232 7244 float nexttoward( float, long double ); §\indexc{nexttoward}§7233 float nexttoward( float, long double );$\indexc{nexttoward}$ 7245 7234 double nexttoward( double, long double ); 7246 7235 long double nexttoward( long double, long double ); 7247 7236 7248 float scalbn( float, int ); §\indexc{scalbn}§7237 float scalbn( float, int );$\indexc{scalbn}$ 7249 7238 double scalbn( double, int ); 7250 7239 long double scalbn( long double, int ); 7251 7240 7252 float scalbln( float, long int ); §\indexc{scalbln}§7241 float scalbln( float, long int );$\indexc{scalbln}$ 7253 7242 double scalbln( double, long int ); 7254 7243 long double scalbln( long double, long int ); … … 7267 7256 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 7268 7257 struct Duration { 7269 int64_t tv; §\C{// nanoseconds}§7258 int64_t tv; $\C{// nanoseconds}$ 7270 7259 }; 7271 7260 … … 7397 7386 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 7398 7387 struct Time { 7399 uint64_t tv; §\C{// nanoseconds since UNIX epoch}§7388 uint64_t tv; $\C{// nanoseconds since UNIX epoch}$ 7400 7389 }; 7401 7390 … … 7468 7457 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 7469 7458 struct Clock { 7470 Duration offset; §\C{// for virtual clock: contains offset from real-time}§7471 int clocktype; §\C{// implementation only -1 (virtual), CLOCK\_REALTIME}§7459 Duration offset; $\C{// for virtual clock: contains offset from real-time}$ 7460 int clocktype; $\C{// implementation only -1 (virtual), CLOCK\_REALTIME}$ 7472 7461 }; 7473 7462 … … 7477 7466 void ?{}( Clock & clk, Duration adj ); 7478 7467 7479 Duration getResNsec(); §\C{// with nanoseconds}§7480 Duration getRes(); §\C{// without nanoseconds}§7481 7482 Time getTimeNsec(); §\C{// with nanoseconds}§7483 Time getTime(); §\C{// without nanoseconds}§7468 Duration getResNsec(); $\C{// with nanoseconds}$ 7469 Duration getRes(); $\C{// without nanoseconds}$ 7470 7471 Time getTimeNsec(); $\C{// with nanoseconds}$ 7472 Time getTime(); $\C{// without nanoseconds}$ 7484 7473 Time getTime( Clock & clk ); 7485 7474 Time ?()( Clock & clk ); … … 7497 7486 7498 7487 \begin{cfa} 7499 void ?{}( Int * this ); §\C{// constructor/destructor}§7488 void ?{}( Int * this ); $\C{// constructor/destructor}$ 7500 7489 void ?{}( Int * this, Int init ); 7501 7490 void ?{}( Int * this, zero_t ); … … 7506 7495 void ^?{}( Int * this ); 7507 7496 7508 Int ?=?( Int * lhs, Int rhs ); §\C{// assignment}§7497 Int ?=?( Int * lhs, Int rhs ); $\C{// assignment}$ 7509 7498 Int ?=?( Int * lhs, long int rhs ); 7510 7499 Int ?=?( Int * lhs, unsigned long int rhs ); … … 7523 7512 unsigned long int narrow( Int val ); 7524 7513 7525 int ?==?( Int oper1, Int oper2 ); §\C{// comparison}§7514 int ?==?( Int oper1, Int oper2 ); $\C{// comparison}$ 7526 7515 int ?==?( Int oper1, long int oper2 ); 7527 7516 int ?==?( long int oper2, Int oper1 ); … … 7559 7548 int ?>=?( unsigned long int oper1, Int oper2 ); 7560 7549 7561 Int +?( Int oper ); §\C{// arithmetic}§7550 Int +?( Int oper ); $\C{// arithmetic}$ 7562 7551 Int -?( Int oper ); 7563 7552 Int ~?( Int oper ); … … 7641 7630 Int ?>>=?( Int * lhs, mp_bitcnt_t shift ); 7642 7631 7643 Int abs( Int oper ); §\C{// number functions}§7632 Int abs( Int oper ); $\C{// number functions}$ 7644 7633 Int fact( unsigned long int N ); 7645 7634 Int gcd( Int oper1, Int oper2 ); … … 7653 7642 Int sqrt( Int oper ); 7654 7643 7655 forall( dtype istype | istream( istype ) ) istype * ?|?( istype * is, Int * mp ); §\C{// I/O}§7644 forall( dtype istype | istream( istype ) ) istype * ?|?( istype * is, Int * mp ); $\C{// I/O}$ 7656 7645 forall( dtype ostype | ostream( ostype ) ) ostype * ?|?( ostype * os, Int mp ); 7657 7646 \end{cfa} … … 7664 7653 \hline 7665 7654 \begin{cfa} 7666 #include <gmp> §\indexc{gmp}§7655 #include <gmp>$\indexc{gmp}$ 7667 7656 int main( void ) { 7668 7657 sout | "Factorial Numbers"; … … 7678 7667 & 7679 7668 \begin{cfa} 7680 #include <gmp.h> §\indexc{gmp.h}§7669 #include <gmp.h>$\indexc{gmp.h}$ 7681 7670 int main( void ) { 7682 ®gmp_printf®( "Factorial Numbers\n" );7683 ®mpz_t®fact;7684 ®mpz_init_set_ui®( fact, 1 );7685 ®gmp_printf®( "%d %Zd\n", 0, fact );7671 @gmp_printf@( "Factorial Numbers\n" ); 7672 @mpz_t@ fact; 7673 @mpz_init_set_ui@( fact, 1 ); 7674 @gmp_printf@( "%d %Zd\n", 0, fact ); 7686 7675 for ( unsigned int i = 1; i <= 40; i += 1 ) { 7687 ®mpz_mul_ui®( fact, fact, i );7688 ®gmp_printf®( "%d %Zd\n", i, fact );7676 @mpz_mul_ui@( fact, fact, i ); 7677 @gmp_printf@( "%d %Zd\n", i, fact ); 7689 7678 } 7690 7679 } … … 7751 7740 \begin{cfa}[belowskip=0pt] 7752 7741 // implementation 7753 struct Rational { §\indexc{Rational}§7754 long int numerator, denominator; §\C{// invariant: denominator > 0}§7742 struct Rational {$\indexc{Rational}$ 7743 long int numerator, denominator; $\C{// invariant: denominator > 0}$ 7755 7744 }; // Rational 7756 7745 7757 Rational rational(); §\C{// constructors}§7746 Rational rational(); $\C{// constructors}$ 7758 7747 Rational rational( long int n ); 7759 7748 Rational rational( long int n, long int d ); … … 7761 7750 void ?{}( Rational * r, one_t ); 7762 7751 7763 long int numerator( Rational r ); §\C{// numerator/denominator getter/setter}§7752 long int numerator( Rational r ); $\C{// numerator/denominator getter/setter}$ 7764 7753 long int numerator( Rational r, long int n ); 7765 7754 long int denominator( Rational r ); 7766 7755 long int denominator( Rational r, long int d ); 7767 7756 7768 int ?==?( Rational l, Rational r ); §\C{// comparison}§7757 int ?==?( Rational l, Rational r ); $\C{// comparison}$ 7769 7758 int ?!=?( Rational l, Rational r ); 7770 7759 int ?<?( Rational l, Rational r ); … … 7773 7762 int ?>=?( Rational l, Rational r ); 7774 7763 7775 Rational -?( Rational r ); §\C{// arithmetic}§7764 Rational -?( Rational r ); $\C{// arithmetic}$ 7776 7765 Rational ?+?( Rational l, Rational r ); 7777 7766 Rational ?-?( Rational l, Rational r ); … … 7779 7768 Rational ?/?( Rational l, Rational r ); 7780 7769 7781 double widen( Rational r ); §\C{// conversion}§7770 double widen( Rational r ); $\C{// conversion}$ 7782 7771 Rational narrow( double f, long int md ); 7783 7772 -
driver/cfa.cc
r342af53 r8e4aa05 10 10 // Created On : Tue Aug 20 13:44:49 2002 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Nov 17 14:27:28 202013 // Update Count : 44 012 // Last Modified On : Sat Jan 16 07:30:19 2021 13 // Update Count : 442 14 14 // 15 15 … … 499 499 args[nargs++] = "-no-integrated-cpp"; 500 500 args[nargs++] = "-Wno-deprecated"; 501 args[nargs++] = "-Wno-strict-aliasing"; // casting from one type to another 501 502 #ifdef HAVE_CAST_FUNCTION_TYPE 502 503 args[nargs++] = "-Wno-cast-function-type"; -
libcfa/configure.ac
r342af53 r8e4aa05 169 169 AH_TEMPLATE([CFA_HAVE_IOSQE_FIXED_FILE],[Defined if io_uring support is present when compiling libcfathread and supports the flag FIXED_FILE.]) 170 170 AH_TEMPLATE([CFA_HAVE_IOSQE_IO_DRAIN],[Defined if io_uring support is present when compiling libcfathread and supports the flag IO_DRAIN.]) 171 AH_TEMPLATE([CFA_HAVE_IOSQE_ASYNC],[Defined if io_uring support is present when compiling libcfathread and supports the flag ASYNC.])172 171 AH_TEMPLATE([CFA_HAVE_IOSQE_IO_LINK],[Defined if io_uring support is present when compiling libcfathread and supports the flag IO_LINK.]) 173 172 AH_TEMPLATE([CFA_HAVE_IOSQE_IO_HARDLINK],[Defined if io_uring support is present when compiling libcfathread and supports the flag IO_HARDLINK.]) 173 AH_TEMPLATE([CFA_HAVE_IOSQE_ASYNC],[Defined if io_uring support is present when compiling libcfathread and supports the flag ASYNC.]) 174 AH_TEMPLATE([CFA_HAVE_IOSQE_BUFFER_SELECT],[Defined if io_uring support is present when compiling libcfathread and supports the flag BUFFER_SELEC.]) 174 175 AH_TEMPLATE([CFA_HAVE_SPLICE_F_FD_IN_FIXED],[Defined if io_uring support is present when compiling libcfathread and supports the flag SPLICE_F_FD_IN_FIXED.]) 175 176 AH_TEMPLATE([CFA_HAVE_IORING_SETUP_ATTACH_WQ],[Defined if io_uring support is present when compiling libcfathread and supports the flag IORING_SETUP_ATTACH_WQ.]) … … 182 183 183 184 define(ioring_ops, [IORING_OP_NOP,IORING_OP_READV,IORING_OP_WRITEV,IORING_OP_FSYNC,IORING_OP_READ_FIXED,IORING_OP_WRITE_FIXED,IORING_OP_POLL_ADD,IORING_OP_POLL_REMOVE,IORING_OP_SYNC_FILE_RANGE,IORING_OP_SENDMSG,IORING_OP_RECVMSG,IORING_OP_TIMEOUT,IORING_OP_TIMEOUT_REMOVE,IORING_OP_ACCEPT,IORING_OP_ASYNC_CANCEL,IORING_OP_LINK_TIMEOUT,IORING_OP_CONNECT,IORING_OP_FALLOCATE,IORING_OP_OPENAT,IORING_OP_CLOSE,IORING_OP_FILES_UPDATE,IORING_OP_STATX,IORING_OP_READ,IORING_OP_WRITE,IORING_OP_FADVISE,IORING_OP_MADVISE,IORING_OP_SEND,IORING_OP_RECV,IORING_OP_OPENAT2,IORING_OP_EPOLL_CTL,IORING_OP_SPLICE,IORING_OP_PROVIDE_BUFFERS,IORING_OP_REMOVE_BUFFER,IORING_OP_TEE]) 184 define(ioring_flags, [IOSQE_FIXED_FILE,IOSQE_IO_DRAIN,IOSQE_ ASYNC,IOSQE_IO_LINK,IOSQE_IO_HARDLINK,SPLICE_F_FD_IN_FIXED,IORING_SETUP_ATTACH_WQ])185 define(ioring_flags, [IOSQE_FIXED_FILE,IOSQE_IO_DRAIN,IOSQE_IO_LINK,IOSQE_IO_HARDLINK,IOSQE_ASYNC,IOSQE_BUFFER_SELECT,SPLICE_F_FD_IN_FIXED,IORING_SETUP_ATTACH_WQ]) 185 186 186 187 define(ioring_from_decls, [ -
libcfa/prelude/builtins.c
r342af53 r8e4aa05 18 18 // type that wraps a pointer and a destructor-like function - used in generating implicit destructor calls for struct members in user-defined functions 19 19 // Note: needs to occur early, because it is used to generate destructor calls during code generation 20 forall( dtype T)20 forall(T &) 21 21 struct __Destructor { 22 22 T * object; … … 25 25 26 26 // defined destructor in the case that non-generated code wants to use __Destructor 27 forall( dtype T)27 forall(T &) 28 28 static inline void ^?{}(__Destructor(T) & x) { 29 29 if (x.object && x.dtor) { … … 34 34 // easy interface into __Destructor's destructor for easy codegen purposes 35 35 extern "C" { 36 forall( dtype T)36 forall(T &) 37 37 static inline void __destroy_Destructor(__Destructor(T) * dtor) { 38 38 ^(*dtor){}; … … 51 51 void abort( const char fmt[], ... ) __attribute__ (( format(printf, 1, 2), __nothrow__, __leaf__, __noreturn__ )); 52 52 53 forall( dtype T)53 forall(T &) 54 54 static inline T & identity(T & i) { 55 55 return i; … … 64 64 static inline void ^?{}($generator &) {} 65 65 66 trait is_generator( dtype T) {66 trait is_generator(T &) { 67 67 void main(T & this); 68 68 $generator * get_generator(T & this); 69 69 }; 70 70 71 forall( dtype T| is_generator(T))71 forall(T & | is_generator(T)) 72 72 static inline T & resume(T & gen) { 73 73 main(gen); … … 78 78 79 79 static inline { 80 forall( dtype DT| { DT & ?+=?( DT &, one_t ); } )80 forall( DT & | { DT & ?+=?( DT &, one_t ); } ) 81 81 DT & ++?( DT & x ) { return x += 1; } 82 82 83 forall( dtype DT| sized(DT) | { void ?{}( DT &, DT ); void ^?{}( DT & ); DT & ?+=?( DT &, one_t ); } )83 forall( DT & | sized(DT) | { void ?{}( DT &, DT ); void ^?{}( DT & ); DT & ?+=?( DT &, one_t ); } ) 84 84 DT & ?++( DT & x ) { DT tmp = x; x += 1; return tmp; } 85 85 86 forall( dtype DT| { DT & ?-=?( DT &, one_t ); } )86 forall( DT & | { DT & ?-=?( DT &, one_t ); } ) 87 87 DT & --?( DT & x ) { return x -= 1; } 88 88 89 forall( dtype DT| sized(DT) | { void ?{}( DT &, DT ); void ^?{}( DT & ); DT & ?-=?( DT &, one_t ); } )89 forall( DT & | sized(DT) | { void ?{}( DT &, DT ); void ^?{}( DT & ); DT & ?-=?( DT &, one_t ); } ) 90 90 DT & ?--( DT & x ) { DT tmp = x; x -= 1; return tmp; } 91 91 92 forall( dtype DT| { int ?!=?( const DT &, zero_t ); } )92 forall( DT & | { int ?!=?( const DT &, zero_t ); } ) 93 93 int !?( const DT & x ) { return !( x != 0 ); } 94 94 } // distribution 95 95 96 96 // universal typed pointer constant 97 static inline forall( dtype DT) DT * intptr( uintptr_t addr ) { return (DT *)addr; }97 static inline forall( DT & ) DT * intptr( uintptr_t addr ) { return (DT *)addr; } 98 98 static inline forall( ftype FT ) FT * intptr( uintptr_t addr ) { return (FT *)addr; } 99 99 … … 156 156 #define __CFA_EXP_OVERFLOW__() 157 157 158 static inline forall( otypeOT | { void ?{}( OT & this, one_t ); OT ?*?( OT, OT ); } ) {158 static inline forall( OT | { void ?{}( OT & this, one_t ); OT ?*?( OT, OT ); } ) { 159 159 OT ?\?( OT ep, unsigned int y ) { __CFA_EXP__(); } 160 160 OT ?\?( OT ep, unsigned long int y ) { __CFA_EXP__(); } -
libcfa/prelude/defines.hfa.in
r342af53 r8e4aa05 149 149 150 150 /* Defined if io_uring support is present when compiling libcfathread and 151 supports the flag BUFFER_SELEC. */ 152 #undef CFA_HAVE_IOSQE_BUFFER_SELECT 153 154 /* Defined if io_uring support is present when compiling libcfathread and 151 155 supports the flag FIXED_FILE. */ 152 156 #undef CFA_HAVE_IOSQE_FIXED_FILE -
libcfa/prelude/prelude-gen.cc
r342af53 r8e4aa05 159 159 int main() { 160 160 cout << "# 2 \"prelude.cfa\" // needed for error messages from this file" << endl; 161 cout << "trait sized( dtype T) {};" << endl;161 cout << "trait sized(T &) {};" << endl; 162 162 163 163 cout << "//////////////////////////" << endl; … … 264 264 for (auto cvq : qualifiersPair) { 265 265 for (auto is_vol : { " ", "volatile" }) { 266 cout << "forall( dtype DT) void ?{}(" << cvq.first << type << " * " << is_vol << " &, " << cvq.second << "DT *);" << endl;266 cout << "forall(DT &) void ?{}(" << cvq.first << type << " * " << is_vol << " &, " << cvq.second << "DT *);" << endl; 267 267 } 268 268 } … … 279 279 for (auto cvq : qualifiersSingle) { 280 280 for (auto is_vol : { " ", "volatile" }) { 281 cout << "forall( dtype DT) void ?{}(" << cvq << " DT" << " * " << is_vol << " &);" << endl;281 cout << "forall(DT &) void ?{}(" << cvq << " DT" << " * " << is_vol << " &);" << endl; 282 282 } 283 283 for (auto is_vol : { " ", "volatile" }) { 284 cout << "forall( dtype DT) void ^?{}(" << cvq << " DT" << " * " << is_vol << " &);" << endl;284 cout << "forall(DT &) void ^?{}(" << cvq << " DT" << " * " << is_vol << " &);" << endl; 285 285 } 286 286 } … … 290 290 for (auto is_vol : { " ", "volatile" }) { 291 291 for (auto cvq : qualifiersSingle) { 292 cout << "forall( dtype DT) void ?{}( " << cvq << type << " * " << is_vol << " &, zero_t);" << endl;292 cout << "forall(DT &) void ?{}( " << cvq << type << " * " << is_vol << " &, zero_t);" << endl; 293 293 } 294 294 } … … 317 317 for (auto op : pointerOperators) { 318 318 auto forall = [&op]() { 319 cout << "forall( dtype DT" << op.sized << ") ";319 cout << "forall(DT &" << op.sized << ") "; 320 320 }; 321 321 for (auto type : { "DT"/*, "void"*/ } ) { … … 408 408 for (auto is_vol : { " ", "volatile" }) { 409 409 for (auto cvq : qualifiersPair) { 410 cout << "forall( dtype DT) " << cvq.first << "void * ?=?( " << cvq.first << "void * " << is_vol << " &, " << cvq.second << "DT *);" << endl;410 cout << "forall(DT &) " << cvq.first << "void * ?=?( " << cvq.first << "void * " << is_vol << " &, " << cvq.second << "DT *);" << endl; 411 411 } 412 412 for (auto cvq : qualifiersSingle) { 413 cout << "forall( dtype DT) " << cvq << " DT * ?=?( " << cvq << " DT * " << is_vol << " &, zero_t);" << endl;413 cout << "forall(DT &) " << cvq << " DT * ?=?( " << cvq << " DT * " << is_vol << " &, zero_t);" << endl; 414 414 } 415 415 } -
libcfa/prelude/prelude.old.cf
r342af53 r8e4aa05 23 23 // ------------------------------------------------------------ 24 24 25 trait sized( dtype T) {};25 trait sized(T &) {}; 26 26 27 27 // ------------------------------------------------------------ … … 68 68 long double _Complex ?--( long double _Complex & ), ?--( volatile long double _Complex & ); 69 69 70 forall( dtype T| sized(T) ) T * ?++( T *& );71 forall( dtype T| sized(T) ) const T * ?++( const T *& );72 forall( dtype T| sized(T) ) volatile T * ?++( volatile T *& );73 forall( dtype T| sized(T) ) const volatile T * ?++( const volatile T *& );74 forall( dtype T| sized(T) ) T * ?--( T *& );75 forall( dtype T| sized(T) ) const T * ?--( const T *& );76 forall( dtype T| sized(T) ) volatile T * ?--( volatile T *& );77 forall( dtype T| sized(T) ) const volatile T * ?--( const volatile T *& );78 79 forall( dtype T| sized(T) ) T & ?[?]( T *, ptrdiff_t );80 forall( dtype T| sized(T) ) const T & ?[?]( const T *, ptrdiff_t );81 forall( dtype T| sized(T) ) volatile T & ?[?]( volatile T *, ptrdiff_t );82 forall( dtype T| sized(T) ) const volatile T & ?[?]( const volatile T *, ptrdiff_t );83 forall( dtype T| sized(T) ) T & ?[?]( ptrdiff_t, T * );84 forall( dtype T| sized(T) ) const T & ?[?]( ptrdiff_t, const T * );85 forall( dtype T| sized(T) ) volatile T & ?[?]( ptrdiff_t, volatile T * );86 forall( dtype T| sized(T) ) const volatile T & ?[?]( ptrdiff_t, const volatile T * );70 forall( T & | sized(T) ) T * ?++( T *& ); 71 forall( T & | sized(T) ) const T * ?++( const T *& ); 72 forall( T & | sized(T) ) volatile T * ?++( volatile T *& ); 73 forall( T & | sized(T) ) const volatile T * ?++( const volatile T *& ); 74 forall( T & | sized(T) ) T * ?--( T *& ); 75 forall( T & | sized(T) ) const T * ?--( const T *& ); 76 forall( T & | sized(T) ) volatile T * ?--( volatile T *& ); 77 forall( T & | sized(T) ) const volatile T * ?--( const volatile T *& ); 78 79 forall( T & | sized(T) ) T & ?[?]( T *, ptrdiff_t ); 80 forall( T & | sized(T) ) const T & ?[?]( const T *, ptrdiff_t ); 81 forall( T & | sized(T) ) volatile T & ?[?]( volatile T *, ptrdiff_t ); 82 forall( T & | sized(T) ) const volatile T & ?[?]( const volatile T *, ptrdiff_t ); 83 forall( T & | sized(T) ) T & ?[?]( ptrdiff_t, T * ); 84 forall( T & | sized(T) ) const T & ?[?]( ptrdiff_t, const T * ); 85 forall( T & | sized(T) ) volatile T & ?[?]( ptrdiff_t, volatile T * ); 86 forall( T & | sized(T) ) const volatile T & ?[?]( ptrdiff_t, const volatile T * ); 87 87 88 88 // ------------------------------------------------------------ … … 107 107 long double _Complex ++?( long double _Complex & ), --?( long double _Complex & ); 108 108 109 forall( dtype T| sized(T) ) T * ++?( T *& );110 forall( dtype T| sized(T) ) const T * ++?( const T *& );111 forall( dtype T| sized(T) ) volatile T * ++?( volatile T *& );112 forall( dtype T| sized(T) ) const volatile T * ++?( const volatile T *& );113 forall( dtype T| sized(T) ) T * --?( T *& );114 forall( dtype T| sized(T) ) const T * --?( const T *& );115 forall( dtype T| sized(T) ) volatile T * --?( volatile T *& );116 forall( dtype T| sized(T) ) const volatile T * --?( const volatile T *& );117 118 forall( dtype T| sized(T) ) T & *?( T * );119 forall( dtype T| sized(T) ) const T & *?( const T * );120 forall( dtype T| sized(T) ) volatile T & *?( volatile T * );121 forall( dtype T| sized(T) ) const volatile T & *?( const volatile T * );109 forall( T & | sized(T) ) T * ++?( T *& ); 110 forall( T & | sized(T) ) const T * ++?( const T *& ); 111 forall( T & | sized(T) ) volatile T * ++?( volatile T *& ); 112 forall( T & | sized(T) ) const volatile T * ++?( const volatile T *& ); 113 forall( T & | sized(T) ) T * --?( T *& ); 114 forall( T & | sized(T) ) const T * --?( const T *& ); 115 forall( T & | sized(T) ) volatile T * --?( volatile T *& ); 116 forall( T & | sized(T) ) const volatile T * --?( const volatile T *& ); 117 118 forall( T & | sized(T) ) T & *?( T * ); 119 forall( T & | sized(T) ) const T & *?( const T * ); 120 forall( T & | sized(T) ) volatile T & *?( volatile T * ); 121 forall( T & | sized(T) ) const volatile T & *?( const volatile T * ); 122 122 forall( ftype FT ) FT & *?( FT * ); 123 123 … … 142 142 !?( float _Complex ), !?( double _Complex ), !?( long double _Complex ); 143 143 144 forall( dtype DT) int !?( DT * );145 forall( dtype DT) int !?( const DT * );146 forall( dtype DT) int !?( volatile DT * );147 forall( dtype DT) int !?( const volatile DT * );144 forall( DT & ) int !?( DT * ); 145 forall( DT & ) int !?( const DT * ); 146 forall( DT & ) int !?( volatile DT * ); 147 forall( DT & ) int !?( const volatile DT * ); 148 148 forall( ftype FT ) int !?( FT * ); 149 149 … … 191 191 long double _Complex ?+?( long double _Complex, long double _Complex ), ?-?( long double _Complex, long double _Complex ); 192 192 193 forall( dtype T| sized(T) ) T * ?+?( T *, ptrdiff_t );194 forall( dtype T| sized(T) ) T * ?+?( ptrdiff_t, T * );195 forall( dtype T| sized(T) ) const T * ?+?( const T *, ptrdiff_t );196 forall( dtype T| sized(T) ) const T * ?+?( ptrdiff_t, const T * );197 forall( dtype T| sized(T) ) volatile T * ?+?( volatile T *, ptrdiff_t );198 forall( dtype T| sized(T) ) volatile T * ?+?( ptrdiff_t, volatile T * );199 forall( dtype T| sized(T) ) const volatile T * ?+?( const volatile T *, ptrdiff_t );200 forall( dtype T| sized(T) ) const volatile T * ?+?( ptrdiff_t, const volatile T * );201 forall( dtype T| sized(T) ) T * ?-?( T *, ptrdiff_t );202 forall( dtype T| sized(T) ) const T * ?-?( const T *, ptrdiff_t );203 forall( dtype T| sized(T) ) volatile T * ?-?( volatile T *, ptrdiff_t );204 forall( dtype T| sized(T) ) const volatile T * ?-?( const volatile T *, ptrdiff_t );205 forall( dtype T| sized(T) ) ptrdiff_t ?-?( const volatile T *, const volatile T * );193 forall( T & | sized(T) ) T * ?+?( T *, ptrdiff_t ); 194 forall( T & | sized(T) ) T * ?+?( ptrdiff_t, T * ); 195 forall( T & | sized(T) ) const T * ?+?( const T *, ptrdiff_t ); 196 forall( T & | sized(T) ) const T * ?+?( ptrdiff_t, const T * ); 197 forall( T & | sized(T) ) volatile T * ?+?( volatile T *, ptrdiff_t ); 198 forall( T & | sized(T) ) volatile T * ?+?( ptrdiff_t, volatile T * ); 199 forall( T & | sized(T) ) const volatile T * ?+?( const volatile T *, ptrdiff_t ); 200 forall( T & | sized(T) ) const volatile T * ?+?( ptrdiff_t, const volatile T * ); 201 forall( T & | sized(T) ) T * ?-?( T *, ptrdiff_t ); 202 forall( T & | sized(T) ) const T * ?-?( const T *, ptrdiff_t ); 203 forall( T & | sized(T) ) volatile T * ?-?( volatile T *, ptrdiff_t ); 204 forall( T & | sized(T) ) const volatile T * ?-?( const volatile T *, ptrdiff_t ); 205 forall( T & | sized(T) ) ptrdiff_t ?-?( const volatile T *, const volatile T * ); 206 206 207 207 // ------------------------------------------------------------ … … 255 255 ?>?( long double, long double ), ?>=?( long double, long double ); 256 256 257 forall( dtype DT) signed int ?<?( DT *, DT * );258 forall( dtype DT) signed int ?<?( const DT *, const DT * );259 forall( dtype DT) signed int ?<?( volatile DT *, volatile DT * );260 forall( dtype DT) signed int ?<?( const volatile DT *, const volatile DT * );261 262 forall( dtype DT) signed int ?>?( DT *, DT * );263 forall( dtype DT) signed int ?>?( const DT *, const DT * );264 forall( dtype DT) signed int ?>?( volatile DT *, volatile DT * );265 forall( dtype DT) signed int ?>?( const volatile DT *, const volatile DT * );266 267 forall( dtype DT) signed int ?<=?( DT *, DT * );268 forall( dtype DT) signed int ?<=?( const DT *, const DT * );269 forall( dtype DT) signed int ?<=?( volatile DT *, volatile DT * );270 forall( dtype DT) signed int ?<=?( const volatile DT *, const volatile DT * );271 272 forall( dtype DT) signed int ?>=?( DT *, DT * );273 forall( dtype DT) signed int ?>=?( const DT *, const DT * );274 forall( dtype DT) signed int ?>=?( volatile DT *, volatile DT * );275 forall( dtype DT) signed int ?>=?( const volatile DT *, const volatile DT * );257 forall( DT & ) signed int ?<?( DT *, DT * ); 258 forall( DT & ) signed int ?<?( const DT *, const DT * ); 259 forall( DT & ) signed int ?<?( volatile DT *, volatile DT * ); 260 forall( DT & ) signed int ?<?( const volatile DT *, const volatile DT * ); 261 262 forall( DT & ) signed int ?>?( DT *, DT * ); 263 forall( DT & ) signed int ?>?( const DT *, const DT * ); 264 forall( DT & ) signed int ?>?( volatile DT *, volatile DT * ); 265 forall( DT & ) signed int ?>?( const volatile DT *, const volatile DT * ); 266 267 forall( DT & ) signed int ?<=?( DT *, DT * ); 268 forall( DT & ) signed int ?<=?( const DT *, const DT * ); 269 forall( DT & ) signed int ?<=?( volatile DT *, volatile DT * ); 270 forall( DT & ) signed int ?<=?( const volatile DT *, const volatile DT * ); 271 272 forall( DT & ) signed int ?>=?( DT *, DT * ); 273 forall( DT & ) signed int ?>=?( const DT *, const DT * ); 274 forall( DT & ) signed int ?>=?( volatile DT *, volatile DT * ); 275 forall( DT & ) signed int ?>=?( const volatile DT *, const volatile DT * ); 276 276 277 277 // ------------------------------------------------------------ … … 302 302 signed int ?==?( one_t, one_t ), ?!=?( one_t, one_t ); 303 303 304 forall( dtype DT) signed int ?==?( DT *, DT * );305 forall( dtype DT) signed int ?==?( const DT *, const DT * );306 forall( dtype DT) signed int ?==?( volatile DT *, volatile DT * );307 forall( dtype DT) signed int ?==?( const volatile DT *, const volatile DT * );304 forall( DT & ) signed int ?==?( DT *, DT * ); 305 forall( DT & ) signed int ?==?( const DT *, const DT * ); 306 forall( DT & ) signed int ?==?( volatile DT *, volatile DT * ); 307 forall( DT & ) signed int ?==?( const volatile DT *, const volatile DT * ); 308 308 forall( ftype FT ) signed int ?==?( FT *, FT * ); 309 forall( dtype DT) signed int ?!=?( DT *, DT * );310 forall( dtype DT) signed int ?!=?( const DT *, const DT * );311 forall( dtype DT) signed int ?!=?( volatile DT *, volatile DT * );312 forall( dtype DT) signed int ?!=?( const volatile DT *, const volatile DT * );309 forall( DT & ) signed int ?!=?( DT *, DT * ); 310 forall( DT & ) signed int ?!=?( const DT *, const DT * ); 311 forall( DT & ) signed int ?!=?( volatile DT *, volatile DT * ); 312 forall( DT & ) signed int ?!=?( const volatile DT *, const volatile DT * ); 313 313 forall( ftype FT ) signed int ?!=?( FT *, FT * ); 314 314 … … 376 376 377 377 forall( ftype FT ) FT * ?=?( FT *&, FT * ); 378 forall( fty peFT ) FT * ?=?( FT * volatile &, FT * );379 380 forall( dtype DT) DT * ?=?( DT * &, DT * );381 forall( dtype DT) DT * ?=?( DT * volatile &, DT * );382 forall( dtype DT) const DT * ?=?( const DT * &, DT * );383 forall( dtype DT) const DT * ?=?( const DT * volatile &, DT * );384 forall( dtype DT) const DT * ?=?( const DT * &, const DT * );385 forall( dtype DT) const DT * ?=?( const DT * volatile &, const DT * );386 forall( dtype DT) volatile DT * ?=?( volatile DT * &, DT * );387 forall( dtype DT) volatile DT * ?=?( volatile DT * volatile &, DT * );388 forall( dtype DT) volatile DT * ?=?( volatile DT * &, volatile DT * );389 forall( dtype DT) volatile DT * ?=?( volatile DT * volatile &, volatile DT * );390 391 forall( dtype DT) const volatile DT * ?=?( const volatile DT * &, DT * );392 forall( dtype DT) const volatile DT * ?=?( const volatile DT * volatile &, DT * );393 forall( dtype DT) const volatile DT * ?=?( const volatile DT * &, const DT * );394 forall( dtype DT) const volatile DT * ?=?( const volatile DT * volatile &, const DT * );395 forall( dtype DT) const volatile DT * ?=?( const volatile DT * &, volatile DT * );396 forall( dtype DT) const volatile DT * ?=?( const volatile DT * volatile &, volatile DT * );397 forall( dtype DT) const volatile DT * ?=?( const volatile DT * &, const volatile DT * );398 forall( dtype DT) const volatile DT * ?=?( const volatile DT * volatile &, const volatile DT * );399 400 forall( dtype DT) void * ?=?( void * &, DT * );401 forall( dtype DT) void * ?=?( void * volatile &, DT * );402 forall( dtype DT) const void * ?=?( const void * &, DT * );403 forall( dtype DT) const void * ?=?( const void * volatile &, DT * );404 forall( dtype DT) const void * ?=?( const void * &, const DT * );405 forall( dtype DT) const void * ?=?( const void * volatile &, const DT * );406 forall( dtype DT) volatile void * ?=?( volatile void * &, DT * );407 forall( dtype DT) volatile void * ?=?( volatile void * volatile &, DT * );408 forall( dtype DT) volatile void * ?=?( volatile void * &, volatile DT * );409 forall( dtype DT) volatile void * ?=?( volatile void * volatile &, volatile DT * );410 forall( dtype DT) const volatile void * ?=?( const volatile void * &, DT * );411 forall( dtype DT) const volatile void * ?=?( const volatile void * volatile &, DT * );412 forall( dtype DT) const volatile void * ?=?( const volatile void * &, const DT * );413 forall( dtype DT) const volatile void * ?=?( const volatile void * volatile &, const DT * );414 forall( dtype DT) const volatile void * ?=?( const volatile void * &, volatile DT * );415 forall( dtype DT) const volatile void * ?=?( const volatile void * volatile &, volatile DT * );416 forall( dtype DT) const volatile void * ?=?( const volatile void * &, const volatile DT * );417 forall( dtype DT) const volatile void * ?=?( const volatile void * volatile &, const volatile DT * );378 forall( ftyep FT ) FT * ?=?( FT * volatile &, FT * ); 379 380 forall( DT & ) DT * ?=?( DT * &, DT * ); 381 forall( DT & ) DT * ?=?( DT * volatile &, DT * ); 382 forall( DT & ) const DT * ?=?( const DT * &, DT * ); 383 forall( DT & ) const DT * ?=?( const DT * volatile &, DT * ); 384 forall( DT & ) const DT * ?=?( const DT * &, const DT * ); 385 forall( DT & ) const DT * ?=?( const DT * volatile &, const DT * ); 386 forall( DT & ) volatile DT * ?=?( volatile DT * &, DT * ); 387 forall( DT & ) volatile DT * ?=?( volatile DT * volatile &, DT * ); 388 forall( DT & ) volatile DT * ?=?( volatile DT * &, volatile DT * ); 389 forall( DT & ) volatile DT * ?=?( volatile DT * volatile &, volatile DT * ); 390 391 forall( DT & ) const volatile DT * ?=?( const volatile DT * &, DT * ); 392 forall( DT & ) const volatile DT * ?=?( const volatile DT * volatile &, DT * ); 393 forall( DT & ) const volatile DT * ?=?( const volatile DT * &, const DT * ); 394 forall( DT & ) const volatile DT * ?=?( const volatile DT * volatile &, const DT * ); 395 forall( DT & ) const volatile DT * ?=?( const volatile DT * &, volatile DT * ); 396 forall( DT & ) const volatile DT * ?=?( const volatile DT * volatile &, volatile DT * ); 397 forall( DT & ) const volatile DT * ?=?( const volatile DT * &, const volatile DT * ); 398 forall( DT & ) const volatile DT * ?=?( const volatile DT * volatile &, const volatile DT * ); 399 400 forall( DT & ) void * ?=?( void * &, DT * ); 401 forall( DT & ) void * ?=?( void * volatile &, DT * ); 402 forall( DT & ) const void * ?=?( const void * &, DT * ); 403 forall( DT & ) const void * ?=?( const void * volatile &, DT * ); 404 forall( DT & ) const void * ?=?( const void * &, const DT * ); 405 forall( DT & ) const void * ?=?( const void * volatile &, const DT * ); 406 forall( DT & ) volatile void * ?=?( volatile void * &, DT * ); 407 forall( DT & ) volatile void * ?=?( volatile void * volatile &, DT * ); 408 forall( DT & ) volatile void * ?=?( volatile void * &, volatile DT * ); 409 forall( DT & ) volatile void * ?=?( volatile void * volatile &, volatile DT * ); 410 forall( DT & ) const volatile void * ?=?( const volatile void * &, DT * ); 411 forall( DT & ) const volatile void * ?=?( const volatile void * volatile &, DT * ); 412 forall( DT & ) const volatile void * ?=?( const volatile void * &, const DT * ); 413 forall( DT & ) const volatile void * ?=?( const volatile void * volatile &, const DT * ); 414 forall( DT & ) const volatile void * ?=?( const volatile void * &, volatile DT * ); 415 forall( DT & ) const volatile void * ?=?( const volatile void * volatile &, volatile DT * ); 416 forall( DT & ) const volatile void * ?=?( const volatile void * &, const volatile DT * ); 417 forall( DT & ) const volatile void * ?=?( const volatile void * volatile &, const volatile DT * ); 418 418 419 419 //forall( dtype DT ) DT * ?=?( DT * &, zero_t ); 420 420 //forall( dtype DT ) DT * ?=?( DT * volatile &, zero_t ); 421 forall( dtype DT) const DT * ?=?( const DT * &, zero_t );422 forall( dtype DT) const DT * ?=?( const DT * volatile &, zero_t );421 forall( DT & ) const DT * ?=?( const DT * &, zero_t ); 422 forall( DT & ) const DT * ?=?( const DT * volatile &, zero_t ); 423 423 //forall( dtype DT ) volatile DT * ?=?( volatile DT * &, zero_t ); 424 424 //forall( dtype DT ) volatile DT * ?=?( volatile DT * volatile &, zero_t ); 425 forall( dtype DT) const volatile DT * ?=?( const volatile DT * &, zero_t );426 forall( dtype DT) const volatile DT * ?=?( const volatile DT * volatile &, zero_t );425 forall( DT & ) const volatile DT * ?=?( const volatile DT * &, zero_t ); 426 forall( DT & ) const volatile DT * ?=?( const volatile DT * volatile &, zero_t ); 427 427 428 428 forall( ftype FT ) FT * ?=?( FT * &, zero_t ); 429 429 forall( ftype FT ) FT * ?=?( FT * volatile &, zero_t ); 430 430 431 forall( dtype T| sized(T) ) T * ?+=?( T * &, ptrdiff_t );432 forall( dtype T| sized(T) ) T * ?+=?( T * volatile &, ptrdiff_t );433 forall( dtype T| sized(T) ) const T * ?+=?( const T * &, ptrdiff_t );434 forall( dtype T| sized(T) ) const T * ?+=?( const T * volatile &, ptrdiff_t );435 forall( dtype T| sized(T) ) volatile T * ?+=?( volatile T * &, ptrdiff_t );436 forall( dtype T| sized(T) ) volatile T * ?+=?( volatile T * volatile &, ptrdiff_t );437 forall( dtype T| sized(T) ) const volatile T * ?+=?( const volatile T * &, ptrdiff_t );438 forall( dtype T| sized(T) ) const volatile T * ?+=?( const volatile T * volatile &, ptrdiff_t );439 forall( dtype T| sized(T) ) T * ?-=?( T * &, ptrdiff_t );440 forall( dtype T| sized(T) ) T * ?-=?( T * volatile &, ptrdiff_t );441 forall( dtype T| sized(T) ) const T * ?-=?( const T * &, ptrdiff_t );442 forall( dtype T| sized(T) ) const T * ?-=?( const T * volatile &, ptrdiff_t );443 forall( dtype T| sized(T) ) volatile T * ?-=?( volatile T * &, ptrdiff_t );444 forall( dtype T| sized(T) ) volatile T * ?-=?( volatile T * volatile &, ptrdiff_t );445 forall( dtype T| sized(T) ) const volatile T * ?-=?( const volatile T * &, ptrdiff_t );446 forall( dtype T| sized(T) ) const volatile T * ?-=?( const volatile T * volatile &, ptrdiff_t );431 forall( T & | sized(T) ) T * ?+=?( T * &, ptrdiff_t ); 432 forall( T & | sized(T) ) T * ?+=?( T * volatile &, ptrdiff_t ); 433 forall( T & | sized(T) ) const T * ?+=?( const T * &, ptrdiff_t ); 434 forall( T & | sized(T) ) const T * ?+=?( const T * volatile &, ptrdiff_t ); 435 forall( T & | sized(T) ) volatile T * ?+=?( volatile T * &, ptrdiff_t ); 436 forall( T & | sized(T) ) volatile T * ?+=?( volatile T * volatile &, ptrdiff_t ); 437 forall( T & | sized(T) ) const volatile T * ?+=?( const volatile T * &, ptrdiff_t ); 438 forall( T & | sized(T) ) const volatile T * ?+=?( const volatile T * volatile &, ptrdiff_t ); 439 forall( T & | sized(T) ) T * ?-=?( T * &, ptrdiff_t ); 440 forall( T & | sized(T) ) T * ?-=?( T * volatile &, ptrdiff_t ); 441 forall( T & | sized(T) ) const T * ?-=?( const T * &, ptrdiff_t ); 442 forall( T & | sized(T) ) const T * ?-=?( const T * volatile &, ptrdiff_t ); 443 forall( T & | sized(T) ) volatile T * ?-=?( volatile T * &, ptrdiff_t ); 444 forall( T & | sized(T) ) volatile T * ?-=?( volatile T * volatile &, ptrdiff_t ); 445 forall( T & | sized(T) ) const volatile T * ?-=?( const volatile T * &, ptrdiff_t ); 446 forall( T & | sized(T) ) const volatile T * ?-=?( const volatile T * volatile &, ptrdiff_t ); 447 447 448 448 _Bool ?=?( _Bool &, _Bool ), ?=?( volatile _Bool &, _Bool ); … … 723 723 forall( ftype FT ) void ?{}( FT * volatile &, FT * ); 724 724 725 forall( dtype DT) void ?{}( DT * &, DT * );726 forall( dtype DT) void ?{}( const DT * &, DT * );727 forall( dtype DT) void ?{}( const DT * &, const DT * );728 forall( dtype DT) void ?{}( volatile DT * &, DT * );729 forall( dtype DT) void ?{}( volatile DT * &, volatile DT * );730 forall( dtype DT) void ?{}( const volatile DT * &, DT * );731 forall( dtype DT) void ?{}( const volatile DT * &, const DT * );732 forall( dtype DT) void ?{}( const volatile DT * &, volatile DT * );733 forall( dtype DT) void ?{}( const volatile DT * &, const volatile DT * );734 735 forall( dtype DT) void ?{}( void * &, DT * );736 forall( dtype DT) void ?{}( const void * &, DT * );737 forall( dtype DT) void ?{}( const void * &, const DT * );738 forall( dtype DT) void ?{}( volatile void * &, DT * );739 forall( dtype DT) void ?{}( volatile void * &, volatile DT * );740 forall( dtype DT) void ?{}( const volatile void * &, DT * );741 forall( dtype DT) void ?{}( const volatile void * &, const DT * );742 forall( dtype DT) void ?{}( const volatile void * &, volatile DT * );743 forall( dtype DT) void ?{}( const volatile void * &, const volatile DT * );725 forall( DT & ) void ?{}( DT * &, DT * ); 726 forall( DT & ) void ?{}( const DT * &, DT * ); 727 forall( DT & ) void ?{}( const DT * &, const DT * ); 728 forall( DT & ) void ?{}( volatile DT * &, DT * ); 729 forall( DT & ) void ?{}( volatile DT * &, volatile DT * ); 730 forall( DT & ) void ?{}( const volatile DT * &, DT * ); 731 forall( DT & ) void ?{}( const volatile DT * &, const DT * ); 732 forall( DT & ) void ?{}( const volatile DT * &, volatile DT * ); 733 forall( DT & ) void ?{}( const volatile DT * &, const volatile DT * ); 734 735 forall( DT & ) void ?{}( void * &, DT * ); 736 forall( DT & ) void ?{}( const void * &, DT * ); 737 forall( DT & ) void ?{}( const void * &, const DT * ); 738 forall( DT & ) void ?{}( volatile void * &, DT * ); 739 forall( DT & ) void ?{}( volatile void * &, volatile DT * ); 740 forall( DT & ) void ?{}( const volatile void * &, DT * ); 741 forall( DT & ) void ?{}( const volatile void * &, const DT * ); 742 forall( DT & ) void ?{}( const volatile void * &, volatile DT * ); 743 forall( DT & ) void ?{}( const volatile void * &, const volatile DT * ); 744 744 745 745 //forall( dtype DT ) void ?{}( DT * &, zero_t ); 746 746 //forall( dtype DT ) void ?{}( DT * volatile &, zero_t ); 747 forall( dtype DT) void ?{}( const DT * &, zero_t );747 forall( DT & ) void ?{}( const DT * &, zero_t ); 748 748 //forall( dtype DT ) void ?{}( volatile DT * &, zero_t ); 749 749 //forall( dtype DT ) void ?{}( volatile DT * volatile &, zero_t ); 750 forall( dtype DT) void ?{}( const volatile DT * &, zero_t );750 forall( DT & ) void ?{}( const volatile DT * &, zero_t ); 751 751 752 752 forall( ftype FT ) void ?{}( FT * &, zero_t ); … … 755 755 forall( ftype FT ) void ?{}( FT * & ); 756 756 757 forall( dtype DT) void ?{}( DT * &);758 forall( dtype DT) void ?{}( const DT * &);759 forall( dtype DT) void ?{}( volatile DT * &);760 forall( dtype DT) void ?{}( const volatile DT * &);757 forall( DT & ) void ?{}( DT * &); 758 forall( DT & ) void ?{}( const DT * &); 759 forall( DT & ) void ?{}( volatile DT * &); 760 forall( DT & ) void ?{}( const volatile DT * &); 761 761 762 762 void ?{}( void * &); … … 768 768 forall( ftype FT ) void ^?{}( FT * & ); 769 769 770 forall( dtype DT) void ^?{}( DT * &);771 forall( dtype DT) void ^?{}( const DT * &);772 forall( dtype DT) void ^?{}( volatile DT * &);773 forall( dtype DT) void ^?{}( const volatile DT * &);770 forall( DT & ) void ^?{}( DT * &); 771 forall( DT & ) void ^?{}( const DT * &); 772 forall( DT & ) void ^?{}( volatile DT * &); 773 forall( DT & ) void ^?{}( const volatile DT * &); 774 774 775 775 void ^?{}( void * &); -
libcfa/prelude/sync-builtins.cf
r342af53 r8e4aa05 206 206 _Bool __sync_bool_compare_and_swap(volatile unsigned __int128 *, unsigned __int128, unsigned __int128,...); 207 207 #endif 208 forall( dtype T) _Bool __sync_bool_compare_and_swap(T * volatile *, T *, T*, ...);208 forall(T &) _Bool __sync_bool_compare_and_swap(T * volatile *, T *, T*, ...); 209 209 210 210 char __sync_val_compare_and_swap(volatile char *, char, char,...); … … 223 223 unsigned __int128 __sync_val_compare_and_swap(volatile unsigned __int128 *, unsigned __int128, unsigned __int128,...); 224 224 #endif 225 forall( dtype T) T * __sync_val_compare_and_swap(T * volatile *, T *, T*,...);225 forall(T &) T * __sync_val_compare_and_swap(T * volatile *, T *, T*,...); 226 226 227 227 char __sync_lock_test_and_set(volatile char *, char,...); … … 326 326 void __atomic_exchange(volatile unsigned __int128 *, volatile unsigned __int128 *, volatile unsigned __int128 *, int); 327 327 #endif 328 forall( dtype T) T * __atomic_exchange_n(T * volatile *, T *, int);329 forall( dtype T) void __atomic_exchange(T * volatile *, T * volatile *, T * volatile *, int);328 forall(T &) T * __atomic_exchange_n(T * volatile *, T *, int); 329 forall(T &) void __atomic_exchange(T * volatile *, T * volatile *, T * volatile *, int); 330 330 331 331 _Bool __atomic_load_n(const volatile _Bool *, int); … … 359 359 void __atomic_load(const volatile unsigned __int128 *, volatile unsigned __int128 *, int); 360 360 #endif 361 forall( dtype T) T * __atomic_load_n(T * const volatile *, int);362 forall( dtype T) void __atomic_load(T * const volatile *, T **, int);361 forall(T &) T * __atomic_load_n(T * const volatile *, int); 362 forall(T &) void __atomic_load(T * const volatile *, T **, int); 363 363 364 364 _Bool __atomic_compare_exchange_n(volatile char *, char *, char, _Bool, int, int); … … 390 390 _Bool __atomic_compare_exchange (volatile unsigned __int128 *, unsigned __int128 *, unsigned __int128 *, _Bool, int, int); 391 391 #endif 392 forall( dtype T) _Bool __atomic_compare_exchange_n (T * volatile *, T **, T*, _Bool, int, int);393 forall( dtype T) _Bool __atomic_compare_exchange (T * volatile *, T **, T**, _Bool, int, int);392 forall(T &) _Bool __atomic_compare_exchange_n (T * volatile *, T **, T*, _Bool, int, int); 393 forall(T &) _Bool __atomic_compare_exchange (T * volatile *, T **, T**, _Bool, int, int); 394 394 395 395 void __atomic_store_n(volatile _Bool *, _Bool, int); … … 423 423 void __atomic_store(volatile unsigned __int128 *, unsigned __int128 *, int); 424 424 #endif 425 forall( dtype T) void __atomic_store_n(T * volatile *, T *, int);426 forall( dtype T) void __atomic_store(T * volatile *, T **, int);425 forall(T &) void __atomic_store_n(T * volatile *, T *, int); 426 forall(T &) void __atomic_store(T * volatile *, T **, int); 427 427 428 428 char __atomic_add_fetch (volatile char *, char, int); -
libcfa/src/Makefile.am
r342af53 r8e4aa05 76 76 stdlib.hfa \ 77 77 time.hfa \ 78 bits/weakso_locks.hfa \ 78 79 containers/maybe.hfa \ 79 80 containers/pair.hfa \ -
libcfa/src/bitmanip.hfa
r342af53 r8e4aa05 100 100 unsigned long long int floor2( unsigned long long int n, unsigned long long int align ) { verify( is_pow2( align ) ); return n & -align; } 101 101 102 // forall( otypeT | { T ?&?( T, T ); T -?( T ); } )102 // forall( T | { T ?&?( T, T ); T -?( T ); } ) 103 103 // T floor2( T n, T align ) { verify( is_pow2( align ) ); return n & -align; } 104 104 … … 115 115 unsigned long long int ceiling2( unsigned long long int n, unsigned long long int align ) { verify( is_pow2( align ) ); return -floor2( -n, align ); } 116 116 117 // forall( otypeT | { T floor2( T, T ); T -?( T ); } )117 // forall( T | { T floor2( T, T ); T -?( T ); } ) 118 118 // T ceiling2( T n, T align ) { verify( is_pow2( align ) ); return -floor2( -n, align ); } 119 119 } // distribution -
libcfa/src/bits/algorithm.hfa
r342af53 r8e4aa05 17 17 18 18 #ifdef SAFE_SORT 19 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort2( T * arr );20 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort3( T * arr );21 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort4( T * arr );22 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort5( T * arr );23 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort6( T * arr );24 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sortN( T * arr, size_t dim );19 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort2( T * arr ); 20 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort3( T * arr ); 21 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort4( T * arr ); 22 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort5( T * arr ); 23 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort6( T * arr ); 24 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sortN( T * arr, size_t dim ); 25 25 26 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } )26 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) 27 27 static inline void __libcfa_small_sort( T * arr, size_t dim ) { 28 28 switch( dim ) { … … 41 41 #define SWAP(x,y) { T a = min(arr[x], arr[y]); T b = max(arr[x], arr[y]); arr[x] = a; arr[y] = b;} 42 42 43 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } )43 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) 44 44 static inline void __libcfa_small_sort2( T * arr ) { 45 45 SWAP(0, 1); 46 46 } 47 47 48 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } )48 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) 49 49 static inline void __libcfa_small_sort3( T * arr ) { 50 50 SWAP(1, 2); … … 53 53 } 54 54 55 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } )55 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) 56 56 static inline void __libcfa_small_sort4( T * arr ) { 57 57 SWAP(0, 1); … … 62 62 } 63 63 64 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } )64 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) 65 65 static inline void __libcfa_small_sort5( T * arr ) { 66 66 SWAP(0, 1); … … 75 75 } 76 76 77 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } )77 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) 78 78 static inline void __libcfa_small_sort6( T * arr ) { 79 79 SWAP(1, 2); … … 91 91 } 92 92 93 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } )93 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) 94 94 static inline void __libcfa_small_sortN( T * arr, size_t dim ) { 95 95 int i, j; … … 112 112 static inline void __libcfa_small_sortN( void* * arr, size_t dim ); 113 113 114 forall( dtype T)114 forall( T & ) 115 115 static inline void __libcfa_small_sort( T* * arr, size_t dim ) { 116 116 switch( dim ) { -
libcfa/src/bits/collection.hfa
r342af53 r8e4aa05 1 // 2 // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo 3 // 4 // The contents of this file are covered under the licence agreement in the 5 // file "LICENCE" distributed with Cforall. 6 // 7 // bits/collection.hfa -- PUBLIC 8 // Intrusive singly-linked list 9 // 10 // Author : Colby Alexander Parsons & Peter A. Buhr 11 // Created On : Thu Jan 21 19:46:50 2021 12 // Last Modified By : 13 // Last Modified On : 14 // Update Count : 15 // 16 1 17 #pragma once 2 #include <stdio.h> // REMOVE THIS AFTER DEBUGGING3 4 18 5 19 struct Colable { 6 struct Colable * next;// next node in the list20 // next node in the list 7 21 // invariant: (next != 0) <=> listed() 22 struct Colable * next; 8 23 }; 9 24 #ifdef __cforall … … 31 46 32 47 // // wrappers to make Collection have T 33 // forall( dtype T) {48 // forall( T & ) { 34 49 // T *& Next( T * n ) { 35 50 // return (T *)Next( (Colable *)n ); … … 38 53 } // distribution 39 54 40 forall( dtype T| { T *& Next ( T * ); } ) {55 static inline forall( T & | { T *& Next ( T * ); } ) { 41 56 bool listed( T * n ) { 42 57 return Next( n ) != 0p; … … 53 68 Collection & ?=?( const Collection & ) = void; // no assignment 54 69 55 void ?{}( Collection & collection ) with( collection ) { 70 void ?{}( Collection & collection ) with( collection ) { 56 71 root = 0p; 57 72 } // post: empty() … … 76 91 } // post: elts = null 77 92 78 forall( dtype T) {93 forall( T & ) { 79 94 T * Curr( ColIter & ci ) with( ci ) { 80 95 return (T *)curr; -
libcfa/src/bits/containers.hfa
r342af53 r8e4aa05 23 23 24 24 #ifdef __cforall 25 forall( dtype T)25 forall(T &) 26 26 #else 27 27 #define T void … … 40 40 41 41 #ifdef __cforall 42 // forall( otypeT | sized(T))42 // forall(T | sized(T)) 43 43 // static inline void ?{}(__small_array(T) & this) {} 44 44 45 forall( dtype T| sized(T))45 forall(T & | sized(T)) 46 46 static inline T & ?[?]( __small_array(T) & this, __lock_size_t idx ) { 47 47 return ((typeof(this.data))this.data)[idx]; 48 48 } 49 49 50 forall( dtype T| sized(T))50 forall(T & | sized(T)) 51 51 static inline T & ?[?]( const __small_array(T) & this, __lock_size_t idx ) { 52 52 return ((typeof(this.data))this.data)[idx]; 53 53 } 54 54 55 forall( dtype T)55 forall(T &) 56 56 static inline T * begin( const __small_array(T) & this ) { 57 57 return ((typeof(this.data))this.data); 58 58 } 59 59 60 forall( dtype T| sized(T))60 forall(T & | sized(T)) 61 61 static inline T * end( const __small_array(T) & this ) { 62 62 return ((typeof(this.data))this.data) + this.size; … … 69 69 70 70 #ifdef __cforall 71 trait is_node( dtype T) {71 trait is_node(T &) { 72 72 T *& get_next( T & ); 73 73 }; … … 78 78 //----------------------------------------------------------------------------- 79 79 #ifdef __cforall 80 forall( dtype TYPE)80 forall(TYPE &) 81 81 #define T TYPE 82 82 #else … … 95 95 96 96 #ifdef __cforall 97 forall( dtype T)97 forall(T &) 98 98 static inline void ?{}( __stack(T) & this ) { 99 99 (this.top){ 0p }; 100 100 } 101 101 102 static inline forall( dtype T| is_node(T) ) {102 static inline forall( T & | is_node(T) ) { 103 103 void push( __stack(T) & this, T * val ) { 104 104 verify( !get_next( *val ) ); … … 126 126 //----------------------------------------------------------------------------- 127 127 #ifdef __cforall 128 forall( dtype TYPE)128 forall(TYPE &) 129 129 #define T TYPE 130 130 #else … … 144 144 145 145 #ifdef __cforall 146 static inline forall( dtype T| is_node(T) ) {146 static inline forall( T & | is_node(T) ) { 147 147 void ?{}( __queue(T) & this ) with( this ) { 148 148 (this.head){ 1p }; … … 151 151 } 152 152 153 void append( __queue(T) & this, T * val ) with( this) {153 void append( __queue(T) & this, T * val ) with(this) { 154 154 verify(this.tail != 0p); 155 155 verify(*this.tail == 1p); … … 161 161 T * peek( __queue(T) & this ) { 162 162 verify(*this.tail == 1p); 163 T * front = this.head;164 if( front != 1p ) {163 T * frontnode = this.head; 164 if( frontnode != 1p ) { 165 165 verify(*this.tail == 1p); 166 return front ;166 return frontnode; 167 167 } 168 168 verify(*this.tail == 1p); … … 215 215 //----------------------------------------------------------------------------- 216 216 #ifdef __cforall 217 forall( dtype TYPE)217 forall(TYPE &) 218 218 #define T TYPE 219 219 #define __getter_t * [T * & next, T * & prev] ( T & ) … … 237 237 238 238 #ifdef __cforall 239 forall( dtype T)239 forall(T & ) 240 240 static inline [void] ?{}( __dllist(T) & this, * [T * & next, T * & prev] ( T & ) __get ) { 241 241 (this.head){ 0p }; … … 245 245 #define next 0 246 246 #define prev 1 247 static inline forall( dtype T) {247 static inline forall(T &) { 248 248 void push_front( __dllist(T) & this, T & node ) with( this ) { 249 249 verify(__get); -
libcfa/src/bits/defs.hfa
r342af53 r8e4aa05 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // defs.hfa -- 7 // defs.hfa -- Commen macros, functions and typedefs 8 // Most files depend on them and they are always useful to have. 9 // 10 // *** Must not contain code specific to libcfathread *** 8 11 // 9 12 // Author : Thierry Delisle … … 62 65 #endif 63 66 } 67 68 // pause to prevent excess processor bus usage 69 #if defined( __i386 ) || defined( __x86_64 ) 70 #define Pause() __asm__ __volatile__ ( "pause" : : : ) 71 #elif defined( __ARM_ARCH ) 72 #define Pause() __asm__ __volatile__ ( "YIELD" : : : ) 73 #else 74 #error unsupported architecture 75 #endif 76 77 #define CFA_IO_LAZY (1_l64u << 32_l64u) -
libcfa/src/bits/locks.hfa
r342af53 r8e4aa05 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // bits/locks.hfa -- Fast internal locks. 7 // bits/locks.hfa -- Basic spinlocks that are reused in the system. 8 // Used for locks that aren't specific to cforall threads and can be used anywhere 9 // 10 // *** Must not contain code specific to libcfathread *** 8 11 // 9 12 // Author : Thierry Delisle … … 19 22 #include "bits/defs.hfa" 20 23 #include <assert.h> 21 22 #ifdef __cforall23 extern "C" {24 #include <pthread.h>25 }26 #endif27 28 // pause to prevent excess processor bus usage29 #if defined( __i386 ) || defined( __x86_64 )30 #define Pause() __asm__ __volatile__ ( "pause" : : : )31 #elif defined( __ARM_ARCH )32 #define Pause() __asm__ __volatile__ ( "YIELD" : : : )33 #else34 #error unsupported architecture35 #endif36 24 37 25 struct __spinlock_t { … … 104 92 enable_interrupts_noPoll(); 105 93 } 106 107 108 #ifdef __CFA_WITH_VERIFY__109 extern bool __cfaabi_dbg_in_kernel();110 #endif111 112 extern "C" {113 char * strerror(int);114 }115 #define CHECKED(x) { int err = x; if( err != 0 ) abort("KERNEL ERROR: Operation \"" #x "\" return error %d - %s\n", err, strerror(err)); }116 117 struct __bin_sem_t {118 pthread_mutex_t lock;119 pthread_cond_t cond;120 int val;121 };122 123 static inline void ?{}(__bin_sem_t & this) with( this ) {124 // Create the mutex with error checking125 pthread_mutexattr_t mattr;126 pthread_mutexattr_init( &mattr );127 pthread_mutexattr_settype( &mattr, PTHREAD_MUTEX_ERRORCHECK_NP);128 pthread_mutex_init(&lock, &mattr);129 130 pthread_cond_init (&cond, (const pthread_condattr_t *)0p); // workaround trac#208: cast should not be required131 val = 0;132 }133 134 static inline void ^?{}(__bin_sem_t & this) with( this ) {135 CHECKED( pthread_mutex_destroy(&lock) );136 CHECKED( pthread_cond_destroy (&cond) );137 }138 139 static inline void wait(__bin_sem_t & this) with( this ) {140 verify(__cfaabi_dbg_in_kernel());141 CHECKED( pthread_mutex_lock(&lock) );142 while(val < 1) {143 pthread_cond_wait(&cond, &lock);144 }145 val -= 1;146 CHECKED( pthread_mutex_unlock(&lock) );147 }148 149 static inline bool post(__bin_sem_t & this) with( this ) {150 bool needs_signal = false;151 152 CHECKED( pthread_mutex_lock(&lock) );153 if(val < 1) {154 val += 1;155 pthread_cond_signal(&cond);156 needs_signal = true;157 }158 CHECKED( pthread_mutex_unlock(&lock) );159 160 return needs_signal;161 }162 163 #undef CHECKED164 165 struct $thread;166 extern void park( void );167 extern void unpark( struct $thread * this );168 static inline struct $thread * active_thread ();169 170 // Semaphore which only supports a single thread171 struct single_sem {172 struct $thread * volatile ptr;173 };174 175 static inline {176 void ?{}(single_sem & this) {177 this.ptr = 0p;178 }179 180 void ^?{}(single_sem &) {}181 182 bool wait(single_sem & this) {183 for() {184 struct $thread * expected = this.ptr;185 if(expected == 1p) {186 if(__atomic_compare_exchange_n(&this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {187 return false;188 }189 }190 else {191 /* paranoid */ verify( expected == 0p );192 if(__atomic_compare_exchange_n(&this.ptr, &expected, active_thread(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {193 park();194 return true;195 }196 }197 198 }199 }200 201 bool post(single_sem & this) {202 for() {203 struct $thread * expected = this.ptr;204 if(expected == 1p) return false;205 if(expected == 0p) {206 if(__atomic_compare_exchange_n(&this.ptr, &expected, 1p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {207 return false;208 }209 }210 else {211 if(__atomic_compare_exchange_n(&this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {212 unpark( expected );213 return true;214 }215 }216 }217 }218 }219 220 // Synchronozation primitive which only supports a single thread and one post221 // Similar to a binary semaphore with a 'one shot' semantic222 // is expected to be discarded after each party call their side223 struct oneshot {224 // Internal state :225 // 0p : is initial state (wait will block)226 // 1p : fulfilled (wait won't block)227 // any thread : a thread is currently waiting228 struct $thread * volatile ptr;229 };230 231 static inline {232 void ?{}(oneshot & this) {233 this.ptr = 0p;234 }235 236 void ^?{}(oneshot &) {}237 238 // Wait for the post, return immidiately if it already happened.239 // return true if the thread was parked240 bool wait(oneshot & this) {241 for() {242 struct $thread * expected = this.ptr;243 if(expected == 1p) return false;244 /* paranoid */ verify( expected == 0p );245 if(__atomic_compare_exchange_n(&this.ptr, &expected, active_thread(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {246 park();247 /* paranoid */ verify( this.ptr == 1p );248 return true;249 }250 }251 }252 253 // Mark as fulfilled, wake thread if needed254 // return true if a thread was unparked255 bool post(oneshot & this) {256 struct $thread * got = __atomic_exchange_n( &this.ptr, 1p, __ATOMIC_SEQ_CST);257 if( got == 0p ) return false;258 unpark( got );259 return true;260 }261 }262 263 // base types for future to build upon264 // It is based on the 'oneshot' type to allow multiple futures265 // to block on the same instance, permitting users to block a single266 // thread on "any of" [a given set of] futures.267 // does not support multiple threads waiting on the same future268 struct future_t {269 // Internal state :270 // 0p : is initial state (wait will block)271 // 1p : fulfilled (wait won't block)272 // 2p : in progress ()273 // 3p : abandoned, server should delete274 // any oneshot : a context has been setup to wait, a thread could wait on it275 struct oneshot * volatile ptr;276 };277 278 static inline {279 void ?{}(future_t & this) {280 this.ptr = 0p;281 }282 283 void ^?{}(future_t &) {}284 285 void reset(future_t & this) {286 // needs to be in 0p or 1p287 __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST);288 }289 290 // check if the future is available291 bool available( future_t & this ) {292 return this.ptr == 1p;293 }294 295 // Prepare the future to be waited on296 // intented to be use by wait, wait_any, waitfor, etc. rather than used directly297 bool setup( future_t & this, oneshot & wait_ctx ) {298 /* paranoid */ verify( wait_ctx.ptr == 0p );299 // The future needs to set the wait context300 for() {301 struct oneshot * expected = this.ptr;302 // Is the future already fulfilled?303 if(expected == 1p) return false; // Yes, just return false (didn't block)304 305 // The future is not fulfilled, try to setup the wait context306 /* paranoid */ verify( expected == 0p );307 if(__atomic_compare_exchange_n(&this.ptr, &expected, &wait_ctx, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {308 return true;309 }310 }311 }312 313 // Stop waiting on a future314 // When multiple futures are waited for together in "any of" pattern315 // futures that weren't fulfilled before the thread woke up316 // should retract the wait ctx317 // intented to be use by wait, wait_any, waitfor, etc. rather than used directly318 void retract( future_t & this, oneshot & wait_ctx ) {319 // Remove the wait context320 struct oneshot * got = __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST);321 322 // got == 0p: future was never actually setup, just return323 if( got == 0p ) return;324 325 // got == wait_ctx: since fulfil does an atomic_swap,326 // if we got back the original then no one else saw context327 // It is safe to delete (which could happen after the return)328 if( got == &wait_ctx ) return;329 330 // got == 1p: the future is ready and the context was fully consumed331 // the server won't use the pointer again332 // It is safe to delete (which could happen after the return)333 if( got == 1p ) return;334 335 // got == 2p: the future is ready but the context hasn't fully been consumed336 // spin until it is safe to move on337 if( got == 2p ) {338 while( this.ptr != 1p ) Pause();339 return;340 }341 342 // got == any thing else, something wen't wrong here, abort343 abort("Future in unexpected state");344 }345 346 // Mark the future as abandoned, meaning it will be deleted by the server347 bool abandon( future_t & this ) {348 /* paranoid */ verify( this.ptr != 3p );349 350 // Mark the future as abandonned351 struct oneshot * got = __atomic_exchange_n( &this.ptr, 3p, __ATOMIC_SEQ_CST);352 353 // If the future isn't already fulfilled, let the server delete it354 if( got == 0p ) return false;355 356 // got == 2p: the future is ready but the context hasn't fully been consumed357 // spin until it is safe to move on358 if( got == 2p ) {359 while( this.ptr != 1p ) Pause();360 got = 1p;361 }362 363 // The future is completed delete it now364 /* paranoid */ verify( this.ptr != 1p );365 free( &this );366 return true;367 }368 369 // from the server side, mark the future as fulfilled370 // delete it if needed371 bool fulfil( future_t & this ) {372 for() {373 struct oneshot * expected = this.ptr;374 // was this abandoned?375 #if defined(__GNUC__) && __GNUC__ >= 7376 #pragma GCC diagnostic push377 #pragma GCC diagnostic ignored "-Wfree-nonheap-object"378 #endif379 if( expected == 3p ) { free( &this ); return false; }380 #if defined(__GNUC__) && __GNUC__ >= 7381 #pragma GCC diagnostic pop382 #endif383 384 /* paranoid */ verify( expected != 1p ); // Future is already fulfilled, should not happen385 /* paranoid */ verify( expected != 2p ); // Future is bein fulfilled by someone else, this is even less supported then the previous case.386 387 // If there is a wait context, we need to consume it and mark it as consumed after388 // If there is no context then we can skip the in progress phase389 struct oneshot * want = expected == 0p ? 1p : 2p;390 if(__atomic_compare_exchange_n(&this.ptr, &expected, want, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {391 if( expected == 0p ) { /* paranoid */ verify( this.ptr == 1p); return false; }392 bool ret = post( *expected );393 __atomic_store_n( &this.ptr, 1p, __ATOMIC_SEQ_CST);394 return ret;395 }396 }397 398 }399 400 // Wait for the future to be fulfilled401 bool wait( future_t & this ) {402 oneshot temp;403 if( !setup(this, temp) ) return false;404 405 // Wait context is setup, just wait on it406 bool ret = wait( temp );407 408 // Wait for the future to tru409 while( this.ptr == 2p ) Pause();410 // Make sure the state makes sense411 // Should be fulfilled, could be in progress but it's out of date if so412 // since if that is the case, the oneshot was fulfilled (unparking this thread)413 // and the oneshot should not be needed any more414 __attribute__((unused)) struct oneshot * was = this.ptr;415 /* paranoid */ verifyf( was == 1p, "Expected this.ptr to be 1p, was %p\n", was );416 417 // Mark the future as fulfilled, to be consistent418 // with potential calls to avail419 // this.ptr = 1p;420 return ret;421 }422 }423 94 #endif -
libcfa/src/bits/queue.hfa
r342af53 r8e4aa05 9 9 // instead of being null. 10 10 11 forall( dtype T| { T *& Next ( T * ); } ) {11 forall( T & | { T *& Next ( T * ); } ) { 12 12 struct Queue { 13 13 inline Collection; // Plan 9 inheritance … … 151 151 } // distribution 152 152 153 forall( dtype T| { T *& Next ( T * ); } ) {153 forall( T & | { T *& Next ( T * ); } ) { 154 154 struct QueueIter { 155 155 inline ColIter; // Plan 9 inheritance -
libcfa/src/bits/sequence.hfa
r342af53 r8e4aa05 1 // 2 // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo 3 // 4 // The contents of this file are covered under the licence agreement in the 5 // file "LICENCE" distributed with Cforall. 6 // 7 // bits/sequence.hfa -- PUBLIC 8 // Intrusive doubly-linked list 9 // 10 // Author : Colby Alexander Parsons & Peter A. Buhr 11 // Created On : Thu Jan 21 19:46:50 2021 12 // Last Modified By : 13 // Last Modified On : 14 // Update Count : 15 // 16 1 17 #pragma once 2 18 … … 6 22 struct Seqable { 7 23 __cfa_anonymous_object(Colable); 8 struct Seqable * back; // pointer to previous node in the list 24 // pointer to previous node in the list 25 struct Seqable * back; 9 26 }; 10 27 … … 13 30 // PUBLIC 14 31 15 void ?{}( Seqable & sq ) with( sq ){32 void ?{}( Seqable & sq ) { 16 33 ((Colable &)sq){}; 17 back = 0p;34 sq.back = 0p; 18 35 } // post: ! listed() 19 36 … … 27 44 return sq->back; 28 45 } 29 30 // // wrappers to make Collection have T31 // forall( dtype T ) {32 // T *& Back( T * n ) {33 // return (T *)Back( (Seqable *)n );34 // }35 // } // distribution36 46 } // distribution 37 47 … … 43 53 // and the back field of the last node points at the first node (circular). 44 54 45 forall( dtype T | { T *& Back ( T * ); T *& Next ( T * ); }) {55 forall( T & ) { 46 56 struct Sequence { 47 inline Collection; // Plan 9 inheritance 57 // Plan 9 inheritance 58 inline Collection; 48 59 }; 49 60 50 61 static inline { 62 void ?{}( Sequence(T) &, const Sequence(T) & ) = void; // no copy 63 Sequence(T) & ?=?( const Sequence(T) & ) = void; // no assignment 64 65 void ?{}( Sequence(T) & s ) with( s ) { 66 ((Collection &)s){}; 67 } // post: isEmpty() 68 } 69 70 static inline forall(| { T *& Back ( T * ); T *& Next ( T * ); }) { 51 71 // wrappers to make Collection have T 52 72 T & head( Sequence(T) & s ) with( s ) { 53 73 return *(T *)head( (Collection &)s ); 54 74 } // post: empty() & head() == 0 | !empty() & head() in *s 55 56 void ?{}( Sequence(T) &, const Sequence(T) & ) = void; // no copy57 Sequence(T) & ?=?( const Sequence(T) & ) = void; // no assignment58 59 void ?{}( Sequence(T) & s ) with( s ) {60 ((Collection &)s){};61 } // post: isEmpty()62 75 63 76 // Return a pointer to the last sequence element, without removing it. … … 145 158 return n; 146 159 } // post: n->listed() & *n in *s & succ(n) == bef 147 160 148 161 // pre: n->listed() & *n in *s 149 162 T & remove( Sequence(T) & s, T & n ) with( s ) { // O(1) … … 231 244 } // distribution 232 245 233 forall( dtype T| { T *& Back ( T * ); T *& Next ( T * ); } ) {246 forall( T & | { T *& Back ( T * ); T *& Next ( T * ); } ) { 234 247 // SeqIter(T) is used to iterate over a Sequence(T) in head-to-tail order. 235 248 struct SeqIter { … … 285 298 286 299 static inline { 287 void ?{}( SeqIterRev(T) & si ) with( si ) { 300 void ?{}( SeqIterRev(T) & si ) with( si ) { 288 301 ((ColIter &)si){}; 289 302 seq = 0p; … … 291 304 292 305 // Create a iterator active in sequence s. 293 void ?{}( SeqIterRev(T) & si, Sequence(T) & s ) with( si ) { 306 void ?{}( SeqIterRev(T) & si, Sequence(T) & s ) with( si ) { 294 307 ((ColIter &)si){}; 295 308 seq = &s; … … 297 310 } // post: elts = null 298 311 299 void ?{}( SeqIterRev(T) & si, Sequence(T) & s, T & start ) with( si ) { 312 void ?{}( SeqIterRev(T) & si, Sequence(T) & s, T & start ) with( si ) { 300 313 ((ColIter &)si){}; 301 314 seq = &s; -
libcfa/src/bits/stack.hfa
r342af53 r8e4aa05 9 9 // instead of being null. 10 10 11 forall( dtype T| { T *& Next ( T * ); } ) {11 forall( T & | { T *& Next ( T * ); } ) { 12 12 struct Stack { 13 13 inline Collection; // Plan 9 inheritance … … 67 67 // order returned by drop(). 68 68 69 forall( dtype T| { T *& Next ( T * ); } ) {69 forall( T & | { T *& Next ( T * ); } ) { 70 70 struct StackIter { 71 71 inline ColIter; // Plan 9 inheritance -
libcfa/src/common.cfa
r342af53 r8e4aa05 23 23 [ long int, long int ] div( long int num, long int denom ) { ldiv_t qr = ldiv( num, denom ); return [ qr.quot, qr.rem ]; } 24 24 [ long long int, long long int ] div( long long int num, long long int denom ) { lldiv_t qr = lldiv( num, denom ); return [ qr.quot, qr.rem ]; } 25 forall( otypeT | { T ?/?( T, T ); T ?%?( T, T ); } )25 forall( T | { T ?/?( T, T ); T ?%?( T, T ); } ) 26 26 [ T, T ] div( T num, T denom ) { return [ num / denom, num % denom ]; } 27 27 -
libcfa/src/common.hfa
r342af53 r8e4aa05 21 21 [ long int, long int ] div( long int num, long int denom ); 22 22 [ long long int, long long int ] div( long long int num, long long int denom ); 23 forall( otypeT | { T ?/?( T, T ); T ?%?( T, T ); } )23 forall( T | { T ?/?( T, T ); T ?%?( T, T ); } ) 24 24 [ T, T ] div( T num, T demon ); 25 25 … … 61 61 } // distribution 62 62 63 forall( otypeT | { void ?{}( T &, zero_t ); int ?<?( T, T ); T -?( T ); } )63 forall( T | { void ?{}( T &, zero_t ); int ?<?( T, T ); T -?( T ); } ) 64 64 T abs( T ); 65 65 … … 70 70 intptr_t min( intptr_t t1, intptr_t t2 ) { return t1 < t2 ? t1 : t2; } // optimization 71 71 uintptr_t min( uintptr_t t1, uintptr_t t2 ) { return t1 < t2 ? t1 : t2; } // optimization 72 forall( otypeT | { int ?<?( T, T ); } )72 forall( T | { int ?<?( T, T ); } ) 73 73 T min( T t1, T t2 ) { return t1 < t2 ? t1 : t2; } 74 74 … … 76 76 intptr_t max( intptr_t t1, intptr_t t2 ) { return t1 > t2 ? t1 : t2; } // optimization 77 77 uintptr_t max( uintptr_t t1, uintptr_t t2 ) { return t1 > t2 ? t1 : t2; } // optimization 78 forall( otypeT | { int ?>?( T, T ); } )78 forall( T | { int ?>?( T, T ); } ) 79 79 T max( T t1, T t2 ) { return t1 > t2 ? t1 : t2; } 80 80 81 forall( otypeT | { T min( T, T ); T max( T, T ); } )81 forall( T | { T min( T, T ); T max( T, T ); } ) 82 82 T clamp( T value, T min_val, T max_val ) { return max( min_val, min( value, max_val ) ); } 83 83 84 forall( otypeT )84 forall( T ) 85 85 void swap( T & v1, T & v2 ) { T temp = v1; v1 = v2; v2 = temp; } 86 86 } // distribution -
libcfa/src/concurrency/coroutine.cfa
r342af53 r8e4aa05 46 46 47 47 //----------------------------------------------------------------------------- 48 FORALL_DATA_INSTANCE(CoroutineCancelled, ( dtype coroutine_t), (coroutine_t))49 50 forall( dtype T)48 FORALL_DATA_INSTANCE(CoroutineCancelled, (coroutine_t &), (coroutine_t)) 49 50 forall(T &) 51 51 void mark_exception(CoroutineCancelled(T) *) {} 52 52 53 forall( dtype T)53 forall(T &) 54 54 void copy(CoroutineCancelled(T) * dst, CoroutineCancelled(T) * src) { 55 55 dst->virtual_table = src->virtual_table; … … 58 58 } 59 59 60 forall( dtype T)60 forall(T &) 61 61 const char * msg(CoroutineCancelled(T) *) { 62 62 return "CoroutineCancelled(...)"; … … 64 64 65 65 // This code should not be inlined. It is the error path on resume. 66 forall( dtype T| is_coroutine(T))66 forall(T & | is_coroutine(T)) 67 67 void __cfaehm_cancelled_coroutine( T & cor, $coroutine * desc ) { 68 68 verify( desc->cancellation ); … … 148 148 // Part of the Public API 149 149 // Not inline since only ever called once per coroutine 150 forall( dtype T| is_coroutine(T))150 forall(T & | is_coroutine(T)) 151 151 void prime(T& cor) { 152 152 $coroutine* this = get_coroutine(cor); … … 196 196 197 197 void __stack_clean ( __stack_info_t * this ) { 198 size_t size = ((intptr_t)this->storage->base) - ((intptr_t)this->storage->limit) + sizeof(__stack_t);199 198 void * storage = this->storage->limit; 200 199 201 200 #if CFA_COROUTINE_USE_MMAP 201 size_t size = ((intptr_t)this->storage->base) - ((intptr_t)this->storage->limit) + sizeof(__stack_t); 202 202 storage = (void *)(((intptr_t)storage) - __page_size); 203 203 if(munmap(storage, size + __page_size) == -1) { -
libcfa/src/concurrency/coroutine.hfa
r342af53 r8e4aa05 22 22 //----------------------------------------------------------------------------- 23 23 // Exception thrown from resume when a coroutine stack is cancelled. 24 FORALL_DATA_EXCEPTION(CoroutineCancelled, ( dtype coroutine_t), (coroutine_t)) (24 FORALL_DATA_EXCEPTION(CoroutineCancelled, (coroutine_t &), (coroutine_t)) ( 25 25 coroutine_t * the_coroutine; 26 26 exception_t * the_exception; 27 27 ); 28 28 29 forall( dtype T)29 forall(T &) 30 30 void copy(CoroutineCancelled(T) * dst, CoroutineCancelled(T) * src); 31 31 32 forall( dtype T)32 forall(T &) 33 33 const char * msg(CoroutineCancelled(T) *); 34 34 … … 37 37 // Anything that implements this trait can be resumed. 38 38 // Anything that is resumed is a coroutine. 39 trait is_coroutine( dtype T| IS_RESUMPTION_EXCEPTION(CoroutineCancelled, (T))) {39 trait is_coroutine(T & | IS_RESUMPTION_EXCEPTION(CoroutineCancelled, (T))) { 40 40 void main(T & this); 41 41 $coroutine * get_coroutine(T & this); … … 60 60 //----------------------------------------------------------------------------- 61 61 // Public coroutine API 62 forall( dtype T| is_coroutine(T))62 forall(T & | is_coroutine(T)) 63 63 void prime(T & cor); 64 64 … … 72 72 void __cfactx_invoke_coroutine(void (*main)(void *), void * this); 73 73 74 forall( dtype T)74 forall(T &) 75 75 void __cfactx_start(void (*main)(T &), struct $coroutine * cor, T & this, void (*invoke)(void (*main)(void *), void *)); 76 76 … … 129 129 } 130 130 131 forall( dtype T| is_coroutine(T))131 forall(T & | is_coroutine(T)) 132 132 void __cfaehm_cancelled_coroutine( T & cor, $coroutine * desc ); 133 133 134 134 // Resume implementation inlined for performance 135 forall( dtype T| is_coroutine(T))135 forall(T & | is_coroutine(T)) 136 136 static inline T & resume(T & cor) { 137 137 // optimization : read TLS once and reuse it -
libcfa/src/concurrency/future.hfa
r342af53 r8e4aa05 19 19 #include "monitor.hfa" 20 20 21 forall( otypeT ) {21 forall( T ) { 22 22 struct future { 23 23 inline future_t; … … 58 58 } 59 59 60 forall( otypeT ) {60 forall( T ) { 61 61 monitor multi_future { 62 62 inline future_t; -
libcfa/src/concurrency/io.cfa
r342af53 r8e4aa05 32 32 extern "C" { 33 33 #include <sys/syscall.h> 34 #include <sys/eventfd.h> 34 35 35 36 #include <linux/io_uring.h> … … 41 42 #include "io/types.hfa" 42 43 43 static const char * opcodes[] = {44 __attribute__((unused)) static const char * opcodes[] = { 44 45 "OP_NOP", 45 46 "OP_READV", … … 79 80 }; 80 81 81 // returns true of acquired as leader or second leader 82 static inline bool try_lock( __leaderlock_t & this ) { 83 const uintptr_t thrd = 1z | (uintptr_t)active_thread(); 84 bool block; 85 disable_interrupts(); 86 for() { 87 struct $thread * expected = this.value; 88 if( 1p != expected && 0p != expected ) { 89 /* paranoid */ verify( thrd != (uintptr_t)expected ); // We better not already be the next leader 90 enable_interrupts( __cfaabi_dbg_ctx ); 91 return false; 92 } 93 struct $thread * desired; 94 if( 0p == expected ) { 95 // If the lock isn't locked acquire it, no need to block 96 desired = 1p; 97 block = false; 98 } 99 else { 100 // If the lock is already locked try becomming the next leader 101 desired = (struct $thread *)thrd; 102 block = true; 103 } 104 if( __atomic_compare_exchange_n(&this.value, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ) break; 105 } 106 if( block ) { 107 enable_interrupts( __cfaabi_dbg_ctx ); 108 park(); 109 disable_interrupts(); 110 } 111 return true; 112 } 113 114 static inline bool next( __leaderlock_t & this ) { 82 static $io_context * __ioarbiter_allocate( $io_arbiter & mutex this, processor *, __u32 idxs[], __u32 want ); 83 static void __ioarbiter_submit( $io_arbiter & mutex this, $io_context * , __u32 idxs[], __u32 have, bool lazy ); 84 static void __ioarbiter_flush ( $io_arbiter & mutex this, $io_context * ); 85 static inline void __ioarbiter_notify( $io_context & ctx ); 86 //============================================================================================= 87 // I/O Polling 88 //============================================================================================= 89 static inline unsigned __flush( struct $io_context & ); 90 static inline __u32 __release_sqes( struct $io_context & ); 91 92 void __cfa_io_drain( processor * proc ) { 115 93 /* paranoid */ verify( ! __preemption_enabled() ); 116 struct $thread * nextt; 117 for() { 118 struct $thread * expected = this.value; 119 /* paranoid */ verify( (1 & (uintptr_t)expected) == 1 ); // The lock better be locked 120 121 struct $thread * desired; 122 if( 1p == expected ) { 123 // No next leader, just unlock 124 desired = 0p; 125 nextt = 0p; 126 } 127 else { 128 // There is a next leader, remove but keep locked 129 desired = 1p; 130 nextt = (struct $thread *)(~1z & (uintptr_t)expected); 131 } 132 if( __atomic_compare_exchange_n(&this.value, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ) break; 133 } 134 135 if(nextt) { 136 unpark( nextt ); 137 enable_interrupts( __cfaabi_dbg_ctx ); 138 return true; 139 } 140 enable_interrupts( __cfaabi_dbg_ctx ); 141 return false; 142 } 143 144 //============================================================================================= 145 // I/O Syscall 146 //============================================================================================= 147 static int __io_uring_enter( struct __io_data & ring, unsigned to_submit, bool get ) { 148 bool need_sys_to_submit = false; 149 bool need_sys_to_complete = false; 150 unsigned flags = 0; 151 152 TO_SUBMIT: 153 if( to_submit > 0 ) { 154 if( !(ring.ring_flags & IORING_SETUP_SQPOLL) ) { 155 need_sys_to_submit = true; 156 break TO_SUBMIT; 157 } 158 if( (*ring.submit_q.flags) & IORING_SQ_NEED_WAKEUP ) { 159 need_sys_to_submit = true; 160 flags |= IORING_ENTER_SQ_WAKEUP; 161 } 162 } 163 164 if( get && !(ring.ring_flags & IORING_SETUP_SQPOLL) ) { 165 flags |= IORING_ENTER_GETEVENTS; 166 if( (ring.ring_flags & IORING_SETUP_IOPOLL) ) { 167 need_sys_to_complete = true; 168 } 169 } 170 171 int ret = 0; 172 if( need_sys_to_submit || need_sys_to_complete ) { 173 __cfadbg_print_safe(io_core, "Kernel I/O : IO_URING enter %d %u %u\n", ring.fd, to_submit, flags); 174 ret = syscall( __NR_io_uring_enter, ring.fd, to_submit, 0, flags, (sigset_t *)0p, _NSIG / 8); 175 if( ret < 0 ) { 176 switch((int)errno) { 177 case EAGAIN: 178 case EINTR: 179 ret = -1; 180 break; 181 default: 182 abort( "KERNEL ERROR: IO_URING SYSCALL - (%d) %s\n", (int)errno, strerror(errno) ); 183 } 184 } 185 } 186 187 // Memory barrier 188 __atomic_thread_fence( __ATOMIC_SEQ_CST ); 189 return ret; 190 } 191 192 //============================================================================================= 193 // I/O Polling 194 //============================================================================================= 195 static unsigned __collect_submitions( struct __io_data & ring ); 196 static __u32 __release_consumed_submission( struct __io_data & ring ); 197 static inline void __clean( volatile struct io_uring_sqe * sqe ); 198 199 // Process a single completion message from the io_uring 200 // This is NOT thread-safe 201 static inline void process( volatile struct io_uring_cqe & cqe ) { 202 struct io_future_t * future = (struct io_future_t *)(uintptr_t)cqe.user_data; 203 __cfadbg_print_safe( io, "Kernel I/O : Syscall completed : cqe %p, result %d for %p\n", &cqe, cqe.res, future ); 204 205 fulfil( *future, cqe.res ); 206 } 207 208 static [int, bool] __drain_io( & struct __io_data ring ) { 209 /* paranoid */ verify( ! __preemption_enabled() ); 210 211 unsigned to_submit = 0; 212 if( ring.poller_submits ) { 213 // If the poller thread also submits, then we need to aggregate the submissions which are ready 214 to_submit = __collect_submitions( ring ); 215 } 216 217 int ret = __io_uring_enter(ring, to_submit, true); 218 if( ret < 0 ) { 219 return [0, true]; 220 } 221 222 // update statistics 223 if (to_submit > 0) { 224 __STATS__( true, 225 if( to_submit > 0 ) { 226 io.submit_q.submit_avg.rdy += to_submit; 227 io.submit_q.submit_avg.csm += ret; 228 io.submit_q.submit_avg.cnt += 1; 229 } 230 ) 231 } 232 233 __atomic_thread_fence( __ATOMIC_SEQ_CST ); 234 235 // Release the consumed SQEs 236 __release_consumed_submission( ring ); 94 /* paranoid */ verify( proc ); 95 /* paranoid */ verify( proc->io.ctx ); 237 96 238 97 // Drain the queue 239 unsigned head = *ring.completion_q.head; 240 unsigned tail = *ring.completion_q.tail; 241 const __u32 mask = *ring.completion_q.mask; 242 243 // Nothing was new return 0 244 if (head == tail) { 245 return [0, to_submit > 0]; 246 } 98 $io_context * ctx = proc->io.ctx; 99 unsigned head = *ctx->cq.head; 100 unsigned tail = *ctx->cq.tail; 101 const __u32 mask = *ctx->cq.mask; 247 102 248 103 __u32 count = tail - head; 249 /* paranoid */ verify( count != 0 ); 104 __STATS__( false, io.calls.drain++; io.calls.completed += count; ) 105 250 106 for(i; count) { 251 107 unsigned idx = (head + i) & mask; 252 volatile struct io_uring_cqe & cqe = ring.completion_q.cqes[idx];108 volatile struct io_uring_cqe & cqe = ctx->cq.cqes[idx]; 253 109 254 110 /* paranoid */ verify(&cqe); 255 111 256 process( cqe ); 257 } 112 struct io_future_t * future = (struct io_future_t *)(uintptr_t)cqe.user_data; 113 __cfadbg_print_safe( io, "Kernel I/O : Syscall completed : cqe %p, result %d for %p\n", &cqe, cqe.res, future ); 114 115 fulfil( *future, cqe.res ); 116 } 117 118 __cfadbg_print_safe(io, "Kernel I/O : %u completed\n", count); 258 119 259 120 // Mark to the kernel that the cqe has been seen 260 121 // Ensure that the kernel only sees the new value of the head index after the CQEs have been read. 261 __atomic_fetch_add( ring.completion_q.head, count, __ATOMIC_SEQ_CST ); 262 263 return [count, count > 0 || to_submit > 0]; 264 } 265 266 void main( $io_ctx_thread & this ) { 267 __ioctx_register( this ); 268 269 __cfadbg_print_safe(io_core, "Kernel I/O : IO poller %d (%p) ready\n", this.ring->fd, &this); 270 271 const int reset_cnt = 5; 272 int reset = reset_cnt; 273 // Then loop until we need to start 274 LOOP: 275 while(!__atomic_load_n(&this.done, __ATOMIC_SEQ_CST)) { 276 // Drain the io 277 int count; 278 bool again; 279 disable_interrupts(); 280 [count, again] = __drain_io( *this.ring ); 281 282 if(!again) reset--; 283 122 __atomic_store_n( ctx->cq.head, head + count, __ATOMIC_SEQ_CST ); 123 124 /* paranoid */ verify( ! __preemption_enabled() ); 125 126 return; 127 } 128 129 void __cfa_io_flush( processor * proc ) { 130 /* paranoid */ verify( ! __preemption_enabled() ); 131 /* paranoid */ verify( proc ); 132 /* paranoid */ verify( proc->io.ctx ); 133 134 $io_context & ctx = *proc->io.ctx; 135 136 if(!ctx.ext_sq.empty) { 137 __ioarbiter_flush( *ctx.arbiter, &ctx ); 138 } 139 140 __STATS__( true, io.calls.flush++; ) 141 int ret = syscall( __NR_io_uring_enter, ctx.fd, ctx.sq.to_submit, 0, 0, (sigset_t *)0p, _NSIG / 8); 142 if( ret < 0 ) { 143 switch((int)errno) { 144 case EAGAIN: 145 case EINTR: 146 case EBUSY: 284 147 // Update statistics 285 __STATS__( true, 286 io.complete_q.completed_avg.val += count; 287 io.complete_q.completed_avg.cnt += 1; 288 ) 289 enable_interrupts( __cfaabi_dbg_ctx ); 290 291 // If we got something, just yield and check again 292 if(reset > 1) { 293 yield(); 294 continue LOOP; 148 __STATS__( false, io.calls.errors.busy ++; ) 149 return; 150 default: 151 abort( "KERNEL ERROR: IO_URING SYSCALL - (%d) %s\n", (int)errno, strerror(errno) ); 295 152 } 296 297 // We alread failed to find completed entries a few time. 298 if(reset == 1) { 299 // Rearm the context so it can block 300 // but don't block right away 301 // we need to retry one last time in case 302 // something completed *just now* 303 __ioctx_prepare_block( this ); 304 continue LOOP; 305 } 306 307 __STATS__( false, 308 io.complete_q.blocks += 1; 309 ) 310 __cfadbg_print_safe(io_core, "Kernel I/O : Parking io poller %d (%p)\n", this.ring->fd, &this); 311 312 // block this thread 313 wait( this.sem ); 314 315 // restore counter 316 reset = reset_cnt; 317 } 318 319 __cfadbg_print_safe(io_core, "Kernel I/O : Fast poller %d (%p) stopping\n", this.ring->fd, &this); 153 } 154 155 __cfadbg_print_safe(io, "Kernel I/O : %u submitted to io_uring %d\n", ret, ctx.fd); 156 __STATS__( true, io.calls.submitted += ret; ) 157 /* paranoid */ verify( ctx.sq.to_submit <= *ctx.sq.num ); 158 /* paranoid */ verify( ctx.sq.to_submit >= ret ); 159 160 ctx.sq.to_submit -= ret; 161 162 /* paranoid */ verify( ctx.sq.to_submit <= *ctx.sq.num ); 163 164 // Release the consumed SQEs 165 __release_sqes( ctx ); 166 167 /* paranoid */ verify( ! __preemption_enabled() ); 168 169 ctx.proc->io.pending = false; 320 170 } 321 171 … … 339 189 // head and tail must be fully filled and shouldn't ever be touched again. 340 190 // 191 //============================================================================================= 192 // Allocation 193 // for user's convenience fill the sqes from the indexes 194 static inline void __fill(struct io_uring_sqe * out_sqes[], __u32 want, __u32 idxs[], struct $io_context * ctx) { 195 struct io_uring_sqe * sqes = ctx->sq.sqes; 196 for(i; want) { 197 __cfadbg_print_safe(io, "Kernel I/O : filling loop\n"); 198 out_sqes[i] = &sqes[idxs[i]]; 199 } 200 } 201 202 // Try to directly allocate from the a given context 203 // Not thread-safe 204 static inline bool __alloc(struct $io_context * ctx, __u32 idxs[], __u32 want) { 205 __sub_ring_t & sq = ctx->sq; 206 const __u32 mask = *sq.mask; 207 __u32 fhead = sq.free_ring.head; // get the current head of the queue 208 __u32 ftail = sq.free_ring.tail; // get the current tail of the queue 209 210 // If we don't have enough sqes, fail 211 if((ftail - fhead) < want) { return false; } 212 213 // copy all the indexes we want from the available list 214 for(i; want) { 215 __cfadbg_print_safe(io, "Kernel I/O : allocating loop\n"); 216 idxs[i] = sq.free_ring.array[(fhead + i) & mask]; 217 } 218 219 // Advance the head to mark the indexes as consumed 220 __atomic_store_n(&sq.free_ring.head, fhead + want, __ATOMIC_RELEASE); 221 222 // return success 223 return true; 224 } 341 225 342 226 // Allocate an submit queue entry. … … 345 229 // for convenience, return both the index and the pointer to the sqe 346 230 // sqe == &sqes[idx] 347 [* volatile struct io_uring_sqe, __u32] __submit_alloc( struct __io_data & ring, __u64 data ) { 348 /* paranoid */ verify( data != 0 ); 349 350 // Prepare the data we need 351 __attribute((unused)) int len = 0; 352 __attribute((unused)) int block = 0; 353 __u32 cnt = *ring.submit_q.num; 354 __u32 mask = *ring.submit_q.mask; 355 356 __u32 off = thread_rand(); 357 358 // Loop around looking for an available spot 359 for() { 360 // Look through the list starting at some offset 361 for(i; cnt) { 362 __u64 expected = 3; 363 __u32 idx = (i + off) & mask; // Get an index from a random 364 volatile struct io_uring_sqe * sqe = &ring.submit_q.sqes[idx]; 365 volatile __u64 * udata = &sqe->user_data; 366 367 // Allocate the entry by CASing the user_data field from 0 to the future address 368 if( *udata == expected && 369 __atomic_compare_exchange_n( udata, &expected, data, true, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED ) ) 370 { 371 // update statistics 372 __STATS__( false, 373 io.submit_q.alloc_avg.val += len; 374 io.submit_q.alloc_avg.block += block; 375 io.submit_q.alloc_avg.cnt += 1; 376 ) 377 378 // debug log 379 __cfadbg_print_safe( io, "Kernel I/O : allocated [%p, %u] for %p (%p)\n", sqe, idx, active_thread(), (void*)data ); 380 381 // Success return the data 382 return [sqe, idx]; 383 } 384 verify(expected != data); 385 386 // This one was used 387 len ++; 388 } 389 390 block++; 391 392 abort( "Kernel I/O : all submit queue entries used, yielding\n" ); 393 394 yield(); 395 } 396 } 397 398 static inline __u32 __submit_to_ready_array( struct __io_data & ring, __u32 idx, const __u32 mask ) { 399 /* paranoid */ verify( idx <= mask ); 400 /* paranoid */ verify( idx != -1ul32 ); 401 402 // We need to find a spot in the ready array 403 __attribute((unused)) int len = 0; 404 __attribute((unused)) int block = 0; 405 __u32 ready_mask = ring.submit_q.ready_cnt - 1; 406 407 __u32 off = thread_rand(); 408 409 __u32 picked; 410 LOOKING: for() { 411 for(i; ring.submit_q.ready_cnt) { 412 picked = (i + off) & ready_mask; 413 __u32 expected = -1ul32; 414 if( __atomic_compare_exchange_n( &ring.submit_q.ready[picked], &expected, idx, true, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED ) ) { 415 break LOOKING; 416 } 417 verify(expected != idx); 418 419 len ++; 420 } 421 422 block++; 423 424 __u32 released = __release_consumed_submission( ring ); 425 if( released == 0 ) { 426 yield(); 427 } 428 } 429 430 // update statistics 431 __STATS__( false, 432 io.submit_q.look_avg.val += len; 433 io.submit_q.look_avg.block += block; 434 io.submit_q.look_avg.cnt += 1; 435 ) 436 437 return picked; 438 } 439 440 void __submit( struct io_context * ctx, __u32 idx ) __attribute__((nonnull (1))) { 441 __io_data & ring = *ctx->thrd.ring; 442 231 struct $io_context * cfa_io_allocate(struct io_uring_sqe * sqes[], __u32 idxs[], __u32 want) { 232 __cfadbg_print_safe(io, "Kernel I/O : attempting to allocate %u\n", want); 233 234 disable_interrupts(); 235 processor * proc = __cfaabi_tls.this_processor; 236 $io_context * ctx = proc->io.ctx; 237 /* paranoid */ verify( __cfaabi_tls.this_processor ); 238 /* paranoid */ verify( ctx ); 239 240 __cfadbg_print_safe(io, "Kernel I/O : attempting to fast allocation\n"); 241 242 // We can proceed to the fast path 243 if( __alloc(ctx, idxs, want) ) { 244 // Allocation was successful 245 __STATS__( true, io.alloc.fast += 1; ) 246 enable_interrupts( __cfaabi_dbg_ctx ); 247 248 __cfadbg_print_safe(io, "Kernel I/O : fast allocation successful from ring %d\n", ctx->fd); 249 250 __fill( sqes, want, idxs, ctx ); 251 return ctx; 252 } 253 // The fast path failed, fallback 254 __STATS__( true, io.alloc.fail += 1; ) 255 256 // Fast path failed, fallback on arbitration 257 __STATS__( true, io.alloc.slow += 1; ) 258 enable_interrupts( __cfaabi_dbg_ctx ); 259 260 $io_arbiter * ioarb = proc->cltr->io.arbiter; 261 /* paranoid */ verify( ioarb ); 262 263 __cfadbg_print_safe(io, "Kernel I/O : falling back on arbiter for allocation\n"); 264 265 struct $io_context * ret = __ioarbiter_allocate(*ioarb, proc, idxs, want); 266 267 __cfadbg_print_safe(io, "Kernel I/O : slow allocation completed from ring %d\n", ret->fd); 268 269 __fill( sqes, want, idxs,ret ); 270 return ret; 271 } 272 273 274 //============================================================================================= 275 // submission 276 static inline void __submit( struct $io_context * ctx, __u32 idxs[], __u32 have, bool lazy) { 277 // We can proceed to the fast path 278 // Get the right objects 279 __sub_ring_t & sq = ctx->sq; 280 const __u32 mask = *sq.mask; 281 __u32 tail = *sq.kring.tail; 282 283 // Add the sqes to the array 284 for( i; have ) { 285 __cfadbg_print_safe(io, "Kernel I/O : __submit loop\n"); 286 sq.kring.array[ (tail + i) & mask ] = idxs[i]; 287 } 288 289 // Make the sqes visible to the submitter 290 __atomic_store_n(sq.kring.tail, tail + have, __ATOMIC_RELEASE); 291 sq.to_submit++; 292 293 ctx->proc->io.pending = true; 294 ctx->proc->io.dirty = true; 295 if(sq.to_submit > 30 || !lazy) { 296 __cfa_io_flush( ctx->proc ); 297 } 298 } 299 300 void cfa_io_submit( struct $io_context * inctx, __u32 idxs[], __u32 have, bool lazy ) __attribute__((nonnull (1))) { 301 __cfadbg_print_safe(io, "Kernel I/O : attempting to submit %u (%s)\n", have, lazy ? "lazy" : "eager"); 302 303 disable_interrupts(); 304 processor * proc = __cfaabi_tls.this_processor; 305 $io_context * ctx = proc->io.ctx; 306 /* paranoid */ verify( __cfaabi_tls.this_processor ); 307 /* paranoid */ verify( ctx ); 308 309 // Can we proceed to the fast path 310 if( ctx == inctx ) // We have the right instance? 443 311 { 444 __attribute__((unused)) volatile struct io_uring_sqe * sqe = &ring.submit_q.sqes[idx]; 445 __cfadbg_print_safe( io, 446 "Kernel I/O : submitting %u (%p) for %p\n" 447 " data: %p\n" 448 " opcode: %s\n" 449 " fd: %d\n" 450 " flags: %d\n" 451 " prio: %d\n" 452 " off: %p\n" 453 " addr: %p\n" 454 " len: %d\n" 455 " other flags: %d\n" 456 " splice fd: %d\n" 457 " pad[0]: %llu\n" 458 " pad[1]: %llu\n" 459 " pad[2]: %llu\n", 460 idx, sqe, 461 active_thread(), 462 (void*)sqe->user_data, 463 opcodes[sqe->opcode], 464 sqe->fd, 465 sqe->flags, 466 sqe->ioprio, 467 sqe->off, 468 sqe->addr, 469 sqe->len, 470 sqe->accept_flags, 471 sqe->splice_fd_in, 472 sqe->__pad2[0], 473 sqe->__pad2[1], 474 sqe->__pad2[2] 475 ); 476 } 477 478 479 // Get now the data we definetely need 480 volatile __u32 * const tail = ring.submit_q.tail; 481 const __u32 mask = *ring.submit_q.mask; 482 483 // There are 2 submission schemes, check which one we are using 484 if( ring.poller_submits ) { 485 // If the poller thread submits, then we just need to add this to the ready array 486 __submit_to_ready_array( ring, idx, mask ); 487 488 post( ctx->thrd.sem ); 489 490 __cfadbg_print_safe( io, "Kernel I/O : Added %u to ready for %p\n", idx, active_thread() ); 491 } 492 else if( ring.eager_submits ) { 493 __u32 picked = __submit_to_ready_array( ring, idx, mask ); 494 495 #if defined(LEADER_LOCK) 496 if( !try_lock(ring.submit_q.submit_lock) ) { 497 __STATS__( false, 498 io.submit_q.helped += 1; 499 ) 500 return; 501 } 502 /* paranoid */ verify( ! __preemption_enabled() ); 503 __STATS__( true, 504 io.submit_q.leader += 1; 505 ) 506 #else 507 for() { 508 yield(); 509 510 if( try_lock(ring.submit_q.submit_lock __cfaabi_dbg_ctx2) ) { 511 __STATS__( false, 512 io.submit_q.leader += 1; 513 ) 514 break; 515 } 516 517 // If some one else collected our index, we are done 518 #warning ABA problem 519 if( ring.submit_q.ready[picked] != idx ) { 520 __STATS__( false, 521 io.submit_q.helped += 1; 522 ) 523 return; 524 } 525 526 __STATS__( false, 527 io.submit_q.busy += 1; 528 ) 529 } 530 #endif 531 532 // We got the lock 533 // Collect the submissions 534 unsigned to_submit = __collect_submitions( ring ); 535 536 // Actually submit 537 int ret = __io_uring_enter( ring, to_submit, false ); 538 539 #if defined(LEADER_LOCK) 540 /* paranoid */ verify( ! __preemption_enabled() ); 541 next(ring.submit_q.submit_lock); 542 #else 543 unlock(ring.submit_q.submit_lock); 544 #endif 545 if( ret < 0 ) { 546 return; 547 } 548 549 // Release the consumed SQEs 550 __release_consumed_submission( ring ); 551 552 // update statistics 553 __STATS__( false, 554 io.submit_q.submit_avg.rdy += to_submit; 555 io.submit_q.submit_avg.csm += ret; 556 io.submit_q.submit_avg.cnt += 1; 557 ) 558 559 __cfadbg_print_safe( io, "Kernel I/O : submitted %u (among %u) for %p\n", idx, ret, active_thread() ); 560 } 561 else 562 { 563 // get mutual exclusion 564 #if defined(LEADER_LOCK) 565 while(!try_lock(ring.submit_q.submit_lock)); 566 #else 567 lock(ring.submit_q.submit_lock __cfaabi_dbg_ctx2); 568 #endif 569 570 /* paranoid */ verifyf( ring.submit_q.sqes[ idx ].user_data != 3ul64, 571 /* paranoid */ "index %u already reclaimed\n" 572 /* paranoid */ "head %u, prev %u, tail %u\n" 573 /* paranoid */ "[-0: %u,-1: %u,-2: %u,-3: %u]\n", 574 /* paranoid */ idx, 575 /* paranoid */ *ring.submit_q.head, ring.submit_q.prev_head, *tail 576 /* paranoid */ ,ring.submit_q.array[ ((*ring.submit_q.head) - 0) & (*ring.submit_q.mask) ] 577 /* paranoid */ ,ring.submit_q.array[ ((*ring.submit_q.head) - 1) & (*ring.submit_q.mask) ] 578 /* paranoid */ ,ring.submit_q.array[ ((*ring.submit_q.head) - 2) & (*ring.submit_q.mask) ] 579 /* paranoid */ ,ring.submit_q.array[ ((*ring.submit_q.head) - 3) & (*ring.submit_q.mask) ] 580 /* paranoid */ ); 581 582 // Append to the list of ready entries 583 584 /* paranoid */ verify( idx <= mask ); 585 ring.submit_q.array[ (*tail) & mask ] = idx; 586 __atomic_fetch_add(tail, 1ul32, __ATOMIC_SEQ_CST); 587 588 // Submit however, many entries need to be submitted 589 int ret = __io_uring_enter( ring, 1, false ); 590 if( ret < 0 ) { 591 switch((int)errno) { 592 default: 593 abort( "KERNEL ERROR: IO_URING SUBMIT - %s\n", strerror(errno) ); 594 } 595 } 596 597 /* paranoid */ verify(ret == 1); 598 599 // update statistics 600 __STATS__( false, 601 io.submit_q.submit_avg.csm += 1; 602 io.submit_q.submit_avg.cnt += 1; 603 ) 604 605 { 606 __attribute__((unused)) volatile __u32 * const head = ring.submit_q.head; 607 __attribute__((unused)) __u32 last_idx = ring.submit_q.array[ ((*head) - 1) & mask ]; 608 __attribute__((unused)) volatile struct io_uring_sqe * sqe = &ring.submit_q.sqes[last_idx]; 609 610 __cfadbg_print_safe( io, 611 "Kernel I/O : last submitted is %u (%p)\n" 612 " data: %p\n" 613 " opcode: %s\n" 614 " fd: %d\n" 615 " flags: %d\n" 616 " prio: %d\n" 617 " off: %p\n" 618 " addr: %p\n" 619 " len: %d\n" 620 " other flags: %d\n" 621 " splice fd: %d\n" 622 " pad[0]: %llu\n" 623 " pad[1]: %llu\n" 624 " pad[2]: %llu\n", 625 last_idx, sqe, 626 (void*)sqe->user_data, 627 opcodes[sqe->opcode], 628 sqe->fd, 629 sqe->flags, 630 sqe->ioprio, 631 sqe->off, 632 sqe->addr, 633 sqe->len, 634 sqe->accept_flags, 635 sqe->splice_fd_in, 636 sqe->__pad2[0], 637 sqe->__pad2[1], 638 sqe->__pad2[2] 639 ); 640 } 641 642 __atomic_thread_fence( __ATOMIC_SEQ_CST ); 643 // Release the consumed SQEs 644 __release_consumed_submission( ring ); 645 // ring.submit_q.sqes[idx].user_data = 3ul64; 646 647 #if defined(LEADER_LOCK) 648 next(ring.submit_q.submit_lock); 649 #else 650 unlock(ring.submit_q.submit_lock); 651 #endif 652 653 __cfadbg_print_safe( io, "Kernel I/O : submitted %u for %p\n", idx, active_thread() ); 654 } 655 } 656 657 // #define PARTIAL_SUBMIT 32 658 659 // go through the list of submissions in the ready array and moved them into 660 // the ring's submit queue 661 static unsigned __collect_submitions( struct __io_data & ring ) { 662 /* paranoid */ verify( ring.submit_q.ready != 0p ); 663 /* paranoid */ verify( ring.submit_q.ready_cnt > 0 ); 664 665 unsigned to_submit = 0; 666 __u32 tail = *ring.submit_q.tail; 667 const __u32 mask = *ring.submit_q.mask; 668 #if defined(PARTIAL_SUBMIT) 669 #if defined(LEADER_LOCK) 670 #error PARTIAL_SUBMIT and LEADER_LOCK cannot co-exist 671 #endif 672 const __u32 cnt = ring.submit_q.ready_cnt > PARTIAL_SUBMIT ? PARTIAL_SUBMIT : ring.submit_q.ready_cnt; 673 const __u32 offset = ring.submit_q.prev_ready; 674 ring.submit_q.prev_ready += cnt; 675 #else 676 const __u32 cnt = ring.submit_q.ready_cnt; 677 const __u32 offset = 0; 678 #endif 679 680 // Go through the list of ready submissions 681 for( c; cnt ) { 682 __u32 i = (offset + c) % ring.submit_q.ready_cnt; 683 684 // replace any submission with the sentinel, to consume it. 685 __u32 idx = __atomic_exchange_n( &ring.submit_q.ready[i], -1ul32, __ATOMIC_RELAXED); 686 687 // If it was already the sentinel, then we are done 688 if( idx == -1ul32 ) continue; 689 690 // If we got a real submission, append it to the list 691 ring.submit_q.array[ (tail + to_submit) & mask ] = idx & mask; 692 to_submit++; 693 } 694 695 // Increment the tail based on how many we are ready to submit 696 __atomic_fetch_add(ring.submit_q.tail, to_submit, __ATOMIC_SEQ_CST); 697 698 return to_submit; 699 } 700 312 __submit(ctx, idxs, have, lazy); 313 314 // Mark the instance as no longer in-use, re-enable interrupts and return 315 __STATS__( true, io.submit.fast += 1; ) 316 enable_interrupts( __cfaabi_dbg_ctx ); 317 318 __cfadbg_print_safe(io, "Kernel I/O : submitted on fast path\n"); 319 return; 320 } 321 322 // Fast path failed, fallback on arbitration 323 __STATS__( true, io.submit.slow += 1; ) 324 enable_interrupts( __cfaabi_dbg_ctx ); 325 326 __cfadbg_print_safe(io, "Kernel I/O : falling back on arbiter for submission\n"); 327 328 __ioarbiter_submit(*inctx->arbiter, inctx, idxs, have, lazy); 329 } 330 331 //============================================================================================= 332 // Flushing 701 333 // Go through the ring's submit queue and release everything that has already been consumed 702 334 // by io_uring 703 static __u32 __release_consumed_submission( struct __io_data & ring ) { 704 const __u32 smask = *ring.submit_q.mask; 705 706 // We need to get the lock to copy the old head and new head 707 if( !try_lock(ring.submit_q.release_lock __cfaabi_dbg_ctx2) ) return 0; 335 // This cannot be done by multiple threads 336 static __u32 __release_sqes( struct $io_context & ctx ) { 337 const __u32 mask = *ctx.sq.mask; 338 708 339 __attribute__((unused)) 709 __u32 ctail = * ring.submit_q.tail;// get the current tail of the queue710 __u32 chead = * ring.submit_q.head;// get the current head of the queue711 __u32 phead = ring.submit_q.prev_head;// get the head the last time we were here712 ring.submit_q.prev_head = chead; // note up to were we processed 713 unlock(ring.submit_q.release_lock);340 __u32 ctail = *ctx.sq.kring.tail; // get the current tail of the queue 341 __u32 chead = *ctx.sq.kring.head; // get the current head of the queue 342 __u32 phead = ctx.sq.kring.released; // get the head the last time we were here 343 344 __u32 ftail = ctx.sq.free_ring.tail; // get the current tail of the queue 714 345 715 346 // the 3 fields are organized like this diagram … … 730 361 __u32 count = chead - phead; 731 362 363 if(count == 0) { 364 return 0; 365 } 366 732 367 // We acquired an previous-head/current-head range 733 368 // go through the range and release the sqes 734 369 for( i; count ) { 735 __u32 idx = ring.submit_q.array[ (phead + i) & smask ]; 736 737 /* paranoid */ verify( 0 != ring.submit_q.sqes[ idx ].user_data ); 738 __clean( &ring.submit_q.sqes[ idx ] ); 739 } 370 __cfadbg_print_safe(io, "Kernel I/O : release loop\n"); 371 __u32 idx = ctx.sq.kring.array[ (phead + i) & mask ]; 372 ctx.sq.free_ring.array[ (ftail + i) & mask ] = idx; 373 } 374 375 ctx.sq.kring.released = chead; // note up to were we processed 376 __atomic_store_n(&ctx.sq.free_ring.tail, ftail + count, __ATOMIC_SEQ_CST); 377 378 __ioarbiter_notify(ctx); 379 740 380 return count; 741 381 } 742 382 743 void __sqe_clean( volatile struct io_uring_sqe * sqe ) { 744 __clean( sqe ); 745 } 746 747 static inline void __clean( volatile struct io_uring_sqe * sqe ) { 748 // If we are in debug mode, thrash the fields to make sure we catch reclamation errors 749 __cfaabi_dbg_debug_do( 750 memset(sqe, 0xde, sizeof(*sqe)); 751 sqe->opcode = (sizeof(opcodes) / sizeof(const char *)) - 1; 752 ); 753 754 // Mark the entry as unused 755 __atomic_store_n(&sqe->user_data, 3ul64, __ATOMIC_SEQ_CST); 383 //============================================================================================= 384 // I/O Arbiter 385 //============================================================================================= 386 static $io_context * __ioarbiter_allocate( $io_arbiter & mutex this, processor * proc, __u32 idxs[], __u32 want ) { 387 __cfadbg_print_safe(io, "Kernel I/O : arbiter allocating\n"); 388 389 __STATS__( false, io.alloc.block += 1; ) 390 391 // No one has any resources left, wait for something to finish 392 // Mark as pending 393 __atomic_store_n( &this.pending.flag, true, __ATOMIC_SEQ_CST ); 394 395 // Wait for our turn to submit 396 wait( this.pending.blocked, want ); 397 398 __attribute((unused)) bool ret = 399 __alloc( this.pending.ctx, idxs, want); 400 /* paranoid */ verify( ret ); 401 402 return this.pending.ctx; 403 404 } 405 406 static void __ioarbiter_notify( $io_arbiter & mutex this, $io_context * ctx ) { 407 /* paranoid */ verify( !is_empty(this.pending.blocked) ); 408 this.pending.ctx = ctx; 409 410 while( !is_empty(this.pending.blocked) ) { 411 __cfadbg_print_safe(io, "Kernel I/O : notifying\n"); 412 __u32 have = ctx->sq.free_ring.tail - ctx->sq.free_ring.head; 413 __u32 want = front( this.pending.blocked ); 414 415 if( have > want ) return; 416 417 signal_block( this.pending.blocked ); 418 } 419 420 this.pending.flag = false; 421 } 422 423 static void __ioarbiter_notify( $io_context & ctx ) { 424 if(__atomic_load_n( &ctx.arbiter->pending.flag, __ATOMIC_SEQ_CST)) { 425 __ioarbiter_notify( *ctx.arbiter, &ctx ); 426 } 427 } 428 429 // Simply append to the pending 430 static void __ioarbiter_submit( $io_arbiter & mutex this, $io_context * ctx, __u32 idxs[], __u32 have, bool lazy ) { 431 __cfadbg_print_safe(io, "Kernel I/O : submitting %u from the arbiter to context %u\n", have, ctx->fd); 432 433 /* paranoid */ verify( &this == ctx->arbiter ); 434 435 // Mark as pending 436 __atomic_store_n( &ctx->ext_sq.empty, false, __ATOMIC_SEQ_CST ); 437 438 __cfadbg_print_safe(io, "Kernel I/O : waiting to submit %u\n", have); 439 440 // Wait for our turn to submit 441 wait( ctx->ext_sq.blocked ); 442 443 // Submit our indexes 444 __submit(ctx, idxs, have, lazy); 445 446 __cfadbg_print_safe(io, "Kernel I/O : %u submitted from arbiter\n", have); 447 } 448 449 static void __ioarbiter_flush( $io_arbiter & mutex this, $io_context * ctx ) { 450 /* paranoid */ verify( &this == ctx->arbiter ); 451 452 __STATS__( false, io.flush.external += 1; ) 453 454 __cfadbg_print_safe(io, "Kernel I/O : arbiter flushing\n"); 455 456 condition & blcked = ctx->ext_sq.blocked; 457 /* paranoid */ verify( ctx->ext_sq.empty == is_empty( blcked ) ); 458 while(!is_empty( blcked )) { 459 signal_block( blcked ); 460 } 461 462 ctx->ext_sq.empty = true; 756 463 } 757 464 #endif -
libcfa/src/concurrency/io/call.cfa.in
r342af53 r8e4aa05 54 54 | IOSQE_IO_DRAIN 55 55 #endif 56 #if defined(CFA_HAVE_IOSQE_IO_LINK) 57 | IOSQE_IO_LINK 58 #endif 59 #if defined(CFA_HAVE_IOSQE_IO_HARDLINK) 60 | IOSQE_IO_HARDLINK 61 #endif 56 62 #if defined(CFA_HAVE_IOSQE_ASYNC) 57 63 | IOSQE_ASYNC 58 64 #endif 59 ; 60 61 static const __u32 LINK_FLAGS = 0 62 #if defined(CFA_HAVE_IOSQE_IO_LINK) 63 | IOSQE_IO_LINK 64 #endif 65 #if defined(CFA_HAVE_IOSQE_IO_HARDLINK) 66 | IOSQE_IO_HARDLINK 65 #if defined(CFA_HAVE_IOSQE_BUFFER_SELECTED) 66 | IOSQE_BUFFER_SELECTED 67 67 #endif 68 68 ; … … 74 74 ; 75 75 76 extern [* volatile struct io_uring_sqe, __u32] __submit_alloc( struct __io_data & ring, __u64 data ); 77 extern void __submit( struct io_context * ctx, __u32 idx ) __attribute__((nonnull (1))); 78 79 static inline io_context * __get_io_context( void ) { 80 cluster * cltr = active_cluster(); 81 82 /* paranoid */ verifyf( cltr, "No active cluster for io operation\\n"); 83 assertf( cltr->io.cnt > 0, "Cluster %p has no default io contexts and no context was specified\\n", cltr ); 84 85 /* paranoid */ verifyf( cltr->io.ctxs, "default io contexts for cluster %p are missing\\n", cltr); 86 return &cltr->io.ctxs[ thread_rand() % cltr->io.cnt ]; 87 } 76 extern struct $io_context * cfa_io_allocate(struct io_uring_sqe * out_sqes[], __u32 out_idxs[], __u32 want) __attribute__((nonnull (1,2))); 77 extern void cfa_io_submit( struct $io_context * in_ctx, __u32 in_idxs[], __u32 have, bool lazy ) __attribute__((nonnull (1,2))); 88 78 #endif 89 79 … … 98 88 99 89 extern "C" { 100 #include < sys/types.h>90 #include <asm/types.h> 101 91 #include <sys/socket.h> 102 92 #include <sys/syscall.h> … … 142 132 extern int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 143 133 144 extern ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);134 extern ssize_t splice(int fd_in, __off64_t *off_in, int fd_out, __off64_t *off_out, size_t len, unsigned int flags); 145 135 extern ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags); 146 136 } … … 195 185 return ', '.join(args_a) 196 186 197 AsyncTemplate = """inline void async_{name}(io_future_t & future, {params}, int submit_flags, io_cancellation * cancellation, io_context * context) {{187 AsyncTemplate = """inline void async_{name}(io_future_t & future, {params}, __u64 submit_flags) {{ 198 188 #if !defined(CFA_HAVE_LINUX_IO_URING_H) || !defined(CFA_HAVE_IORING_OP_{op}) 199 189 ssize_t res = {name}({args}); … … 205 195 }} 206 196 #else 207 // we don't support LINK yet208 if( 0 != (submit_flags & LINK_FLAGS) ) {{209 errno = ENOTSUP; return -1;210 }}211 212 if( !context ) {{213 context = __get_io_context();214 }}215 if(cancellation) {{216 cancellation->target = (__u64)(uintptr_t)&future;217 }}218 219 197 __u8 sflags = REGULAR_FLAGS & submit_flags; 220 struct __io_data & ring = *context->thrd.ring;221 222 198 __u32 idx; 223 199 struct io_uring_sqe * sqe; 224 [(volatile struct io_uring_sqe *) sqe, idx] = __submit_alloc( ring, (__u64)(uintptr_t)&future);200 struct $io_context * ctx = cfa_io_allocate( &sqe, &idx, 1 ); 225 201 226 202 sqe->opcode = IORING_OP_{op}; 203 sqe->user_data = (__u64)(uintptr_t)&future; 227 204 sqe->flags = sflags; 228 205 sqe->ioprio = 0; … … 239 216 240 217 verify( sqe->user_data == (__u64)(uintptr_t)&future ); 241 __submit( context, idx);218 cfa_io_submit( ctx, &idx, 1, 0 != (submit_flags & CFA_IO_LAZY) ); 242 219 #endif 243 220 }}""" 244 221 245 SyncTemplate = """{ret} cfa_{name}({params}, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context) {{ 246 if( timeout >= 0 ) {{ 247 errno = ENOTSUP; 248 return -1; 249 }} 222 SyncTemplate = """{ret} cfa_{name}({params}, __u64 submit_flags) {{ 250 223 io_future_t future; 251 224 252 async_{name}( future, {args}, submit_flags , cancellation, context);225 async_{name}( future, {args}, submit_flags ); 253 226 254 227 wait( future ); … … 393 366 }), 394 367 # CFA_HAVE_IORING_OP_SPLICE 395 Call('SPLICE', 'ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags)', {368 Call('SPLICE', 'ssize_t splice(int fd_in, __off64_t *off_in, int fd_out, __off64_t *off_out, size_t len, unsigned int flags)', { 396 369 'splice_fd_in': 'fd_in', 397 370 'splice_off_in': 'off_in ? (__u64)*off_in : (__u64)-1', … … 415 388 if c.define: 416 389 print("""#if defined({define}) 417 {ret} cfa_{name}({params}, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);390 {ret} cfa_{name}({params}, __u64 submit_flags); 418 391 #endif""".format(define=c.define,ret=c.ret, name=c.name, params=c.params)) 419 392 else: 420 print("{ret} cfa_{name}({params}, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);"393 print("{ret} cfa_{name}({params}, __u64 submit_flags);" 421 394 .format(ret=c.ret, name=c.name, params=c.params)) 422 395 … … 426 399 if c.define: 427 400 print("""#if defined({define}) 428 void async_{name}(io_future_t & future, {params}, int submit_flags, io_cancellation * cancellation, io_context * context);401 void async_{name}(io_future_t & future, {params}, __u64 submit_flags); 429 402 #endif""".format(define=c.define,name=c.name, params=c.params)) 430 403 else: 431 print("void async_{name}(io_future_t & future, {params}, int submit_flags, io_cancellation * cancellation, io_context * context);"404 print("void async_{name}(io_future_t & future, {params}, __u64 submit_flags);" 432 405 .format(name=c.name, params=c.params)) 433 406 print("\n") … … 474 447 475 448 print(""" 476 //-----------------------------------------------------------------------------477 bool cancel(io_cancellation & this) {478 #if !defined(CFA_HAVE_LINUX_IO_URING_H) || !defined(CFA_HAVE_IORING_OP_ASYNC_CANCEL)479 return false;480 #else481 io_future_t future;482 483 io_context * context = __get_io_context();484 485 __u8 sflags = 0;486 struct __io_data & ring = *context->thrd.ring;487 488 __u32 idx;489 volatile struct io_uring_sqe * sqe;490 [sqe, idx] = __submit_alloc( ring, (__u64)(uintptr_t)&future );491 492 sqe->__pad2[0] = sqe->__pad2[1] = sqe->__pad2[2] = 0;493 sqe->opcode = IORING_OP_ASYNC_CANCEL;494 sqe->flags = sflags;495 sqe->addr = this.target;496 497 verify( sqe->user_data == (__u64)(uintptr_t)&future );498 __submit( context, idx );499 500 wait(future);501 502 if( future.result == 0 ) return true; // Entry found503 if( future.result == -EALREADY) return true; // Entry found but in progress504 if( future.result == -ENOENT ) return false; // Entry not found505 return false;506 #endif507 }508 509 449 //----------------------------------------------------------------------------- 510 450 // Check if a function is has asynchronous -
libcfa/src/concurrency/io/setup.cfa
r342af53 r8e4aa05 26 26 27 27 #if !defined(CFA_HAVE_LINUX_IO_URING_H) 28 void __kernel_io_startup() {29 // Nothing to do without io_uring30 }31 32 void __kernel_io_shutdown() {33 // Nothing to do without io_uring34 }35 36 28 void ?{}(io_context_params & this) {} 37 29 38 void ?{}(io_context & this, struct cluster & cl) {} 39 void ?{}(io_context & this, struct cluster & cl, const io_context_params & params) {} 40 41 void ^?{}(io_context & this) {} 42 void ^?{}(io_context & this, bool cluster_context) {} 30 void ?{}($io_context & this, struct cluster & cl) {} 31 void ^?{}($io_context & this) {} 32 33 void __cfa_io_start( processor * proc ) {} 34 void __cfa_io_flush( processor * proc ) {} 35 void __cfa_io_stop ( processor * proc ) {} 36 37 $io_arbiter * create(void) { return 0p; } 38 void destroy($io_arbiter *) {} 43 39 44 40 #else … … 65 61 void ?{}(io_context_params & this) { 66 62 this.num_entries = 256; 67 this.num_ready = 256;68 this.submit_aff = -1;69 this.eager_submits = false;70 this.poller_submits = false;71 this.poll_submit = false;72 this.poll_complete = false;73 63 } 74 64 … … 103 93 104 94 //============================================================================================= 105 // I/O Startup / Shutdown logic + Master Poller106 //=============================================================================================107 108 // IO Master poller loop forward109 static void * iopoll_loop( __attribute__((unused)) void * args );110 111 static struct {112 pthread_t thrd; // pthread handle to io poller thread113 void * stack; // pthread stack for io poller thread114 int epollfd; // file descriptor to the epoll instance115 volatile bool run; // Whether or not to continue116 } iopoll;117 118 void __kernel_io_startup(void) {119 __cfadbg_print_safe(io_core, "Kernel : Creating EPOLL instance\n" );120 121 iopoll.epollfd = epoll_create1(0);122 if (iopoll.epollfd == -1) {123 abort( "internal error, epoll_create1\n");124 }125 126 __cfadbg_print_safe(io_core, "Kernel : Starting io poller thread\n" );127 128 iopoll.run = true;129 iopoll.stack = __create_pthread( &iopoll.thrd, iopoll_loop, 0p );130 }131 132 void __kernel_io_shutdown(void) {133 // Notify the io poller thread of the shutdown134 iopoll.run = false;135 sigval val = { 1 };136 pthread_sigqueue( iopoll.thrd, SIGUSR1, val );137 138 // Wait for the io poller thread to finish139 140 __destroy_pthread( iopoll.thrd, iopoll.stack, 0p );141 142 int ret = close(iopoll.epollfd);143 if (ret == -1) {144 abort( "internal error, close epoll\n");145 }146 147 // Io polling is now fully stopped148 149 __cfadbg_print_safe(io_core, "Kernel : IO poller stopped\n" );150 }151 152 static void * iopoll_loop( __attribute__((unused)) void * args ) {153 __processor_id_t id;154 id.full_proc = false;155 id.id = doregister(&id);156 __cfaabi_tls.this_proc_id = &id;157 __cfadbg_print_safe(io_core, "Kernel : IO poller thread starting\n" );158 159 // Block signals to control when they arrive160 sigset_t mask;161 sigfillset(&mask);162 if ( pthread_sigmask( SIG_BLOCK, &mask, 0p ) == -1 ) {163 abort( "internal error, pthread_sigmask" );164 }165 166 sigdelset( &mask, SIGUSR1 );167 168 // Create sufficient events169 struct epoll_event events[10];170 // Main loop171 while( iopoll.run ) {172 __cfadbg_print_safe(io_core, "Kernel I/O - epoll : waiting on io_uring contexts\n");173 174 // Wait for events175 int nfds = epoll_pwait( iopoll.epollfd, events, 10, -1, &mask );176 177 __cfadbg_print_safe(io_core, "Kernel I/O - epoll : %d io contexts events, waking up\n", nfds);178 179 // Check if an error occured180 if (nfds == -1) {181 if( errno == EINTR ) continue;182 abort( "internal error, pthread_sigmask" );183 }184 185 for(i; nfds) {186 $io_ctx_thread * io_ctx = ($io_ctx_thread *)(uintptr_t)events[i].data.u64;187 /* paranoid */ verify( io_ctx );188 __cfadbg_print_safe(io_core, "Kernel I/O - epoll : Unparking io poller %d (%p)\n", io_ctx->ring->fd, io_ctx);189 #if !defined( __CFA_NO_STATISTICS__ )190 __cfaabi_tls.this_stats = io_ctx->self.curr_cluster->stats;191 #endif192 193 eventfd_t v;194 eventfd_read(io_ctx->ring->efd, &v);195 196 post( io_ctx->sem );197 }198 }199 200 __cfadbg_print_safe(io_core, "Kernel : IO poller thread stopping\n" );201 unregister(&id);202 return 0p;203 }204 205 //=============================================================================================206 95 // I/O Context Constrution/Destruction 207 96 //============================================================================================= 208 97 209 void ?{}($io_ctx_thread & this, struct cluster & cl) { (this.self){ "IO Poller", cl }; } 210 void main( $io_ctx_thread & this ); 211 static inline $thread * get_thread( $io_ctx_thread & this ) { return &this.self; } 212 void ^?{}( $io_ctx_thread & mutex this ) {} 213 214 static void __io_create ( __io_data & this, const io_context_params & params_in ); 215 static void __io_destroy( __io_data & this ); 216 217 void ?{}(io_context & this, struct cluster & cl, const io_context_params & params) { 218 (this.thrd){ cl }; 219 this.thrd.ring = malloc(); 220 __cfadbg_print_safe(io_core, "Kernel I/O : Creating ring for io_context %p\n", &this); 221 __io_create( *this.thrd.ring, params ); 222 223 __cfadbg_print_safe(io_core, "Kernel I/O : Starting poller thread for io_context %p\n", &this); 224 this.thrd.done = false; 225 __thrd_start( this.thrd, main ); 226 227 __cfadbg_print_safe(io_core, "Kernel I/O : io_context %p ready\n", &this); 228 } 229 230 void ?{}(io_context & this, struct cluster & cl) { 231 io_context_params params; 232 (this){ cl, params }; 233 } 234 235 void ^?{}(io_context & this, bool cluster_context) { 236 __cfadbg_print_safe(io_core, "Kernel I/O : tearing down io_context %p\n", &this); 237 238 // Notify the thread of the shutdown 239 __atomic_store_n(&this.thrd.done, true, __ATOMIC_SEQ_CST); 240 241 // If this is an io_context within a cluster, things get trickier 242 $thread & thrd = this.thrd.self; 243 if( cluster_context ) { 244 // We are about to do weird things with the threads 245 // we don't need interrupts to complicate everything 246 disable_interrupts(); 247 248 // Get cluster info 249 cluster & cltr = *thrd.curr_cluster; 250 /* paranoid */ verify( cltr.idles.total == 0 || &cltr == mainCluster ); 251 /* paranoid */ verify( !ready_mutate_islocked() ); 252 253 // We need to adjust the clean-up based on where the thread is 254 if( thrd.state == Ready || thrd.preempted != __NO_PREEMPTION ) { 255 // This is the tricky case 256 // The thread was preempted or ready to run and now it is on the ready queue 257 // but the cluster is shutting down, so there aren't any processors to run the ready queue 258 // the solution is to steal the thread from the ready-queue and pretend it was blocked all along 259 260 ready_schedule_lock(); 261 // The thread should on the list 262 /* paranoid */ verify( thrd.link.next != 0p ); 263 264 // Remove the thread from the ready queue of this cluster 265 // The thread should be the last on the list 266 __attribute__((unused)) bool removed = remove_head( &cltr, &thrd ); 267 /* paranoid */ verify( removed ); 268 thrd.link.next = 0p; 269 thrd.link.prev = 0p; 270 271 // Fixup the thread state 272 thrd.state = Blocked; 273 thrd.ticket = TICKET_BLOCKED; 274 thrd.preempted = __NO_PREEMPTION; 275 276 ready_schedule_unlock(); 277 278 // Pretend like the thread was blocked all along 279 } 280 // !!! This is not an else if !!! 281 // Ok, now the thread is blocked (whether we cheated to get here or not) 282 if( thrd.state == Blocked ) { 283 // This is the "easy case" 284 // The thread is parked and can easily be moved to active cluster 285 verify( thrd.curr_cluster != active_cluster() || thrd.curr_cluster == mainCluster ); 286 thrd.curr_cluster = active_cluster(); 287 288 // unpark the fast io_poller 289 unpark( &thrd ); 290 } 291 else { 292 // The thread is in a weird state 293 // I don't know what to do here 294 abort("io_context poller thread is in unexpected state, cannot clean-up correctly\n"); 295 } 296 297 // The weird thread kidnapping stuff is over, restore interrupts. 298 enable_interrupts( __cfaabi_dbg_ctx ); 299 } else { 300 post( this.thrd.sem ); 301 } 302 303 ^(this.thrd){}; 304 __cfadbg_print_safe(io_core, "Kernel I/O : Stopped poller thread for io_context %p\n", &this); 305 306 __io_destroy( *this.thrd.ring ); 307 __cfadbg_print_safe(io_core, "Kernel I/O : Destroyed ring for io_context %p\n", &this); 308 309 free(this.thrd.ring); 310 } 311 312 void ^?{}(io_context & this) { 313 ^(this){ false }; 314 } 315 316 static void __io_create( __io_data & this, const io_context_params & params_in ) { 98 99 100 static void __io_uring_setup ( $io_context & this, const io_context_params & params_in, int procfd ); 101 static void __io_uring_teardown( $io_context & this ); 102 static void __epoll_register($io_context & ctx); 103 static void __epoll_unregister($io_context & ctx); 104 void __ioarbiter_register( $io_arbiter & mutex, $io_context & ctx ); 105 void __ioarbiter_unregister( $io_arbiter & mutex, $io_context & ctx ); 106 107 void ?{}($io_context & this, processor * proc, struct cluster & cl) { 108 /* paranoid */ verify( cl.io.arbiter ); 109 this.proc = proc; 110 this.arbiter = cl.io.arbiter; 111 this.ext_sq.empty = true; 112 (this.ext_sq.blocked){}; 113 __io_uring_setup( this, cl.io.params, proc->idle ); 114 __cfadbg_print_safe(io_core, "Kernel I/O : Created ring for io_context %u (%p)\n", this.fd, &this); 115 } 116 117 void ^?{}($io_context & this) { 118 __cfadbg_print_safe(io_core, "Kernel I/O : tearing down io_context %u\n", this.fd); 119 120 __io_uring_teardown( this ); 121 __cfadbg_print_safe(io_core, "Kernel I/O : Destroyed ring for io_context %u\n", this.fd); 122 } 123 124 extern void __disable_interrupts_hard(); 125 extern void __enable_interrupts_hard(); 126 127 static void __io_uring_setup( $io_context & this, const io_context_params & params_in, int procfd ) { 317 128 // Step 1 : call to setup 318 129 struct io_uring_params params; 319 130 memset(¶ms, 0, sizeof(params)); 320 if( params_in.poll_submit ) params.flags |= IORING_SETUP_SQPOLL;321 if( params_in.poll_complete ) params.flags |= IORING_SETUP_IOPOLL;131 // if( params_in.poll_submit ) params.flags |= IORING_SETUP_SQPOLL; 132 // if( params_in.poll_complete ) params.flags |= IORING_SETUP_IOPOLL; 322 133 323 134 __u32 nentries = params_in.num_entries != 0 ? params_in.num_entries : 256; … … 325 136 abort("ERROR: I/O setup 'num_entries' must be a power of 2\n"); 326 137 } 327 if( params_in.poller_submits && params_in.eager_submits ) {328 abort("ERROR: I/O setup 'poller_submits' and 'eager_submits' cannot be used together\n");329 }330 138 331 139 int fd = syscall(__NR_io_uring_setup, nentries, ¶ms ); … … 335 143 336 144 // Step 2 : mmap result 337 memset( &this, 0, sizeof(struct __io_data) ); 338 struct __submition_data & sq = this.submit_q; 339 struct __completion_data & cq = this.completion_q; 145 struct __sub_ring_t & sq = this.sq; 146 struct __cmp_ring_t & cq = this.cq; 340 147 341 148 // calculate the right ring size … … 386 193 // Get the pointers from the kernel to fill the structure 387 194 // submit queue 388 sq.head = (volatile __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.head); 389 sq.tail = (volatile __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.tail); 390 sq.mask = ( const __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.ring_mask); 391 sq.num = ( const __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.ring_entries); 392 sq.flags = ( __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.flags); 393 sq.dropped = ( __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.dropped); 394 sq.array = ( __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.array); 395 sq.prev_head = *sq.head; 396 397 { 398 const __u32 num = *sq.num; 399 for( i; num ) { 400 __sqe_clean( &sq.sqes[i] ); 401 } 402 } 403 404 (sq.submit_lock){}; 405 (sq.release_lock){}; 406 407 if( params_in.poller_submits || params_in.eager_submits ) { 408 /* paranoid */ verify( is_pow2( params_in.num_ready ) || (params_in.num_ready < 8) ); 409 sq.ready_cnt = max( params_in.num_ready, 8 ); 410 sq.ready = alloc( sq.ready_cnt, 64`align ); 411 for(i; sq.ready_cnt) { 412 sq.ready[i] = -1ul32; 413 } 414 sq.prev_ready = 0; 415 } 416 else { 417 sq.ready_cnt = 0; 418 sq.ready = 0p; 419 sq.prev_ready = 0; 420 } 195 sq.kring.head = (volatile __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.head); 196 sq.kring.tail = (volatile __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.tail); 197 sq.kring.array = ( __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.array); 198 sq.mask = ( const __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.ring_mask); 199 sq.num = ( const __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.ring_entries); 200 sq.flags = ( __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.flags); 201 sq.dropped = ( __u32 *)(((intptr_t)sq.ring_ptr) + params.sq_off.dropped); 202 203 sq.kring.released = 0; 204 205 sq.free_ring.head = 0; 206 sq.free_ring.tail = *sq.num; 207 sq.free_ring.array = alloc( *sq.num, 128`align ); 208 for(i; (__u32)*sq.num) { 209 sq.free_ring.array[i] = i; 210 } 211 212 sq.to_submit = 0; 421 213 422 214 // completion queue … … 429 221 430 222 // Step 4 : eventfd 431 int efd; 432 for() { 433 efd = eventfd(0, 0); 434 if (efd < 0) { 435 if (errno == EINTR) continue; 436 abort("KERNEL ERROR: IO_URING EVENTFD - %s\n", strerror(errno)); 437 } 438 break; 439 } 440 441 int ret; 442 for() { 443 ret = syscall( __NR_io_uring_register, fd, IORING_REGISTER_EVENTFD, &efd, 1); 444 if (ret < 0) { 445 if (errno == EINTR) continue; 446 abort("KERNEL ERROR: IO_URING EVENTFD REGISTER - %s\n", strerror(errno)); 447 } 448 break; 449 } 223 // io_uring_register is so f*cking slow on some machine that it 224 // will never succeed if preemption isn't hard blocked 225 __cfadbg_print_safe(io_core, "Kernel I/O : registering %d for completion with ring %d\n", procfd, fd); 226 227 __disable_interrupts_hard(); 228 229 int ret = syscall( __NR_io_uring_register, fd, IORING_REGISTER_EVENTFD, &procfd, 1); 230 if (ret < 0) { 231 abort("KERNEL ERROR: IO_URING EVENTFD REGISTER - %s\n", strerror(errno)); 232 } 233 234 __enable_interrupts_hard(); 235 236 __cfadbg_print_safe(io_core, "Kernel I/O : registered %d for completion with ring %d\n", procfd, fd); 450 237 451 238 // some paranoid checks … … 457 244 /* paranoid */ verifyf( (*sq.mask) == ((*sq.num) - 1ul32), "IO_URING Expected mask to be %u (%u entries), was %u", (*sq.num) - 1ul32, *sq.num, *sq.mask ); 458 245 /* paranoid */ verifyf( (*sq.num) >= nentries, "IO_URING Expected %u entries, got %u", nentries, *sq.num ); 459 /* paranoid */ verifyf( (*sq. head) == 0, "IO_URING Expected head to be 0, got %u", *sq.head );460 /* paranoid */ verifyf( (*sq. tail) == 0, "IO_URING Expected tail to be 0, got %u", *sq.tail );246 /* paranoid */ verifyf( (*sq.kring.head) == 0, "IO_URING Expected head to be 0, got %u", *sq.kring.head ); 247 /* paranoid */ verifyf( (*sq.kring.tail) == 0, "IO_URING Expected tail to be 0, got %u", *sq.kring.tail ); 461 248 462 249 // Update the global ring info 463 this.ring_flags = params.flags;250 this.ring_flags = 0; 464 251 this.fd = fd; 465 this.efd = efd; 466 this.eager_submits = params_in.eager_submits; 467 this.poller_submits = params_in.poller_submits; 468 } 469 470 static void __io_destroy( __io_data & this ) { 252 } 253 254 static void __io_uring_teardown( $io_context & this ) { 471 255 // Shutdown the io rings 472 struct __sub mition_data & sq = this.submit_q;473 struct __c ompletion_data & cq = this.completion_q;256 struct __sub_ring_t & sq = this.sq; 257 struct __cmp_ring_t & cq = this.cq; 474 258 475 259 // unmap the submit queue entries … … 486 270 // close the file descriptor 487 271 close(this.fd); 488 close(this.efd); 489 490 free( this.submit_q.ready ); // Maybe null, doesn't matter 272 273 free( this.sq.free_ring.array ); // Maybe null, doesn't matter 274 } 275 276 void __cfa_io_start( processor * proc ) { 277 proc->io.ctx = alloc(); 278 (*proc->io.ctx){proc, *proc->cltr}; 279 } 280 void __cfa_io_stop ( processor * proc ) { 281 ^(*proc->io.ctx){}; 282 free(proc->io.ctx); 491 283 } 492 284 … … 494 286 // I/O Context Sleep 495 287 //============================================================================================= 496 #define IOEVENTS EPOLLIN | EPOLLONESHOT 497 498 static inline void __ioctx_epoll_ctl($io_ctx_thread & ctx, int op, const char * error) { 499 struct epoll_event ev; 500 ev.events = IOEVENTS; 501 ev.data.u64 = (__u64)&ctx; 502 int ret = epoll_ctl(iopoll.epollfd, op, ctx.ring->efd, &ev); 503 if (ret < 0) { 504 abort( "KERNEL ERROR: EPOLL %s - (%d) %s\n", error, (int)errno, strerror(errno) ); 505 } 506 } 507 508 void __ioctx_register($io_ctx_thread & ctx) { 509 __ioctx_epoll_ctl(ctx, EPOLL_CTL_ADD, "ADD"); 510 } 511 512 void __ioctx_prepare_block($io_ctx_thread & ctx) { 513 __cfadbg_print_safe(io_core, "Kernel I/O - epoll : Re-arming io poller %d (%p)\n", ctx.ring->fd, &ctx); 514 __ioctx_epoll_ctl(ctx, EPOLL_CTL_MOD, "REARM"); 515 } 288 // static inline void __epoll_ctl($io_context & ctx, int op, const char * error) { 289 // struct epoll_event ev; 290 // ev.events = EPOLLIN | EPOLLONESHOT; 291 // ev.data.u64 = (__u64)&ctx; 292 // int ret = epoll_ctl(iopoll.epollfd, op, ctx.efd, &ev); 293 // if (ret < 0) { 294 // abort( "KERNEL ERROR: EPOLL %s - (%d) %s\n", error, (int)errno, strerror(errno) ); 295 // } 296 // } 297 298 // static void __epoll_register($io_context & ctx) { 299 // __epoll_ctl(ctx, EPOLL_CTL_ADD, "ADD"); 300 // } 301 302 // static void __epoll_unregister($io_context & ctx) { 303 // // Read the current epoch so we know when to stop 304 // size_t curr = __atomic_load_n(&iopoll.epoch, __ATOMIC_SEQ_CST); 305 306 // // Remove the fd from the iopoller 307 // __epoll_ctl(ctx, EPOLL_CTL_DEL, "REMOVE"); 308 309 // // Notify the io poller thread of the shutdown 310 // iopoll.run = false; 311 // sigval val = { 1 }; 312 // pthread_sigqueue( iopoll.thrd, SIGUSR1, val ); 313 314 // // Make sure all this is done 315 // __atomic_thread_fence(__ATOMIC_SEQ_CST); 316 317 // // Wait for the next epoch 318 // while(curr == iopoll.epoch && !iopoll.stopped) Pause(); 319 // } 320 321 // void __ioctx_prepare_block($io_context & ctx) { 322 // __cfadbg_print_safe(io_core, "Kernel I/O - epoll : Re-arming io poller %d (%p)\n", ctx.fd, &ctx); 323 // __epoll_ctl(ctx, EPOLL_CTL_MOD, "REARM"); 324 // } 325 516 326 517 327 //============================================================================================= 518 328 // I/O Context Misc Setup 519 329 //============================================================================================= 520 void register_fixed_files( io_context & ctx, int * files, unsigned count ) { 521 int ret = syscall( __NR_io_uring_register, ctx.thrd.ring->fd, IORING_REGISTER_FILES, files, count ); 522 if( ret < 0 ) { 523 abort( "KERNEL ERROR: IO_URING SYSCALL - (%d) %s\n", (int)errno, strerror(errno) ); 524 } 525 526 __cfadbg_print_safe( io_core, "Kernel I/O : Performed io_register for %p, returned %d\n", active_thread(), ret ); 527 } 528 529 void register_fixed_files( cluster & cltr, int * files, unsigned count ) { 530 for(i; cltr.io.cnt) { 531 register_fixed_files( cltr.io.ctxs[i], files, count ); 532 } 533 } 330 void ?{}( $io_arbiter & this ) { 331 this.pending.flag = false; 332 } 333 334 void ^?{}( $io_arbiter & mutex this ) { 335 // /* paranoid */ verify( empty(this.assigned) ); 336 // /* paranoid */ verify( empty(this.available) ); 337 /* paranoid */ verify( is_empty(this.pending.blocked) ); 338 } 339 340 $io_arbiter * create(void) { 341 return new(); 342 } 343 void destroy($io_arbiter * arbiter) { 344 delete(arbiter); 345 } 346 347 //============================================================================================= 348 // I/O Context Misc Setup 349 //============================================================================================= 350 534 351 #endif -
libcfa/src/concurrency/io/types.hfa
r342af53 r8e4aa05 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // io/types.hfa -- 7 // io/types.hfa -- PRIVATE 8 // Types used by the I/O subsystem 8 9 // 9 10 // Author : Thierry Delisle … … 21 22 22 23 #include "bits/locks.hfa" 24 #include "kernel/fwd.hfa" 23 25 24 26 #if defined(CFA_HAVE_LINUX_IO_URING_H) 25 #define LEADER_LOCK 26 struct __leaderlock_t { 27 struct $thread * volatile value; // ($thread) next_leader | (bool:1) is_locked 28 }; 27 #include "bits/sequence.hfa" 28 #include "monitor.hfa" 29 29 30 static inline void ?{}( __leaderlock_t & this ) { this.value = 0p; } 30 struct processor; 31 monitor $io_arbiter; 31 32 32 33 //----------------------------------------------------------------------- 33 34 // Ring Data structure 34 struct __submition_data { 35 // Head and tail of the ring (associated with array) 36 volatile __u32 * head; 37 volatile __u32 * tail; 38 volatile __u32 prev_head; 35 struct __sub_ring_t { 36 struct { 37 // Head and tail of the ring (associated with array) 38 volatile __u32 * head; // one passed last index consumed by the kernel 39 volatile __u32 * tail; // one passed last index visible to the kernel 40 volatile __u32 released; // one passed last index released back to the free list 39 41 40 // The actual kernel ring which uses head/tail 41 // indexes into the sqes arrays 42 __u32 * array; 42 // The actual kernel ring which uses head/tail 43 // indexes into the sqes arrays 44 __u32 * array; 45 } kring; 46 47 struct { 48 volatile __u32 head; 49 volatile __u32 tail; 50 // The ring which contains free allocations 51 // indexes into the sqes arrays 52 __u32 * array; 53 } free_ring; 54 55 // number of sqes to submit on next system call. 56 __u32 to_submit; 43 57 44 58 // number of entries and mask to go with it … … 46 60 const __u32 * mask; 47 61 48 // Submission flags (Not sure what for)62 // Submission flags, currently only IORING_SETUP_SQPOLL 49 63 __u32 * flags; 50 64 51 // number of sqes not submitted (whatever that means) 65 // number of sqes not submitted 66 // From documentation : [dropped] is incremented for each invalid submission queue entry encountered in the ring buffer. 52 67 __u32 * dropped; 53 68 54 // Like head/tail but not seen by the kernel55 volatile __u32 * ready;56 __u32 ready_cnt;57 __u32 prev_ready;58 59 #if defined(LEADER_LOCK)60 __leaderlock_t submit_lock;61 #else62 __spinlock_t submit_lock;63 #endif64 __spinlock_t release_lock;65 66 69 // A buffer of sqes (not the actual ring) 67 volatilestruct io_uring_sqe * sqes;70 struct io_uring_sqe * sqes; 68 71 69 72 // The location and size of the mmaped area … … 72 75 }; 73 76 74 struct __c ompletion_data{77 struct __cmp_ring_t { 75 78 // Head and tail of the ring 76 79 volatile __u32 * head; … … 81 84 const __u32 * num; 82 85 83 // number of cqes not submitted (whatever that means)86 // I don't know what this value is for 84 87 __u32 * overflow; 85 88 … … 92 95 }; 93 96 94 struct __io_data { 95 struct __submition_data submit_q; 96 struct __completion_data completion_q; 97 struct __attribute__((aligned(128))) $io_context { 98 $io_arbiter * arbiter; 99 processor * proc; 100 101 struct { 102 volatile bool empty; 103 condition blocked; 104 } ext_sq; 105 106 struct __sub_ring_t sq; 107 struct __cmp_ring_t cq; 97 108 __u32 ring_flags; 98 109 int fd; 99 int efd; 100 bool eager_submits:1; 101 bool poller_submits:1; 110 }; 111 112 monitor __attribute__((aligned(128))) $io_arbiter { 113 struct { 114 condition blocked; 115 $io_context * ctx; 116 volatile bool flag; 117 } pending; 102 118 }; 103 119 … … 131 147 #endif 132 148 133 struct $io_ctx_thread; 134 void __ioctx_register($io_ctx_thread & ctx); 135 void __ioctx_prepare_block($io_ctx_thread & ctx); 136 void __sqe_clean( volatile struct io_uring_sqe * sqe ); 149 // void __ioctx_prepare_block($io_context & ctx); 137 150 #endif 138 151 -
libcfa/src/concurrency/iofwd.hfa
r342af53 r8e4aa05 18 18 #include <unistd.h> 19 19 extern "C" { 20 #include < sys/types.h>20 #include <asm/types.h> 21 21 #if CFA_HAVE_LINUX_IO_URING_H 22 22 #include <linux/io_uring.h> … … 48 48 struct cluster; 49 49 struct io_future_t; 50 struct io_context; 51 struct io_cancellation; 50 struct $io_context; 52 51 53 52 struct iovec; … … 55 54 struct sockaddr; 56 55 struct statx; 56 struct epoll_event; 57 58 //---------- 59 // underlying calls 60 extern struct $io_context * cfa_io_allocate(struct io_uring_sqe * out_sqes[], __u32 out_idxs[], __u32 want) __attribute__((nonnull (1,2))); 61 extern void cfa_io_submit( struct $io_context * in_ctx, __u32 in_idxs[], __u32 have, bool lazy ) __attribute__((nonnull (1,2))); 57 62 58 63 //---------- 59 64 // synchronous calls 60 65 #if defined(CFA_HAVE_PREADV2) 61 extern ssize_t cfa_preadv2(int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);66 extern ssize_t cfa_preadv2(int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags, __u64 submit_flags); 62 67 #endif 63 68 #if defined(CFA_HAVE_PWRITEV2) 64 extern ssize_t cfa_pwritev2(int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);69 extern ssize_t cfa_pwritev2(int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags, __u64 submit_flags); 65 70 #endif 66 extern int cfa_fsync(int fd, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);67 extern int cfa_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);68 extern int cfa_sync_file_range(int fd, off64_t offset, off64_t nbytes, unsigned int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);69 extern ssize_t cfa_sendmsg(int sockfd, const struct msghdr *msg, int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);70 extern ssize_t cfa_recvmsg(int sockfd, struct msghdr *msg, int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);71 extern ssize_t cfa_send(int sockfd, const void *buf, size_t len, int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);72 extern ssize_t cfa_recv(int sockfd, void *buf, size_t len, int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);73 extern int cfa_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);74 extern int cfa_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);75 extern int cfa_fallocate(int fd, int mode, off_t offset, off_t len, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);76 extern int cfa_posix_fadvise(int fd, off_t offset, off_t len, int advice, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);77 extern int cfa_madvise(void *addr, size_t length, int advice, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);78 extern int cfa_openat(int dirfd, const char *pathname, int flags, mode_t mode, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);71 extern int cfa_fsync(int fd, __u64 submit_flags); 72 extern int cfa_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event, __u64 submit_flags); 73 extern int cfa_sync_file_range(int fd, off64_t offset, off64_t nbytes, unsigned int flags, __u64 submit_flags); 74 extern ssize_t cfa_sendmsg(int sockfd, const struct msghdr *msg, int flags, __u64 submit_flags); 75 extern ssize_t cfa_recvmsg(int sockfd, struct msghdr *msg, int flags, __u64 submit_flags); 76 extern ssize_t cfa_send(int sockfd, const void *buf, size_t len, int flags, __u64 submit_flags); 77 extern ssize_t cfa_recv(int sockfd, void *buf, size_t len, int flags, __u64 submit_flags); 78 extern int cfa_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags, __u64 submit_flags); 79 extern int cfa_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen, __u64 submit_flags); 80 extern int cfa_fallocate(int fd, int mode, off_t offset, off_t len, __u64 submit_flags); 81 extern int cfa_posix_fadvise(int fd, off_t offset, off_t len, int advice, __u64 submit_flags); 82 extern int cfa_madvise(void *addr, size_t length, int advice, __u64 submit_flags); 83 extern int cfa_openat(int dirfd, const char *pathname, int flags, mode_t mode, __u64 submit_flags); 79 84 #if defined(CFA_HAVE_OPENAT2) 80 extern int cfa_openat2(int dirfd, const char *pathname, struct open_how * how, size_t size, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);85 extern int cfa_openat2(int dirfd, const char *pathname, struct open_how * how, size_t size, __u64 submit_flags); 81 86 #endif 82 extern int cfa_close(int fd, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);87 extern int cfa_close(int fd, __u64 submit_flags); 83 88 #if defined(CFA_HAVE_STATX) 84 extern int cfa_statx(int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);89 extern int cfa_statx(int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf, __u64 submit_flags); 85 90 #endif 86 extern ssize_t cfa_read(int fd, void * buf, size_t count, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);87 extern ssize_t cfa_write(int fd, void * buf, size_t count, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);88 extern ssize_t cfa_splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);89 extern ssize_t cfa_tee(int fd_in, int fd_out, size_t len, unsigned int flags, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);91 extern ssize_t cfa_read(int fd, void * buf, size_t count, __u64 submit_flags); 92 extern ssize_t cfa_write(int fd, void * buf, size_t count, __u64 submit_flags); 93 extern ssize_t cfa_splice(int fd_in, __off64_t *off_in, int fd_out, __off64_t *off_out, size_t len, unsigned int flags, __u64 submit_flags); 94 extern ssize_t cfa_tee(int fd_in, int fd_out, size_t len, unsigned int flags, __u64 submit_flags); 90 95 91 96 //---------- 92 97 // asynchronous calls 93 98 #if defined(CFA_HAVE_PREADV2) 94 extern void async_preadv2(io_future_t & future, int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags, int submit_flags, io_cancellation * cancellation, io_context * context);99 extern void async_preadv2(io_future_t & future, int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags, __u64 submit_flags); 95 100 #endif 96 101 #if defined(CFA_HAVE_PWRITEV2) 97 extern void async_pwritev2(io_future_t & future, int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags, int submit_flags, io_cancellation * cancellation, io_context * context);102 extern void async_pwritev2(io_future_t & future, int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags, __u64 submit_flags); 98 103 #endif 99 extern void async_fsync(io_future_t & future, int fd, int submit_flags, io_cancellation * cancellation, io_context * context);100 extern void async_epoll_ctl(io_future_t & future, int epfd, int op, int fd, struct epoll_event *event, int submit_flags, io_cancellation * cancellation, io_context * context);101 extern void async_sync_file_range(io_future_t & future, int fd, off64_t offset, off64_t nbytes, unsigned int flags, int submit_flags, io_cancellation * cancellation, io_context * context);102 extern void async_sendmsg(io_future_t & future, int sockfd, const struct msghdr *msg, int flags, int submit_flags, io_cancellation * cancellation, io_context * context);103 extern void async_recvmsg(io_future_t & future, int sockfd, struct msghdr *msg, int flags, int submit_flags, io_cancellation * cancellation, io_context * context);104 extern void async_send(io_future_t & future, int sockfd, const void *buf, size_t len, int flags, int submit_flags, io_cancellation * cancellation, io_context * context);105 extern void async_recv(io_future_t & future, int sockfd, void *buf, size_t len, int flags, int submit_flags, io_cancellation * cancellation, io_context * context);106 extern void async_accept4(io_future_t & future, int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags, int submit_flags, io_cancellation * cancellation, io_context * context);107 extern void async_connect(io_future_t & future, int sockfd, const struct sockaddr *addr, socklen_t addrlen, int submit_flags, io_cancellation * cancellation, io_context * context);108 extern void async_fallocate(io_future_t & future, int fd, int mode, off_t offset, off_t len, int submit_flags, io_cancellation * cancellation, io_context * context);109 extern void async_posix_fadvise(io_future_t & future, int fd, off_t offset, off_t len, int advice, int submit_flags, io_cancellation * cancellation, io_context * context);110 extern void async_madvise(io_future_t & future, void *addr, size_t length, int advice, int submit_flags, io_cancellation * cancellation, io_context * context);111 extern void async_openat(io_future_t & future, int dirfd, const char *pathname, int flags, mode_t mode, int submit_flags, io_cancellation * cancellation, io_context * context);104 extern void async_fsync(io_future_t & future, int fd, __u64 submit_flags); 105 extern void async_epoll_ctl(io_future_t & future, int epfd, int op, int fd, struct epoll_event *event, __u64 submit_flags); 106 extern void async_sync_file_range(io_future_t & future, int fd, off64_t offset, off64_t nbytes, unsigned int flags, __u64 submit_flags); 107 extern void async_sendmsg(io_future_t & future, int sockfd, const struct msghdr *msg, int flags, __u64 submit_flags); 108 extern void async_recvmsg(io_future_t & future, int sockfd, struct msghdr *msg, int flags, __u64 submit_flags); 109 extern void async_send(io_future_t & future, int sockfd, const void *buf, size_t len, int flags, __u64 submit_flags); 110 extern void async_recv(io_future_t & future, int sockfd, void *buf, size_t len, int flags, __u64 submit_flags); 111 extern void async_accept4(io_future_t & future, int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags, __u64 submit_flags); 112 extern void async_connect(io_future_t & future, int sockfd, const struct sockaddr *addr, socklen_t addrlen, __u64 submit_flags); 113 extern void async_fallocate(io_future_t & future, int fd, int mode, off_t offset, off_t len, __u64 submit_flags); 114 extern void async_posix_fadvise(io_future_t & future, int fd, off_t offset, off_t len, int advice, __u64 submit_flags); 115 extern void async_madvise(io_future_t & future, void *addr, size_t length, int advice, __u64 submit_flags); 116 extern void async_openat(io_future_t & future, int dirfd, const char *pathname, int flags, mode_t mode, __u64 submit_flags); 112 117 #if defined(CFA_HAVE_OPENAT2) 113 extern void async_openat2(io_future_t & future, int dirfd, const char *pathname, struct open_how * how, size_t size, int submit_flags, io_cancellation * cancellation, io_context * context);118 extern void async_openat2(io_future_t & future, int dirfd, const char *pathname, struct open_how * how, size_t size, __u64 submit_flags); 114 119 #endif 115 extern void async_close(io_future_t & future, int fd, int submit_flags, io_cancellation * cancellation, io_context * context);120 extern void async_close(io_future_t & future, int fd, __u64 submit_flags); 116 121 #if defined(CFA_HAVE_STATX) 117 extern void async_statx(io_future_t & future, int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf, int submit_flags, io_cancellation * cancellation, io_context * context);122 extern void async_statx(io_future_t & future, int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf, __u64 submit_flags); 118 123 #endif 119 void async_read(io_future_t & future, int fd, void * buf, size_t count, int submit_flags, io_cancellation * cancellation, io_context * context);120 extern void async_write(io_future_t & future, int fd, void * buf, size_t count, int submit_flags, io_cancellation * cancellation, io_context * context);121 extern void async_splice(io_future_t & future, int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags, int submit_flags, io_cancellation * cancellation, io_context * context);122 extern void async_tee(io_future_t & future, int fd_in, int fd_out, size_t len, unsigned int flags, int submit_flags, io_cancellation * cancellation, io_context * context);124 void async_read(io_future_t & future, int fd, void * buf, size_t count, __u64 submit_flags); 125 extern void async_write(io_future_t & future, int fd, void * buf, size_t count, __u64 submit_flags); 126 extern void async_splice(io_future_t & future, int fd_in, __off64_t *off_in, int fd_out, __off64_t *off_out, size_t len, unsigned int flags, __u64 submit_flags); 127 extern void async_tee(io_future_t & future, int fd_in, int fd_out, size_t len, unsigned int flags, __u64 submit_flags); 123 128 124 129 … … 126 131 // Check if a function is blocks a only the user thread 127 132 bool has_user_level_blocking( fptr_t func ); 128 129 //-----------------------------------------------------------------------------130 void register_fixed_files( io_context & ctx , int * files, unsigned count );131 void register_fixed_files( cluster & cltr, int * files, unsigned count ); -
libcfa/src/concurrency/kernel.cfa
r342af53 r8e4aa05 22 22 #include <signal.h> 23 23 #include <unistd.h> 24 extern "C" { 25 #include <sys/eventfd.h> 26 } 24 27 25 28 //CFA Includes … … 114 117 static [unsigned idle, unsigned total, * processor] query( & __cluster_idles idles ); 115 118 119 extern void __cfa_io_start( processor * ); 120 extern void __cfa_io_drain( processor * ); 121 extern void __cfa_io_flush( processor * ); 122 extern void __cfa_io_stop ( processor * ); 123 static inline void __maybe_io_drain( processor * ); 124 125 extern void __disable_interrupts_hard(); 126 extern void __enable_interrupts_hard(); 116 127 117 128 //============================================================================================= … … 129 140 verify(this); 130 141 142 __cfa_io_start( this ); 143 131 144 __cfadbg_print_safe(runtime_core, "Kernel : core %p starting\n", this); 132 145 #if !defined(__CFA_NO_STATISTICS__) … … 140 153 preemption_scope scope = { this }; 141 154 155 #if !defined(__CFA_NO_STATISTICS__) 156 unsigned long long last_tally = rdtscl(); 157 #endif 158 159 142 160 __cfadbg_print_safe(runtime_core, "Kernel : core %p started\n", this); 143 161 … … 145 163 MAIN_LOOP: 146 164 for() { 165 // Check if there is pending io 166 __maybe_io_drain( this ); 167 147 168 // Try to get the next thread 148 169 readyThread = __next_thread( this->cltr ); 149 170 150 171 if( !readyThread ) { 172 __cfa_io_flush( this ); 151 173 readyThread = __next_thread_slow( this->cltr ); 152 174 } … … 184 206 #endif 185 207 186 wait( this->idle ); 208 __cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle); 209 210 __disable_interrupts_hard(); 211 eventfd_t val; 212 eventfd_read( this->idle, &val ); 213 __enable_interrupts_hard(); 187 214 188 215 #if !defined(__CFA_NO_STATISTICS__) … … 201 228 /* paranoid */ verify( readyThread ); 202 229 230 // Reset io dirty bit 231 this->io.dirty = false; 232 203 233 // We found a thread run it 204 234 __run_thread(this, readyThread); … … 206 236 // Are we done? 207 237 if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP; 238 239 #if !defined(__CFA_NO_STATISTICS__) 240 unsigned long long curr = rdtscl(); 241 if(curr > (last_tally + 500000000)) { 242 __tally_stats(this->cltr->stats, __cfaabi_tls.this_stats); 243 last_tally = curr; 244 } 245 #endif 246 247 if(this->io.pending && !this->io.dirty) { 248 __cfa_io_flush( this ); 249 } 208 250 } 209 251 … … 211 253 } 212 254 213 V( this->terminated ); 255 __cfa_io_stop( this ); 256 257 post( this->terminated ); 258 214 259 215 260 if(this == mainProcessor) { … … 234 279 /* paranoid */ verifyf( thrd_dst->link.next == 0p, "Expected null got %p", thrd_dst->link.next ); 235 280 __builtin_prefetch( thrd_dst->context.SP ); 281 282 __cfadbg_print_safe(runtime_core, "Kernel : core %p running thread %p (%s)\n", this, thrd_dst, thrd_dst->self_cor.name); 236 283 237 284 $coroutine * proc_cor = get_coroutine(this->runner); … … 316 363 // Just before returning to the processor, set the processor coroutine to active 317 364 proc_cor->state = Active; 365 366 __cfadbg_print_safe(runtime_core, "Kernel : core %p finished running thread %p\n", this, thrd_dst); 318 367 319 368 /* paranoid */ verify( ! __preemption_enabled() ); … … 550 599 551 600 // We found a processor, wake it up 552 post( p->idle ); 601 eventfd_t val; 602 val = 1; 603 eventfd_write( p->idle, val ); 553 604 554 605 #if !defined(__CFA_NO_STATISTICS__) … … 568 619 disable_interrupts(); 569 620 /* paranoid */ verify( ! __preemption_enabled() ); 570 post( this->idle ); 621 eventfd_t val; 622 val = 1; 623 eventfd_write( this->idle, val ); 571 624 enable_interrupts( __cfaabi_dbg_ctx ); 572 625 } … … 611 664 // Unexpected Terminating logic 612 665 //============================================================================================= 613 static __spinlock_t kernel_abort_lock; 614 static bool kernel_abort_called = false; 615 616 void * kernel_abort(void) __attribute__ ((__nothrow__)) { 617 // abort cannot be recursively entered by the same or different processors because all signal handlers return when 618 // the globalAbort flag is true. 619 lock( kernel_abort_lock __cfaabi_dbg_ctx2 ); 620 621 // disable interrupts, it no longer makes sense to try to interrupt this processor 622 disable_interrupts(); 623 624 // first task to abort ? 625 if ( kernel_abort_called ) { // not first task to abort ? 626 unlock( kernel_abort_lock ); 627 628 sigset_t mask; 629 sigemptyset( &mask ); 630 sigaddset( &mask, SIGALRM ); // block SIGALRM signals 631 sigaddset( &mask, SIGUSR1 ); // block SIGALRM signals 632 sigsuspend( &mask ); // block the processor to prevent further damage during abort 633 _exit( EXIT_FAILURE ); // if processor unblocks before it is killed, terminate it 634 } 635 else { 636 kernel_abort_called = true; 637 unlock( kernel_abort_lock ); 638 } 639 640 return __cfaabi_tls.this_thread; 641 } 642 643 void kernel_abort_msg( void * kernel_data, char * abort_text, int abort_text_size ) { 644 $thread * thrd = ( $thread * ) kernel_data; 666 void __kernel_abort_msg( char * abort_text, int abort_text_size ) { 667 $thread * thrd = __cfaabi_tls.this_thread; 645 668 646 669 if(thrd) { … … 662 685 } 663 686 664 int kernel_abort_lastframe( void ) __attribute__ ((__nothrow__)) {665 return get_coroutine( kernelTLS().this_thread) == get_coroutine(mainThread) ? 4 : 2;687 int __kernel_abort_lastframe( void ) __attribute__ ((__nothrow__)) { 688 return get_coroutine(__cfaabi_tls.this_thread) == get_coroutine(mainThread) ? 4 : 2; 666 689 } 667 690 … … 681 704 // Kernel Utilities 682 705 //============================================================================================= 683 //----------------------------------------------------------------------------- 684 // Locks 685 void ?{}( semaphore & this, int count = 1 ) { 686 (this.lock){}; 687 this.count = count; 688 (this.waiting){}; 689 } 690 void ^?{}(semaphore & this) {} 691 692 bool P(semaphore & this) with( this ){ 693 lock( lock __cfaabi_dbg_ctx2 ); 694 count -= 1; 695 if ( count < 0 ) { 696 // queue current task 697 append( waiting, active_thread() ); 698 699 // atomically release spin lock and block 700 unlock( lock ); 701 park(); 702 return true; 703 } 704 else { 705 unlock( lock ); 706 return false; 707 } 708 } 709 710 bool V(semaphore & this) with( this ) { 711 $thread * thrd = 0p; 712 lock( lock __cfaabi_dbg_ctx2 ); 713 count += 1; 714 if ( count <= 0 ) { 715 // remove task at head of waiting list 716 thrd = pop_head( waiting ); 717 } 718 719 unlock( lock ); 720 721 // make new owner 722 unpark( thrd ); 723 724 return thrd != 0p; 725 } 726 727 bool V(semaphore & this, unsigned diff) with( this ) { 728 $thread * thrd = 0p; 729 lock( lock __cfaabi_dbg_ctx2 ); 730 int release = max(-count, (int)diff); 731 count += diff; 732 for(release) { 733 unpark( pop_head( waiting ) ); 734 } 735 736 unlock( lock ); 737 738 return thrd != 0p; 706 #if defined(CFA_HAVE_LINUX_IO_URING_H) 707 #include "io/types.hfa" 708 #endif 709 710 static inline void __maybe_io_drain( processor * proc ) { 711 #if defined(CFA_HAVE_LINUX_IO_URING_H) 712 __cfadbg_print_safe(runtime_core, "Kernel : core %p checking io for ring %d\n", proc, proc->io.ctx->fd); 713 714 // Check if we should drain the queue 715 $io_context * ctx = proc->io.ctx; 716 unsigned head = *ctx->cq.head; 717 unsigned tail = *ctx->cq.tail; 718 if(head != tail) __cfa_io_drain( proc ); 719 #endif 739 720 } 740 721 -
libcfa/src/concurrency/kernel.hfa
r342af53 r8e4aa05 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // kernel -- 7 // kernel -- Header containing the core of the kernel API 8 8 // 9 9 // Author : Thierry Delisle … … 24 24 extern "C" { 25 25 #include <bits/pthreadtypes.h> 26 #include <pthread.h> 26 27 #include <linux/types.h> 27 28 } 28 29 29 //----------------------------------------------------------------------------- 30 // Locks 31 struct semaphore { 32 __spinlock_t lock; 33 int count; 34 __queue_t($thread) waiting; 35 }; 36 37 void ?{}(semaphore & this, int count = 1); 38 void ^?{}(semaphore & this); 39 bool P (semaphore & this); 40 bool V (semaphore & this); 41 bool V (semaphore & this, unsigned count); 42 30 #ifdef __CFA_WITH_VERIFY__ 31 extern bool __cfaabi_dbg_in_kernel(); 32 #endif 33 34 //----------------------------------------------------------------------------- 35 // I/O 36 struct cluster; 37 struct $io_context; 38 struct $io_arbiter; 39 40 struct io_context_params { 41 int num_entries; 42 }; 43 44 void ?{}(io_context_params & this); 43 45 44 46 //----------------------------------------------------------------------------- … … 80 82 pthread_t kernel_thread; 81 83 84 struct { 85 $io_context * ctx; 86 bool pending; 87 bool dirty; 88 } io; 89 82 90 // Preemption data 83 91 // Node which is added in the discrete event simulaiton … … 88 96 89 97 // Idle lock (kernel semaphore) 90 __bin_sem_t idle;98 int idle; 91 99 92 100 // Termination synchronisation (user semaphore) 93 semaphoreterminated;101 oneshot terminated; 94 102 95 103 // pthread Stack … … 118 126 119 127 DLISTED_MGD_IMPL_OUT(processor) 120 121 //-----------------------------------------------------------------------------122 // I/O123 struct __io_data;124 125 // IO poller user-thread126 // Not using the "thread" keyword because we want to control127 // more carefully when to start/stop it128 struct $io_ctx_thread {129 struct __io_data * ring;130 single_sem sem;131 volatile bool done;132 $thread self;133 };134 135 136 struct io_context {137 $io_ctx_thread thrd;138 };139 140 struct io_context_params {141 int num_entries;142 int num_ready;143 int submit_aff;144 bool eager_submits:1;145 bool poller_submits:1;146 bool poll_submit:1;147 bool poll_complete:1;148 };149 150 void ?{}(io_context_params & this);151 152 void ?{}(io_context & this, struct cluster & cl);153 void ?{}(io_context & this, struct cluster & cl, const io_context_params & params);154 void ^?{}(io_context & this);155 156 struct io_cancellation {157 __u64 target;158 };159 160 static inline void ?{}(io_cancellation & this) { this.target = -1u; }161 static inline void ^?{}(io_cancellation &) {}162 bool cancel(io_cancellation & this);163 128 164 129 //----------------------------------------------------------------------------- … … 246 211 247 212 struct { 248 io_context * ctxs;249 unsigned cnt;213 $io_arbiter * arbiter; 214 io_context_params params; 250 215 } io; 251 216 -
libcfa/src/concurrency/kernel/fwd.hfa
r342af53 r8e4aa05 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // kernel/fwd.hfa -- 7 // kernel/fwd.hfa -- PUBLIC 8 // Fundamental code needed to implement threading M.E.S. algorithms. 8 9 // 9 10 // Author : Thierry Delisle … … 134 135 extern uint64_t thread_rand(); 135 136 137 // Semaphore which only supports a single thread 138 struct single_sem { 139 struct $thread * volatile ptr; 140 }; 141 142 static inline { 143 void ?{}(single_sem & this) { 144 this.ptr = 0p; 145 } 146 147 void ^?{}(single_sem &) {} 148 149 bool wait(single_sem & this) { 150 for() { 151 struct $thread * expected = this.ptr; 152 if(expected == 1p) { 153 if(__atomic_compare_exchange_n(&this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 154 return false; 155 } 156 } 157 else { 158 /* paranoid */ verify( expected == 0p ); 159 if(__atomic_compare_exchange_n(&this.ptr, &expected, active_thread(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 160 park(); 161 return true; 162 } 163 } 164 165 } 166 } 167 168 bool post(single_sem & this) { 169 for() { 170 struct $thread * expected = this.ptr; 171 if(expected == 1p) return false; 172 if(expected == 0p) { 173 if(__atomic_compare_exchange_n(&this.ptr, &expected, 1p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 174 return false; 175 } 176 } 177 else { 178 if(__atomic_compare_exchange_n(&this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 179 unpark( expected ); 180 return true; 181 } 182 } 183 } 184 } 185 } 186 187 // Synchronozation primitive which only supports a single thread and one post 188 // Similar to a binary semaphore with a 'one shot' semantic 189 // is expected to be discarded after each party call their side 190 struct oneshot { 191 // Internal state : 192 // 0p : is initial state (wait will block) 193 // 1p : fulfilled (wait won't block) 194 // any thread : a thread is currently waiting 195 struct $thread * volatile ptr; 196 }; 197 198 static inline { 199 void ?{}(oneshot & this) { 200 this.ptr = 0p; 201 } 202 203 void ^?{}(oneshot &) {} 204 205 // Wait for the post, return immidiately if it already happened. 206 // return true if the thread was parked 207 bool wait(oneshot & this) { 208 for() { 209 struct $thread * expected = this.ptr; 210 if(expected == 1p) return false; 211 /* paranoid */ verify( expected == 0p ); 212 if(__atomic_compare_exchange_n(&this.ptr, &expected, active_thread(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 213 park(); 214 /* paranoid */ verify( this.ptr == 1p ); 215 return true; 216 } 217 } 218 } 219 220 // Mark as fulfilled, wake thread if needed 221 // return true if a thread was unparked 222 bool post(oneshot & this) { 223 struct $thread * got = __atomic_exchange_n( &this.ptr, 1p, __ATOMIC_SEQ_CST); 224 if( got == 0p ) return false; 225 unpark( got ); 226 return true; 227 } 228 } 229 230 // base types for future to build upon 231 // It is based on the 'oneshot' type to allow multiple futures 232 // to block on the same instance, permitting users to block a single 233 // thread on "any of" [a given set of] futures. 234 // does not support multiple threads waiting on the same future 235 struct future_t { 236 // Internal state : 237 // 0p : is initial state (wait will block) 238 // 1p : fulfilled (wait won't block) 239 // 2p : in progress () 240 // 3p : abandoned, server should delete 241 // any oneshot : a context has been setup to wait, a thread could wait on it 242 struct oneshot * volatile ptr; 243 }; 244 245 static inline { 246 void ?{}(future_t & this) { 247 this.ptr = 0p; 248 } 249 250 void ^?{}(future_t &) {} 251 252 void reset(future_t & this) { 253 // needs to be in 0p or 1p 254 __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST); 255 } 256 257 // check if the future is available 258 bool available( future_t & this ) { 259 return this.ptr == 1p; 260 } 261 262 // Prepare the future to be waited on 263 // intented to be use by wait, wait_any, waitfor, etc. rather than used directly 264 bool setup( future_t & this, oneshot & wait_ctx ) { 265 /* paranoid */ verify( wait_ctx.ptr == 0p ); 266 // The future needs to set the wait context 267 for() { 268 struct oneshot * expected = this.ptr; 269 // Is the future already fulfilled? 270 if(expected == 1p) return false; // Yes, just return false (didn't block) 271 272 // The future is not fulfilled, try to setup the wait context 273 /* paranoid */ verify( expected == 0p ); 274 if(__atomic_compare_exchange_n(&this.ptr, &expected, &wait_ctx, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 275 return true; 276 } 277 } 278 } 279 280 // Stop waiting on a future 281 // When multiple futures are waited for together in "any of" pattern 282 // futures that weren't fulfilled before the thread woke up 283 // should retract the wait ctx 284 // intented to be use by wait, wait_any, waitfor, etc. rather than used directly 285 void retract( future_t & this, oneshot & wait_ctx ) { 286 // Remove the wait context 287 struct oneshot * got = __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST); 288 289 // got == 0p: future was never actually setup, just return 290 if( got == 0p ) return; 291 292 // got == wait_ctx: since fulfil does an atomic_swap, 293 // if we got back the original then no one else saw context 294 // It is safe to delete (which could happen after the return) 295 if( got == &wait_ctx ) return; 296 297 // got == 1p: the future is ready and the context was fully consumed 298 // the server won't use the pointer again 299 // It is safe to delete (which could happen after the return) 300 if( got == 1p ) return; 301 302 // got == 2p: the future is ready but the context hasn't fully been consumed 303 // spin until it is safe to move on 304 if( got == 2p ) { 305 while( this.ptr != 1p ) Pause(); 306 return; 307 } 308 309 // got == any thing else, something wen't wrong here, abort 310 abort("Future in unexpected state"); 311 } 312 313 // Mark the future as abandoned, meaning it will be deleted by the server 314 bool abandon( future_t & this ) { 315 /* paranoid */ verify( this.ptr != 3p ); 316 317 // Mark the future as abandonned 318 struct oneshot * got = __atomic_exchange_n( &this.ptr, 3p, __ATOMIC_SEQ_CST); 319 320 // If the future isn't already fulfilled, let the server delete it 321 if( got == 0p ) return false; 322 323 // got == 2p: the future is ready but the context hasn't fully been consumed 324 // spin until it is safe to move on 325 if( got == 2p ) { 326 while( this.ptr != 1p ) Pause(); 327 got = 1p; 328 } 329 330 // The future is completed delete it now 331 /* paranoid */ verify( this.ptr != 1p ); 332 free( &this ); 333 return true; 334 } 335 336 // from the server side, mark the future as fulfilled 337 // delete it if needed 338 bool fulfil( future_t & this ) { 339 for() { 340 struct oneshot * expected = this.ptr; 341 // was this abandoned? 342 #if defined(__GNUC__) && __GNUC__ >= 7 343 #pragma GCC diagnostic push 344 #pragma GCC diagnostic ignored "-Wfree-nonheap-object" 345 #endif 346 if( expected == 3p ) { free( &this ); return false; } 347 #if defined(__GNUC__) && __GNUC__ >= 7 348 #pragma GCC diagnostic pop 349 #endif 350 351 /* paranoid */ verify( expected != 1p ); // Future is already fulfilled, should not happen 352 /* paranoid */ verify( expected != 2p ); // Future is bein fulfilled by someone else, this is even less supported then the previous case. 353 354 // If there is a wait context, we need to consume it and mark it as consumed after 355 // If there is no context then we can skip the in progress phase 356 struct oneshot * want = expected == 0p ? 1p : 2p; 357 if(__atomic_compare_exchange_n(&this.ptr, &expected, want, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 358 if( expected == 0p ) { /* paranoid */ verify( this.ptr == 1p); return false; } 359 bool ret = post( *expected ); 360 __atomic_store_n( &this.ptr, 1p, __ATOMIC_SEQ_CST); 361 return ret; 362 } 363 } 364 365 } 366 367 // Wait for the future to be fulfilled 368 bool wait( future_t & this ) { 369 oneshot temp; 370 if( !setup(this, temp) ) return false; 371 372 // Wait context is setup, just wait on it 373 bool ret = wait( temp ); 374 375 // Wait for the future to tru 376 while( this.ptr == 2p ) Pause(); 377 // Make sure the state makes sense 378 // Should be fulfilled, could be in progress but it's out of date if so 379 // since if that is the case, the oneshot was fulfilled (unparking this thread) 380 // and the oneshot should not be needed any more 381 __attribute__((unused)) struct oneshot * was = this.ptr; 382 /* paranoid */ verifyf( was == 1p, "Expected this.ptr to be 1p, was %p\n", was ); 383 384 // Mark the future as fulfilled, to be consistent 385 // with potential calls to avail 386 // this.ptr = 1p; 387 return ret; 388 } 389 } 390 136 391 //----------------------------------------------------------------------- 137 392 // Statics call at the end of each thread to register statistics -
libcfa/src/concurrency/kernel/startup.cfa
r342af53 r8e4aa05 22 22 extern "C" { 23 23 #include <limits.h> // PTHREAD_STACK_MIN 24 #include <sys/eventfd.h> // eventfd 24 25 #include <sys/mman.h> // mprotect 25 26 #include <sys/resource.h> // getrlimit … … 89 90 extern void __kernel_alarm_startup(void); 90 91 extern void __kernel_alarm_shutdown(void); 91 extern void __kernel_io_startup (void);92 extern void __kernel_io_shutdown(void);93 92 94 93 //----------------------------------------------------------------------------- … … 102 101 KERNEL_STORAGE($thread, mainThread); 103 102 KERNEL_STORAGE(__stack_t, mainThreadCtx); 104 KERNEL_STORAGE(io_context, mainPollerThread);105 103 KERNEL_STORAGE(__scheduler_RWLock_t, __scheduler_lock); 106 104 #if !defined(__CFA_NO_STATISTICS__) … … 198 196 199 197 void ?{}(processor & this) with( this ) { 200 ( this.idle ){}; 201 ( this.terminated ){ 0 }; 198 ( this.terminated ){}; 202 199 ( this.runner ){}; 203 200 init( this, "Main Processor", *mainCluster ); … … 226 223 __kernel_alarm_startup(); 227 224 228 // Start IO229 __kernel_io_startup();230 231 225 // Add the main thread to the ready queue 232 226 // once resume is called on mainProcessor->runner the mainThread needs to be scheduled like any normal thread … … 241 235 // THE SYSTEM IS NOW COMPLETELY RUNNING 242 236 243 244 // SKULLDUGGERY: The constructor for the mainCluster will call alloc with a dimension of 0245 // malloc *can* return a non-null value, we should free it if that is the case246 free( mainCluster->io.ctxs );247 248 // Now that the system is up, finish creating systems that need threading249 mainCluster->io.ctxs = (io_context *)&storage_mainPollerThread;250 mainCluster->io.cnt = 1;251 (*mainCluster->io.ctxs){ *mainCluster };252 253 237 __cfadbg_print_safe(runtime_core, "Kernel : Started\n--------------------------------------------------\n\n"); 254 238 … … 260 244 261 245 static void __kernel_shutdown(void) { 262 //Before we start shutting things down, wait for systems that need threading to shutdown263 ^(*mainCluster->io.ctxs){};264 mainCluster->io.cnt = 0;265 mainCluster->io.ctxs = 0p;266 267 246 /* paranoid */ verify( __preemption_enabled() ); 268 247 disable_interrupts(); … … 282 261 // Disable preemption 283 262 __kernel_alarm_shutdown(); 284 285 // Stop IO286 __kernel_io_shutdown();287 263 288 264 // Destroy the main processor and its context in reverse order of construction … … 484 460 pending_preemption = false; 485 461 462 this.io.ctx = 0p; 463 this.io.pending = false; 464 this.io.dirty = false; 465 466 this.idle = eventfd(0, 0); 467 if (idle < 0) { 468 abort("KERNEL ERROR: PROCESSOR EVENTFD - %s\n", strerror(errno)); 469 } 470 486 471 #if !defined(__CFA_NO_STATISTICS__) 487 472 print_stats = 0; … … 524 509 // Finally we don't need the read_lock any more 525 510 unregister((__processor_id_t*)&this); 511 512 close(this.idle); 526 513 } 527 514 528 515 void ?{}(processor & this, const char name[], cluster & _cltr) { 529 ( this.idle ){}; 530 ( this.terminated ){ 0 }; 516 ( this.terminated ){}; 531 517 ( this.runner ){}; 532 518 … … 549 535 __wake_proc( &this ); 550 536 551 P( terminated );537 wait( terminated ); 552 538 /* paranoid */ verify( active_processor() != &this); 553 539 } … … 582 568 threads{ __get }; 583 569 570 io.arbiter = create(); 571 io.params = io_params; 572 584 573 doregister(this); 585 574 … … 594 583 ready_mutate_unlock( last_size ); 595 584 enable_interrupts_noPoll(); // Don't poll, could be in main cluster 596 597 598 this.io.cnt = num_io;599 this.io.ctxs = aalloc(num_io);600 for(i; this.io.cnt) {601 (this.io.ctxs[i]){ this, io_params };602 }603 585 } 604 586 605 587 void ^?{}(cluster & this) { 606 for(i; this.io.cnt) { 607 ^(this.io.ctxs[i]){ true }; 608 } 609 free(this.io.ctxs); 588 destroy(this.io.arbiter); 610 589 611 590 // Lock the RWlock so no-one pushes/pops while we are changing the queue … … 736 715 } 737 716 738 739 717 #if defined(__CFA_WITH_VERIFY__) 740 718 static bool verify_fwd_bck_rng(void) { -
libcfa/src/concurrency/kernel_private.hfa
r342af53 r8e4aa05 77 77 //----------------------------------------------------------------------------- 78 78 // I/O 79 void ^?{}(io_context & this, bool ); 79 $io_arbiter * create(void); 80 void destroy($io_arbiter *); 80 81 81 82 //======================================================================= -
libcfa/src/concurrency/locks.cfa
r342af53 r8e4aa05 1 // 2 // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo 3 // 4 // The contents of this file are covered under the licence agreement in the 5 // file "LICENCE" distributed with Cforall. 6 // 7 // locks.hfa -- LIBCFATHREAD 8 // Runtime locks that used with the runtime thread system. 9 // 10 // Author : Colby Alexander Parsons 11 // Created On : Thu Jan 21 19:46:50 2021 12 // Last Modified By : 13 // Last Modified On : 14 // Update Count : 15 // 16 17 #define __cforall_thread__ 18 1 19 #include "locks.hfa" 2 20 #include "kernel_private.hfa" … … 7 25 //----------------------------------------------------------------------------- 8 26 // info_thread 9 forall( dtype L| is_blocking_lock(L)) {27 forall(L & | is_blocking_lock(L)) { 10 28 struct info_thread { 11 29 // used to put info_thread on a dl queue (aka sequence) … … 56 74 57 75 void ^?{}( blocking_lock & this ) {} 58 void ?{}( single_acquisition_lock & this ) {((blocking_lock &)this){ false, false };} 59 void ^?{}( single_acquisition_lock & this ) {} 60 void ?{}( owner_lock & this ) {((blocking_lock &)this){ true, true };} 61 void ^?{}( owner_lock & this ) {} 62 void ?{}( multiple_acquisition_lock & this ) {((blocking_lock &)this){ true, false };} 63 void ^?{}( multiple_acquisition_lock & this ) {} 76 64 77 65 78 void lock( blocking_lock & this ) with( this ) { … … 170 183 171 184 //----------------------------------------------------------------------------- 172 // Overloaded routines for traits173 // These routines are temporary until an inheritance bug is fixed174 void lock ( single_acquisition_lock & this ) { lock ( (blocking_lock &)this ); }175 void unlock ( single_acquisition_lock & this ) { unlock ( (blocking_lock &)this ); }176 void on_wait ( single_acquisition_lock & this ) { on_wait( (blocking_lock &)this ); }177 void on_notify ( single_acquisition_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); }178 void set_recursion_count( single_acquisition_lock & this, size_t recursion ) { set_recursion_count( (blocking_lock &)this, recursion ); }179 size_t get_recursion_count( single_acquisition_lock & this ) { return get_recursion_count( (blocking_lock &)this ); }180 181 void lock ( owner_lock & this ) { lock ( (blocking_lock &)this ); }182 void unlock ( owner_lock & this ) { unlock ( (blocking_lock &)this ); }183 void on_wait ( owner_lock & this ) { on_wait( (blocking_lock &)this ); }184 void on_notify( owner_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); }185 void set_recursion_count( owner_lock & this, size_t recursion ) { set_recursion_count( (blocking_lock &)this, recursion ); }186 size_t get_recursion_count( owner_lock & this ) { return get_recursion_count( (blocking_lock &)this ); }187 188 void lock ( multiple_acquisition_lock & this ) { lock ( (blocking_lock &)this ); }189 void unlock ( multiple_acquisition_lock & this ) { unlock ( (blocking_lock &)this ); }190 void on_wait ( multiple_acquisition_lock & this ) { on_wait( (blocking_lock &)this ); }191 void on_notify( multiple_acquisition_lock & this, struct $thread * t ){ on_notify( (blocking_lock &)this, t ); }192 void set_recursion_count( multiple_acquisition_lock & this, size_t recursion ){ set_recursion_count( (blocking_lock &)this, recursion ); }193 size_t get_recursion_count( multiple_acquisition_lock & this ){ return get_recursion_count( (blocking_lock &)this ); }194 195 //-----------------------------------------------------------------------------196 185 // alarm node wrapper 197 forall( dtype L| is_blocking_lock(L)) {186 forall(L & | is_blocking_lock(L)) { 198 187 struct alarm_node_wrap { 199 188 alarm_node_t alarm_node; … … 239 228 //----------------------------------------------------------------------------- 240 229 // condition variable 241 forall( dtype L| is_blocking_lock(L)) {230 forall(L & | is_blocking_lock(L)) { 242 231 243 232 void ?{}( condition_variable(L) & this ){ … … 356 345 bool wait( condition_variable(L) & this, L & l, uintptr_t info, Time time ) with(this) { WAIT_TIME( info, &l , time ) } 357 346 } 347 348 //----------------------------------------------------------------------------- 349 // Semaphore 350 void ?{}( semaphore & this, int count = 1 ) { 351 (this.lock){}; 352 this.count = count; 353 (this.waiting){}; 354 } 355 void ^?{}(semaphore & this) {} 356 357 bool P(semaphore & this) with( this ){ 358 lock( lock __cfaabi_dbg_ctx2 ); 359 count -= 1; 360 if ( count < 0 ) { 361 // queue current task 362 append( waiting, active_thread() ); 363 364 // atomically release spin lock and block 365 unlock( lock ); 366 park(); 367 return true; 368 } 369 else { 370 unlock( lock ); 371 return false; 372 } 373 } 374 375 bool V(semaphore & this) with( this ) { 376 $thread * thrd = 0p; 377 lock( lock __cfaabi_dbg_ctx2 ); 378 count += 1; 379 if ( count <= 0 ) { 380 // remove task at head of waiting list 381 thrd = pop_head( waiting ); 382 } 383 384 unlock( lock ); 385 386 // make new owner 387 unpark( thrd ); 388 389 return thrd != 0p; 390 } 391 392 bool V(semaphore & this, unsigned diff) with( this ) { 393 $thread * thrd = 0p; 394 lock( lock __cfaabi_dbg_ctx2 ); 395 int release = max(-count, (int)diff); 396 count += diff; 397 for(release) { 398 unpark( pop_head( waiting ) ); 399 } 400 401 unlock( lock ); 402 403 return thrd != 0p; 404 } -
libcfa/src/concurrency/locks.hfa
r342af53 r8e4aa05 1 // 2 // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo 3 // 4 // The contents of this file are covered under the licence agreement in the 5 // file "LICENCE" distributed with Cforall. 6 // 7 // locks.hfa -- PUBLIC 8 // Runtime locks that used with the runtime thread system. 9 // 10 // Author : Colby Alexander Parsons 11 // Created On : Thu Jan 21 19:46:50 2021 12 // Last Modified By : 13 // Last Modified On : 14 // Update Count : 15 // 16 1 17 #pragma once 2 18 3 19 #include <stdbool.h> 4 20 5 #include "bits/locks.hfa" 6 #include "bits/sequence.hfa" 7 8 #include "invoke.h" 21 #include "bits/weakso_locks.hfa" 9 22 10 23 #include "time_t.hfa" 11 24 #include "time.hfa" 12 25 26 //---------- 27 struct single_acquisition_lock { 28 inline blocking_lock; 29 }; 30 31 static inline void ?{}( single_acquisition_lock & this ) {((blocking_lock &)this){ false, false };} 32 static inline void ^?{}( single_acquisition_lock & this ) {} 33 static inline void lock ( single_acquisition_lock & this ) { lock ( (blocking_lock &)this ); } 34 static inline void unlock ( single_acquisition_lock & this ) { unlock ( (blocking_lock &)this ); } 35 static inline void on_wait ( single_acquisition_lock & this ) { on_wait( (blocking_lock &)this ); } 36 static inline void on_notify ( single_acquisition_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); } 37 static inline void set_recursion_count( single_acquisition_lock & this, size_t recursion ) { set_recursion_count( (blocking_lock &)this, recursion ); } 38 static inline size_t get_recursion_count( single_acquisition_lock & this ) { return get_recursion_count( (blocking_lock &)this ); } 39 40 //---------- 41 struct owner_lock { 42 inline blocking_lock; 43 }; 44 45 static inline void ?{}( owner_lock & this ) {((blocking_lock &)this){ true, true };} 46 static inline void ^?{}( owner_lock & this ) {} 47 static inline void lock ( owner_lock & this ) { lock ( (blocking_lock &)this ); } 48 static inline void unlock ( owner_lock & this ) { unlock ( (blocking_lock &)this ); } 49 static inline void on_wait ( owner_lock & this ) { on_wait( (blocking_lock &)this ); } 50 static inline void on_notify( owner_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); } 51 static inline void set_recursion_count( owner_lock & this, size_t recursion ) { set_recursion_count( (blocking_lock &)this, recursion ); } 52 static inline size_t get_recursion_count( owner_lock & this ) { return get_recursion_count( (blocking_lock &)this ); } 53 13 54 //----------------------------------------------------------------------------- 14 55 // is_blocking_lock 15 trait is_blocking_lock( dtype L| sized(L)) {56 trait is_blocking_lock(L & | sized(L)) { 16 57 // For synchronization locks to use when acquiring 17 58 void on_notify( L &, struct $thread * ); … … 31 72 // the info thread is a wrapper around a thread used 32 73 // to store extra data for use in the condition variable 33 forall( dtype L| is_blocking_lock(L)) {74 forall(L & | is_blocking_lock(L)) { 34 75 struct info_thread; 35 76 … … 40 81 41 82 //----------------------------------------------------------------------------- 42 // Blocking Locks43 struct blocking_lock {44 // Spin lock used for mutual exclusion45 __spinlock_t lock;46 47 // List of blocked threads48 Sequence( $thread ) blocked_threads;49 50 // Count of current blocked threads51 size_t wait_count;52 53 // Flag if the lock allows multiple acquisition54 bool multi_acquisition;55 56 // Flag if lock can be released by non owner57 bool strict_owner;58 59 // Current thread owning the lock60 struct $thread * owner;61 62 // Number of recursion level63 size_t recursion_count;64 };65 66 struct single_acquisition_lock {67 inline blocking_lock;68 };69 70 struct owner_lock {71 inline blocking_lock;72 };73 74 struct multiple_acquisition_lock {75 inline blocking_lock;76 };77 78 void ?{}( blocking_lock & this, bool multi_acquisition, bool strict_owner );79 void ^?{}( blocking_lock & this );80 81 void ?{}( single_acquisition_lock & this );82 void ^?{}( single_acquisition_lock & this );83 84 void ?{}( owner_lock & this );85 void ^?{}( owner_lock & this );86 87 void ?{}( multiple_acquisition_lock & this );88 void ^?{}( multiple_acquisition_lock & this );89 90 void lock( blocking_lock & this );91 bool try_lock( blocking_lock & this );92 void unlock( blocking_lock & this );93 void on_notify( blocking_lock & this, struct $thread * t );94 void on_wait( blocking_lock & this );95 size_t wait_count( blocking_lock & this );96 void set_recursion_count( blocking_lock & this, size_t recursion );97 size_t get_recursion_count( blocking_lock & this );98 99 void lock( single_acquisition_lock & this );100 void unlock( single_acquisition_lock & this );101 void on_notify( single_acquisition_lock & this, struct $thread * t );102 void on_wait( single_acquisition_lock & this );103 void set_recursion_count( single_acquisition_lock & this, size_t recursion );104 size_t get_recursion_count( single_acquisition_lock & this );105 106 void lock( owner_lock & this );107 void unlock( owner_lock & this );108 void on_notify( owner_lock & this, struct $thread * t );109 void on_wait( owner_lock & this );110 void set_recursion_count( owner_lock & this, size_t recursion );111 size_t get_recursion_count( owner_lock & this );112 113 void lock( multiple_acquisition_lock & this );114 void unlock( multiple_acquisition_lock & this );115 void on_notify( multiple_acquisition_lock & this, struct $thread * t );116 void on_wait( multiple_acquisition_lock & this );117 void set_recursion_count( multiple_acquisition_lock & this, size_t recursion );118 size_t get_recursion_count( multiple_acquisition_lock & this );119 120 //-----------------------------------------------------------------------------121 83 // Synchronization Locks 122 forall( dtype L| is_blocking_lock(L)) {84 forall(L & | is_blocking_lock(L)) { 123 85 struct condition_variable { 124 86 // Spin lock used for mutual exclusion … … 157 119 bool wait( condition_variable(L) & this, L & l, uintptr_t info, Time time ); 158 120 } 121 122 //----------------------------------------------------------------------------- 123 // Semaphore 124 struct semaphore { 125 __spinlock_t lock; 126 int count; 127 __queue_t($thread) waiting; 128 }; 129 130 void ?{}(semaphore & this, int count = 1); 131 void ^?{}(semaphore & this); 132 bool P (semaphore & this); 133 bool V (semaphore & this); 134 bool V (semaphore & this, unsigned count); -
libcfa/src/concurrency/monitor.cfa
r342af53 r8e4aa05 50 50 static inline [$thread *, int] search_entry_queue( const __waitfor_mask_t &, $monitor * monitors [], __lock_size_t count ); 51 51 52 forall( dtype T| sized( T ))52 forall(T & | sized( T )) 53 53 static inline __lock_size_t insert_unique( T * array [], __lock_size_t & size, T * val ); 54 54 static inline __lock_size_t count_max ( const __waitfor_mask_t & mask ); … … 949 949 } 950 950 951 forall( dtype T| sized( T ))951 forall(T & | sized( T )) 952 952 static inline __lock_size_t insert_unique( T * array [], __lock_size_t & size, T * val ) { 953 953 if( !val ) return size; -
libcfa/src/concurrency/monitor.hfa
r342af53 r8e4aa05 22 22 #include "stdlib.hfa" 23 23 24 trait is_monitor( dtype T) {24 trait is_monitor(T &) { 25 25 $monitor * get_monitor( T & ); 26 26 void ^?{}( T & mutex ); … … 59 59 void ^?{}( monitor_dtor_guard_t & this ); 60 60 61 static inline forall( dtype T| sized(T) | { void ^?{}( T & mutex ); } )61 static inline forall( T & | sized(T) | { void ^?{}( T & mutex ); } ) 62 62 void delete( T * th ) { 63 ^(*th){};63 if(th) ^(*th){}; 64 64 free( th ); 65 65 } -
libcfa/src/concurrency/mutex.cfa
r342af53 r8e4aa05 164 164 } 165 165 166 forall( dtype L| is_lock(L))166 forall(L & | is_lock(L)) 167 167 void wait(condition_variable & this, L & l) { 168 168 lock( this.lock __cfaabi_dbg_ctx2 ); … … 176 176 //----------------------------------------------------------------------------- 177 177 // Scopes 178 forall( dtype L| is_lock(L))178 forall(L & | is_lock(L)) 179 179 void lock_all ( L * locks[], size_t count) { 180 180 // Sort locks based on addresses … … 188 188 } 189 189 190 forall( dtype L| is_lock(L))190 forall(L & | is_lock(L)) 191 191 void unlock_all( L * locks[], size_t count) { 192 192 // Lock all -
libcfa/src/concurrency/mutex.hfa
r342af53 r8e4aa05 42 42 }; 43 43 44 void ?{}(mutex_lock & this) ;45 void ^?{}(mutex_lock & this) ;46 void lock(mutex_lock & this) ;47 bool try_lock(mutex_lock & this) ;48 void unlock(mutex_lock & this) ;44 void ?{}(mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 45 void ^?{}(mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 46 void lock(mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 47 bool try_lock(mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 48 void unlock(mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 49 49 50 50 // Exclusive lock - recursive … … 64 64 }; 65 65 66 void ?{}(recursive_mutex_lock & this) ;67 void ^?{}(recursive_mutex_lock & this) ;68 void lock(recursive_mutex_lock & this) ;69 bool try_lock(recursive_mutex_lock & this) ;70 void unlock(recursive_mutex_lock & this) ;66 void ?{}(recursive_mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 67 void ^?{}(recursive_mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 68 void lock(recursive_mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 69 bool try_lock(recursive_mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 70 void unlock(recursive_mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 71 71 72 trait is_lock( dtype L| sized(L)) {72 trait is_lock(L & | sized(L)) { 73 73 void lock (L &); 74 74 void unlock(L &); … … 86 86 }; 87 87 88 void ?{}(condition_variable & this) ;89 void ^?{}(condition_variable & this) ;88 void ?{}(condition_variable & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 89 void ^?{}(condition_variable & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 90 90 91 void notify_one(condition_variable & this) ;92 void notify_all(condition_variable & this) ;91 void notify_one(condition_variable & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 92 void notify_all(condition_variable & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 93 93 94 void wait(condition_variable & this) ;94 void wait(condition_variable & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 95 95 96 forall( dtype L| is_lock(L))97 void wait(condition_variable & this, L & l) ;96 forall(L & | is_lock(L)) 97 void wait(condition_variable & this, L & l) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 98 98 99 99 //----------------------------------------------------------------------------- 100 100 // Scopes 101 forall( dtype L| is_lock(L)) {101 forall(L & | is_lock(L)) { 102 102 #if !defined( __TUPLE_ARRAYS_EXIST__ ) 103 103 void lock ( L * locks [], size_t count); -
libcfa/src/concurrency/preemption.cfa
r342af53 r8e4aa05 424 424 static void timeout( $thread * this ) { 425 425 unpark( this ); 426 } 427 428 void __disable_interrupts_hard() { 429 sigset_t oldset; 430 int ret; 431 ret = pthread_sigmask(0, ( const sigset_t * ) 0p, &oldset); // workaround trac#208: cast should be unnecessary 432 if(ret != 0) { abort("ERROR sigprocmask returned %d", ret); } 433 434 ret = sigismember(&oldset, SIGUSR1); 435 if(ret < 0) { abort("ERROR sigismember returned %d", ret); } 436 if(ret == 1) { abort("ERROR SIGUSR1 is disabled"); } 437 438 ret = sigismember(&oldset, SIGALRM); 439 if(ret < 0) { abort("ERROR sigismember returned %d", ret); } 440 if(ret == 0) { abort("ERROR SIGALRM is enabled"); } 441 442 signal_block( SIGUSR1 ); 443 } 444 445 void __enable_interrupts_hard() { 446 signal_unblock( SIGUSR1 ); 447 448 sigset_t oldset; 449 int ret; 450 ret = pthread_sigmask(0, ( const sigset_t * ) 0p, &oldset); // workaround trac#208: cast should be unnecessary 451 if(ret != 0) { abort("ERROR sigprocmask returned %d", ret); } 452 453 ret = sigismember(&oldset, SIGUSR1); 454 if(ret < 0) { abort("ERROR sigismember returned %d", ret); } 455 if(ret == 1) { abort("ERROR SIGUSR1 is disabled"); } 456 457 ret = sigismember(&oldset, SIGALRM); 458 if(ret < 0) { abort("ERROR sigismember returned %d", ret); } 459 if(ret == 0) { abort("ERROR SIGALRM is enabled"); } 426 460 } 427 461 … … 551 585 552 586 // Setup proper signal handlers 553 __cfaabi_sigaction( SIGUSR1, sigHandler_ctxSwitch, SA_SIGINFO | SA_RESTART); // __cfactx_switch handler554 __cfaabi_sigaction( SIGALRM, sigHandler_alarm , SA_SIGINFO | SA_RESTART); // debug handler587 __cfaabi_sigaction( SIGUSR1, sigHandler_ctxSwitch, SA_SIGINFO ); // __cfactx_switch handler 588 __cfaabi_sigaction( SIGALRM, sigHandler_alarm , SA_SIGINFO ); // debug handler 555 589 556 590 signal_block( SIGALRM ); … … 580 614 581 615 __cfaabi_dbg_print_safe( "Kernel : Preemption stopped\n" ); 616 } 617 618 // Prevent preemption since we are about to start terminating things 619 void __kernel_abort_lock(void) { 620 signal_block( SIGUSR1 ); 582 621 } 583 622 -
libcfa/src/concurrency/ready_queue.cfa
r342af53 r8e4aa05 330 330 #if defined(BIAS) 331 331 // Don't bother trying locally too much 332 int local_tries = 8;333 332 preferred = kernelTLS().this_processor->id * 4; 334 333 #endif -
libcfa/src/concurrency/stats.cfa
r342af53 r8e4aa05 25 25 26 26 #if defined(CFA_HAVE_LINUX_IO_URING_H) 27 stats->io.submit_q.submit_avg.rdy = 0; 28 stats->io.submit_q.submit_avg.csm = 0; 29 stats->io.submit_q.submit_avg.cnt = 0; 30 stats->io.submit_q.look_avg.val = 0; 31 stats->io.submit_q.look_avg.cnt = 0; 32 stats->io.submit_q.look_avg.block = 0; 33 stats->io.submit_q.alloc_avg.val = 0; 34 stats->io.submit_q.alloc_avg.cnt = 0; 35 stats->io.submit_q.alloc_avg.block = 0; 36 stats->io.submit_q.helped = 0; 37 stats->io.submit_q.leader = 0; 38 stats->io.submit_q.busy = 0; 39 stats->io.complete_q.completed_avg.val = 0; 40 stats->io.complete_q.completed_avg.cnt = 0; 41 stats->io.complete_q.blocks = 0; 27 stats->io.alloc.fast = 0; 28 stats->io.alloc.slow = 0; 29 stats->io.alloc.fail = 0; 30 stats->io.alloc.revoke = 0; 31 stats->io.alloc.block = 0; 32 stats->io.submit.fast = 0; 33 stats->io.submit.slow = 0; 34 stats->io.flush.external = 0; 35 stats->io.calls.flush = 0; 36 stats->io.calls.submitted = 0; 37 stats->io.calls.drain = 0; 38 stats->io.calls.completed = 0; 39 stats->io.calls.errors.busy = 0; 40 stats->io.poller.sleeps = 0; 42 41 #endif 43 42 } … … 60 59 61 60 #if defined(CFA_HAVE_LINUX_IO_URING_H) 62 __atomic_fetch_add( &cltr->io.submit_q.submit_avg.rdy , proc->io.submit_q.submit_avg.rdy , __ATOMIC_SEQ_CST ); proc->io.submit_q.submit_avg.rdy = 0; 63 __atomic_fetch_add( &cltr->io.submit_q.submit_avg.csm , proc->io.submit_q.submit_avg.csm , __ATOMIC_SEQ_CST ); proc->io.submit_q.submit_avg.csm = 0; 64 __atomic_fetch_add( &cltr->io.submit_q.submit_avg.avl , proc->io.submit_q.submit_avg.avl , __ATOMIC_SEQ_CST ); proc->io.submit_q.submit_avg.avl = 0; 65 __atomic_fetch_add( &cltr->io.submit_q.submit_avg.cnt , proc->io.submit_q.submit_avg.cnt , __ATOMIC_SEQ_CST ); proc->io.submit_q.submit_avg.cnt = 0; 66 __atomic_fetch_add( &cltr->io.submit_q.look_avg.val , proc->io.submit_q.look_avg.val , __ATOMIC_SEQ_CST ); proc->io.submit_q.look_avg.val = 0; 67 __atomic_fetch_add( &cltr->io.submit_q.look_avg.cnt , proc->io.submit_q.look_avg.cnt , __ATOMIC_SEQ_CST ); proc->io.submit_q.look_avg.cnt = 0; 68 __atomic_fetch_add( &cltr->io.submit_q.look_avg.block , proc->io.submit_q.look_avg.block , __ATOMIC_SEQ_CST ); proc->io.submit_q.look_avg.block = 0; 69 __atomic_fetch_add( &cltr->io.submit_q.alloc_avg.val , proc->io.submit_q.alloc_avg.val , __ATOMIC_SEQ_CST ); proc->io.submit_q.alloc_avg.val = 0; 70 __atomic_fetch_add( &cltr->io.submit_q.alloc_avg.cnt , proc->io.submit_q.alloc_avg.cnt , __ATOMIC_SEQ_CST ); proc->io.submit_q.alloc_avg.cnt = 0; 71 __atomic_fetch_add( &cltr->io.submit_q.alloc_avg.block , proc->io.submit_q.alloc_avg.block , __ATOMIC_SEQ_CST ); proc->io.submit_q.alloc_avg.block = 0; 72 __atomic_fetch_add( &cltr->io.submit_q.helped , proc->io.submit_q.helped , __ATOMIC_SEQ_CST ); proc->io.submit_q.helped = 0; 73 __atomic_fetch_add( &cltr->io.submit_q.leader , proc->io.submit_q.leader , __ATOMIC_SEQ_CST ); proc->io.submit_q.leader = 0; 74 __atomic_fetch_add( &cltr->io.submit_q.busy , proc->io.submit_q.busy , __ATOMIC_SEQ_CST ); proc->io.submit_q.busy = 0; 75 __atomic_fetch_add( &cltr->io.complete_q.completed_avg.val, proc->io.complete_q.completed_avg.val, __ATOMIC_SEQ_CST ); proc->io.complete_q.completed_avg.val = 0; 76 __atomic_fetch_add( &cltr->io.complete_q.completed_avg.cnt, proc->io.complete_q.completed_avg.cnt, __ATOMIC_SEQ_CST ); proc->io.complete_q.completed_avg.cnt = 0; 77 __atomic_fetch_add( &cltr->io.complete_q.blocks , proc->io.complete_q.blocks , __ATOMIC_SEQ_CST ); proc->io.complete_q.blocks = 0; 61 __atomic_fetch_add( &cltr->io.alloc.fast , proc->io.alloc.fast , __ATOMIC_SEQ_CST ); proc->io.alloc.fast = 0; 62 __atomic_fetch_add( &cltr->io.alloc.slow , proc->io.alloc.slow , __ATOMIC_SEQ_CST ); proc->io.alloc.slow = 0; 63 __atomic_fetch_add( &cltr->io.alloc.fail , proc->io.alloc.fail , __ATOMIC_SEQ_CST ); proc->io.alloc.fail = 0; 64 __atomic_fetch_add( &cltr->io.alloc.revoke , proc->io.alloc.revoke , __ATOMIC_SEQ_CST ); proc->io.alloc.revoke = 0; 65 __atomic_fetch_add( &cltr->io.alloc.block , proc->io.alloc.block , __ATOMIC_SEQ_CST ); proc->io.alloc.block = 0; 66 __atomic_fetch_add( &cltr->io.submit.fast , proc->io.submit.fast , __ATOMIC_SEQ_CST ); proc->io.submit.fast = 0; 67 __atomic_fetch_add( &cltr->io.submit.slow , proc->io.submit.slow , __ATOMIC_SEQ_CST ); proc->io.submit.slow = 0; 68 __atomic_fetch_add( &cltr->io.flush.external , proc->io.flush.external , __ATOMIC_SEQ_CST ); proc->io.flush.external = 0; 69 __atomic_fetch_add( &cltr->io.calls.flush , proc->io.calls.flush , __ATOMIC_SEQ_CST ); proc->io.calls.flush = 0; 70 __atomic_fetch_add( &cltr->io.calls.submitted , proc->io.calls.submitted , __ATOMIC_SEQ_CST ); proc->io.calls.submitted = 0; 71 __atomic_fetch_add( &cltr->io.calls.drain , proc->io.calls.drain , __ATOMIC_SEQ_CST ); proc->io.calls.drain = 0; 72 __atomic_fetch_add( &cltr->io.calls.completed , proc->io.calls.completed , __ATOMIC_SEQ_CST ); proc->io.calls.completed = 0; 73 __atomic_fetch_add( &cltr->io.calls.errors.busy, proc->io.calls.errors.busy, __ATOMIC_SEQ_CST ); proc->io.calls.errors.busy = 0; 74 __atomic_fetch_add( &cltr->io.poller.sleeps , proc->io.poller.sleeps , __ATOMIC_SEQ_CST ); proc->io.poller.sleeps = 0; 78 75 #endif 79 76 } … … 82 79 83 80 if( flags & CFA_STATS_READY_Q ) { 84 double push_sur = (100.0 * ((double)ready.pick.push.success) / ready.pick.push.attempt);85 double pop_sur = (100.0 * ((double)ready.pick.pop .success) / ready.pick.pop .attempt);86 87 81 double push_len = ((double)ready.pick.push.attempt) / ready.pick.push.success; 88 82 double pop_len = ((double)ready.pick.pop .attempt) / ready.pick.pop .success; 89 90 double lpush_sur = (100.0 * ((double)ready.pick.push.lsuccess) / ready.pick.push.local);91 double lpop_sur = (100.0 * ((double)ready.pick.pop .lsuccess) / ready.pick.pop .local);92 83 93 84 double lpush_len = ((double)ready.pick.push.local) / ready.pick.push.lsuccess; … … 96 87 __cfaabi_bits_print_safe( STDOUT_FILENO, 97 88 "----- %s \"%s\" (%p) - Ready Q Stats -----\n" 98 "- total threads run : %'15" PRIu64 "\n" 99 "- total threads scheduled: %'15" PRIu64 "\n" 100 "- push average probe len : %'18.2lf, %'18.2lf%% (%'15" PRIu64 " attempts)\n" 101 "- pop average probe len : %'18.2lf, %'18.2lf%% (%'15" PRIu64 " attempts)\n" 102 "- local push avg prb len : %'18.2lf, %'18.2lf%% (%'15" PRIu64 " attempts)\n" 103 "- local pop avg prb len : %'18.2lf, %'18.2lf%% (%'15" PRIu64 " attempts)\n" 104 "- thread migrations : %'15" PRIu64 "\n" 105 "- Idle Sleep -\n" 106 "-- halts : %'15" PRIu64 "\n" 107 "-- cancelled halts : %'15" PRIu64 "\n" 108 "-- schedule wake : %'15" PRIu64 "\n" 109 "-- wake on exit : %'15" PRIu64 "\n" 89 "- total threads : %'15" PRIu64 "run, %'15" PRIu64 "schd (%'" PRIu64 "mig )\n" 90 "- push avg probe : %'3.2lf, %'3.2lfl (%'15" PRIu64 " attempts, %'15" PRIu64 " locals)\n" 91 "- pop avg probe : %'3.2lf, %'3.2lfl (%'15" PRIu64 " attempts, %'15" PRIu64 " locals)\n" 92 "- Idle Sleep : %'15" PRIu64 "h, %'15" PRIu64 "c, %'15" PRIu64 "w, %'15" PRIu64 "e\n" 110 93 "\n" 111 94 , type, name, id 112 95 , ready.pick.pop.success 113 96 , ready.pick.push.success 114 , push_len, push_sur, ready.pick.push.attempt115 , pop_len , pop_sur , ready.pick.pop .attempt116 , lpush_len, lpush_sur, ready.pick.push.local117 , lpop_len , lpop_sur , ready.pick.pop .local118 97 , ready.threads.migration 98 , push_len, lpush_len, ready.pick.push.attempt, ready.pick.push.local 99 , pop_len , lpop_len , ready.pick.pop .attempt, ready.pick.pop .local 119 100 , ready.sleep.halts, ready.sleep.cancels, ready.sleep.wakes, ready.sleep.exits 120 101 ); … … 123 104 #if defined(CFA_HAVE_LINUX_IO_URING_H) 124 105 if( flags & CFA_STATS_IO ) { 125 double avgrdy = ((double)io.submit_q.submit_avg.rdy) / io.submit_q.submit_avg.cnt;126 double avg csm = ((double)io.submit_q.submit_avg.csm) / io.submit_q.submit_avg.cnt;106 uint64_t total_allocs = io.alloc.fast + io.alloc.slow; 107 double avgfasta = ((double)io.alloc.fast) / total_allocs; 127 108 128 double lavgv = 0; 129 double lavgb = 0; 130 if(io.submit_q.look_avg.cnt != 0) { 131 lavgv = ((double)io.submit_q.look_avg.val ) / io.submit_q.look_avg.cnt; 132 lavgb = ((double)io.submit_q.look_avg.block) / io.submit_q.look_avg.cnt; 133 } 109 uint64_t total_submits = io.submit.fast + io.submit.slow; 110 double avgfasts = ((double)io.submit.fast) / total_submits; 134 111 135 double aavgv = 0; 136 double aavgb = 0; 137 if(io.submit_q.alloc_avg.cnt != 0) { 138 aavgv = ((double)io.submit_q.alloc_avg.val ) / io.submit_q.alloc_avg.cnt; 139 aavgb = ((double)io.submit_q.alloc_avg.block) / io.submit_q.alloc_avg.cnt; 140 } 112 double avgsubs = ((double)io.calls.submitted) / io.calls.flush; 113 double avgcomp = ((double)io.calls.completed) / io.calls.drain; 141 114 142 115 __cfaabi_bits_print_safe( STDOUT_FILENO, 143 116 "----- %s \"%s\" (%p) - I/O Stats -----\n" 144 "- total submit calls : %'15" PRIu64 "\n" 145 "- avg ready entries : %'18.2lf\n" 146 "- avg submitted entries : %'18.2lf\n" 147 "- total helped entries : %'15" PRIu64 "\n" 148 "- total leader entries : %'15" PRIu64 "\n" 149 "- total busy submit : %'15" PRIu64 "\n" 150 "- total ready search : %'15" PRIu64 "\n" 151 "- avg ready search len : %'18.2lf\n" 152 "- avg ready search block : %'18.2lf\n" 153 "- total alloc search : %'15" PRIu64 "\n" 154 "- avg alloc search len : %'18.2lf\n" 155 "- avg alloc search block : %'18.2lf\n" 156 "- total wait calls : %'15" PRIu64 "\n" 157 "- avg completion/wait : %'18.2lf\n" 158 "- total completion blocks: %'15" PRIu64 "\n" 117 "- total allocations : %'" PRIu64 "f, %'" PRIu64 "s (%'2.2lff) \n" 118 "- failures : %'" PRIu64 "oom, %'" PRIu64 "rvk, %'" PRIu64 "blk\n" 119 "- total submits : %'" PRIu64 "f, %'" PRIu64 "s (%'2.2lf) \n" 120 "- flush external : %'" PRIu64 "\n" 121 "- io_uring_enter : %'" PRIu64 " (%'" PRIu64 ", %'" PRIu64 " EBUSY)\n" 122 "- submits : %'" PRIu64 " (%'.2lf) \n" 123 "- completes : %'" PRIu64 " (%'.2lf) \n" 124 "- poller sleeping : %'" PRIu64 "\n" 159 125 "\n" 160 126 , type, name, id 161 , io.submit_q.submit_avg.cnt 162 , avgrdy, avgcsm 163 , io.submit_q.helped, io.submit_q.leader, io.submit_q.busy 164 , io.submit_q.look_avg.cnt 165 , lavgv, lavgb 166 , io.submit_q.alloc_avg.cnt 167 , aavgv, aavgb 168 , io.complete_q.completed_avg.cnt 169 , ((double)io.complete_q.completed_avg.val) / io.complete_q.completed_avg.cnt 170 , io.complete_q.blocks 127 , io.alloc.fast, io.alloc.slow, avgfasta 128 , io.alloc.fail, io.alloc.revoke, io.alloc.block 129 , io.submit.fast, io.submit.slow, avgfasts 130 , io.flush.external 131 , io.calls.flush, io.calls.drain, io.calls.errors.busy 132 , io.calls.submitted, avgsubs 133 , io.calls.completed, avgcomp 134 , io.poller.sleeps 171 135 ); 172 136 } -
libcfa/src/concurrency/stats.hfa
r342af53 r8e4aa05 2 2 3 3 #include <stdint.h> 4 5 enum { 6 CFA_STATS_READY_Q = 0x01, 7 CFA_STATS_IO = 0x02, 8 }; 4 9 5 10 #if defined(__CFA_NO_STATISTICS__) … … 9 14 static inline void __print_stats( struct __stats_t *, int, const char *, const char *, void * ) {} 10 15 #else 11 enum {12 CFA_STATS_READY_Q = 0x01,13 #if defined(CFA_HAVE_LINUX_IO_URING_H)14 CFA_STATS_IO = 0x02,15 #endif16 };17 16 18 17 struct __attribute__((aligned(64))) __stats_readQ_t { … … 67 66 struct __attribute__((aligned(64))) __stats_io_t{ 68 67 struct { 68 volatile uint64_t fast; 69 volatile uint64_t slow; 70 volatile uint64_t fail; 71 volatile uint64_t revoke; 72 volatile uint64_t block; 73 } alloc; 74 struct { 75 volatile uint64_t fast; 76 volatile uint64_t slow; 77 } submit; 78 struct { 79 volatile uint64_t external; 80 } flush; 81 struct { 82 volatile uint64_t drain; 83 volatile uint64_t completed; 84 volatile uint64_t flush; 85 volatile uint64_t submitted; 69 86 struct { 70 volatile uint64_t rdy; 71 volatile uint64_t csm; 72 volatile uint64_t avl; 73 volatile uint64_t cnt; 74 } submit_avg; 75 struct { 76 volatile uint64_t val; 77 volatile uint64_t cnt; 78 volatile uint64_t block; 79 } look_avg; 80 struct { 81 volatile uint64_t val; 82 volatile uint64_t cnt; 83 volatile uint64_t block; 84 } alloc_avg; 85 volatile uint64_t helped; 86 volatile uint64_t leader; 87 volatile uint64_t busy; 88 } submit_q; 87 volatile uint64_t busy; 88 } errors; 89 } calls; 89 90 struct { 90 struct { 91 volatile uint64_t val; 92 volatile uint64_t cnt; 93 } completed_avg; 94 volatile uint64_t blocks; 95 } complete_q; 91 volatile uint64_t sleeps; 92 } poller; 96 93 }; 97 94 #endif -
libcfa/src/concurrency/thread.cfa
r342af53 r8e4aa05 62 62 } 63 63 64 FORALL_DATA_INSTANCE(ThreadCancelled, ( dtype thread_t), (thread_t))64 FORALL_DATA_INSTANCE(ThreadCancelled, (thread_t &), (thread_t)) 65 65 66 forall( dtype T)66 forall(T &) 67 67 void copy(ThreadCancelled(T) * dst, ThreadCancelled(T) * src) { 68 68 dst->virtual_table = src->virtual_table; … … 71 71 } 72 72 73 forall( dtype T)73 forall(T &) 74 74 const char * msg(ThreadCancelled(T) *) { 75 75 return "ThreadCancelled"; 76 76 } 77 77 78 forall( dtype T)78 forall(T &) 79 79 static void default_thread_cancel_handler(ThreadCancelled(T) & ) { 80 80 abort( "Unhandled thread cancellation.\n" ); 81 81 } 82 82 83 forall( dtype T| is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T)))83 forall(T & | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T))) 84 84 void ?{}( thread_dtor_guard_t & this, 85 T & thrd, void(* defaultResumptionHandler)(ThreadCancelled(T) &)) {86 $monitor * m = get_monitor(thrd);85 T & thrd, void(*cancelHandler)(ThreadCancelled(T) &)) { 86 $monitor * m = get_monitor(thrd); 87 87 $thread * desc = get_thread(thrd); 88 88 89 89 // Setup the monitor guard 90 90 void (*dtor)(T& mutex this) = ^?{}; 91 bool join = defaultResumptionHandler != (void(*)(ThreadCancelled(T)&))0;91 bool join = cancelHandler != (void(*)(ThreadCancelled(T)&))0; 92 92 (this.mg){&m, (void(*)())dtor, join}; 93 93 … … 103 103 } 104 104 desc->state = Cancelled; 105 if (!join) { 106 defaultResumptionHandler = default_thread_cancel_handler; 107 } 105 void(*defaultResumptionHandler)(ThreadCancelled(T) &) = 106 join ? cancelHandler : default_thread_cancel_handler; 108 107 109 108 ThreadCancelled(T) except; … … 125 124 //----------------------------------------------------------------------------- 126 125 // Starting and stopping threads 127 forall( dtype T| is_thread(T) )126 forall( T & | is_thread(T) ) 128 127 void __thrd_start( T & this, void (*main_p)(T &) ) { 129 128 $thread * this_thrd = get_thread(this); … … 141 140 //----------------------------------------------------------------------------- 142 141 // Support for threads that don't ues the thread keyword 143 forall( dtype T| sized(T) | is_thread(T) | { void ?{}(T&); } )142 forall( T & | sized(T) | is_thread(T) | { void ?{}(T&); } ) 144 143 void ?{}( scoped(T)& this ) with( this ) { 145 144 handle{}; … … 147 146 } 148 147 149 forall( dtype T, ttype P| sized(T) | is_thread(T) | { void ?{}(T&, P); } )148 forall( T &, P... | sized(T) | is_thread(T) | { void ?{}(T&, P); } ) 150 149 void ?{}( scoped(T)& this, P params ) with( this ) { 151 150 handle{ params }; … … 153 152 } 154 153 155 forall( dtype T| sized(T) | is_thread(T) )154 forall( T & | sized(T) | is_thread(T) ) 156 155 void ^?{}( scoped(T)& this ) with( this ) { 157 156 ^handle{}; … … 159 158 160 159 //----------------------------------------------------------------------------- 161 forall( dtype T| is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T)))160 forall(T & | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T))) 162 161 T & join( T & this ) { 163 162 thread_dtor_guard_t guard = { this, defaultResumptionHandler }; -
libcfa/src/concurrency/thread.hfa
r342af53 r8e4aa05 26 26 //----------------------------------------------------------------------------- 27 27 // thread trait 28 trait is_thread( dtype T) {28 trait is_thread(T &) { 29 29 void ^?{}(T& mutex this); 30 30 void main(T& this); … … 32 32 }; 33 33 34 FORALL_DATA_EXCEPTION(ThreadCancelled, ( dtype thread_t), (thread_t)) (34 FORALL_DATA_EXCEPTION(ThreadCancelled, (thread_t &), (thread_t)) ( 35 35 thread_t * the_thread; 36 36 exception_t * the_exception; 37 37 ); 38 38 39 forall( dtype T)39 forall(T &) 40 40 void copy(ThreadCancelled(T) * dst, ThreadCancelled(T) * src); 41 41 42 forall( dtype T)42 forall(T &) 43 43 const char * msg(ThreadCancelled(T) *); 44 44 … … 47 47 48 48 // Inline getters for threads/coroutines/monitors 49 forall( dtype T| is_thread(T) )49 forall( T & | is_thread(T) ) 50 50 static inline $coroutine* get_coroutine(T & this) __attribute__((const)) { return &get_thread(this)->self_cor; } 51 51 52 forall( dtype T| is_thread(T) )52 forall( T & | is_thread(T) ) 53 53 static inline $monitor * get_monitor (T & this) __attribute__((const)) { return &get_thread(this)->self_mon; } 54 54 … … 60 60 extern struct cluster * mainCluster; 61 61 62 forall( dtype T| is_thread(T) )62 forall( T & | is_thread(T) ) 63 63 void __thrd_start( T & this, void (*)(T &) ); 64 64 … … 82 82 }; 83 83 84 forall( dtype T| is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T)) )84 forall( T & | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T)) ) 85 85 void ?{}( thread_dtor_guard_t & this, T & thrd, void(*)(ThreadCancelled(T) &) ); 86 86 void ^?{}( thread_dtor_guard_t & this ); … … 89 89 // thread runner 90 90 // Structure that actually start and stop threads 91 forall( dtype T| sized(T) | is_thread(T) )91 forall( T & | sized(T) | is_thread(T) ) 92 92 struct scoped { 93 93 T handle; 94 94 }; 95 95 96 forall( dtype T| sized(T) | is_thread(T) | { void ?{}(T&); } )96 forall( T & | sized(T) | is_thread(T) | { void ?{}(T&); } ) 97 97 void ?{}( scoped(T)& this ); 98 98 99 forall( dtype T, ttype P| sized(T) | is_thread(T) | { void ?{}(T&, P); } )99 forall( T &, P... | sized(T) | is_thread(T) | { void ?{}(T&, P); } ) 100 100 void ?{}( scoped(T)& this, P params ); 101 101 102 forall( dtype T| sized(T) | is_thread(T) )102 forall( T & | sized(T) | is_thread(T) ) 103 103 void ^?{}( scoped(T)& this ); 104 104 … … 115 115 void unpark( $thread * this ); 116 116 117 forall( dtype T| is_thread(T) )117 forall( T & | is_thread(T) ) 118 118 static inline void unpark( T & this ) { if(!&this) return; unpark( get_thread( this ) );} 119 119 … … 128 128 //---------- 129 129 // join 130 forall( dtype T| is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T)) )130 forall( T & | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T)) ) 131 131 T & join( T & this ); 132 132 -
libcfa/src/containers/list.hfa
r342af53 r8e4aa05 66 66 #define __DLISTED_MGD_JUSTIMPL(STRUCT) 67 67 68 forall( dtype tE) {68 forall( tE & ) { 69 69 struct $mgd_link { 70 70 tE *elem; … … 83 83 (this.is_terminator){ 1 }; 84 84 } 85 forall ( otypetInit | { void ?{}( $mgd_link(tE) &, tInit); } )85 forall ( tInit | { void ?{}( $mgd_link(tE) &, tInit); } ) 86 86 static inline void ?=?( $mgd_link(tE) &this, tInit i ) { 87 87 ^?{}( this ); … … 115 115 __DLISTED_MGD_COMMON(STRUCT, STRUCT, $links) 116 116 117 trait $dlistable( dtype Tnode, dtype Telem) {117 trait $dlistable(Tnode &, Telem &) { 118 118 $mgd_link(Telem) & $prev_link(Tnode &); 119 119 $mgd_link(Telem) & $next_link(Tnode &); … … 125 125 }; 126 126 127 forall ( dtype Tnode, dtype Telem| $dlistable(Tnode, Telem)) {127 forall (Tnode &, Telem & | $dlistable(Tnode, Telem)) { 128 128 129 129 // implemented as a sentinel item in an underlying cicrular list -
libcfa/src/containers/maybe.cfa
r342af53 r8e4aa05 18 18 19 19 20 forall( otypeT)20 forall(T) 21 21 void ?{}(maybe(T) & this) { 22 22 this.has_value = false; 23 23 } 24 24 25 forall( otypeT)25 forall(T) 26 26 void ?{}(maybe(T) & this, T value) { 27 27 this.has_value = true; … … 29 29 } 30 30 31 forall( otypeT)31 forall(T) 32 32 void ?{}(maybe(T) & this, maybe(T) other) { 33 33 this.has_value = other.has_value; … … 37 37 } 38 38 39 forall( otypeT)39 forall(T) 40 40 maybe(T) ?=?(maybe(T) & this, maybe(T) that) { 41 41 if (this.has_value && that.has_value) { … … 51 51 } 52 52 53 forall( otypeT)53 forall(T) 54 54 void ^?{}(maybe(T) & this) { 55 55 if (this.has_value) { … … 58 58 } 59 59 60 forall( otypeT)60 forall(T) 61 61 bool ?!=?(maybe(T) this, zero_t) { 62 62 return this.has_value; 63 63 } 64 64 65 forall( otypeT)65 forall(T) 66 66 maybe(T) maybe_value(T value) { 67 67 return (maybe(T)){value}; 68 68 } 69 69 70 forall( otypeT)70 forall(T) 71 71 maybe(T) maybe_none() { 72 72 return (maybe(T)){}; 73 73 } 74 74 75 forall( otypeT)75 forall(T) 76 76 bool has_value(maybe(T) * this) { 77 77 return this->has_value; 78 78 } 79 79 80 forall( otypeT)80 forall(T) 81 81 T get(maybe(T) * this) { 82 82 assertf(this->has_value, "attempt to get from maybe without value"); … … 84 84 } 85 85 86 forall( otypeT)86 forall(T) 87 87 void set(maybe(T) * this, T value) { 88 88 if (this->has_value) { … … 94 94 } 95 95 96 forall( otypeT)96 forall(T) 97 97 void set_none(maybe(T) * this) { 98 98 if (this->has_value) { -
libcfa/src/containers/maybe.hfa
r342af53 r8e4aa05 19 19 20 20 // DO NOT USE DIRECTLY! 21 forall( otypeT)21 forall(T) 22 22 struct maybe { 23 23 bool has_value; … … 26 26 27 27 28 forall( otypeT)28 forall(T) 29 29 void ?{}(maybe(T) & this); 30 30 31 forall( otypeT)31 forall(T) 32 32 void ?{}(maybe(T) & this, T value); 33 33 34 forall( otypeT)34 forall(T) 35 35 void ?{}(maybe(T) & this, maybe(T) other); 36 36 37 forall( otypeT)37 forall(T) 38 38 void ^?{}(maybe(T) & this); 39 39 40 forall( otypeT)40 forall(T) 41 41 maybe(T) ?=?(maybe(T) & this, maybe(T) other); 42 42 43 forall( otypeT)43 forall(T) 44 44 bool ?!=?(maybe(T) this, zero_t); 45 45 46 46 /* Waiting for bug#11 to be fixed. 47 forall( otypeT)47 forall(T) 48 48 maybe(T) maybe_value(T value); 49 49 50 forall( otypeT)50 forall(T) 51 51 maybe(T) maybe_none(); 52 52 */ 53 53 54 forall( otypeT)54 forall(T) 55 55 bool has_value(maybe(T) * this); 56 56 57 forall( otypeT)57 forall(T) 58 58 T get(maybe(T) * this); 59 59 60 forall( otypeT)60 forall(T) 61 61 void set(maybe(T) * this, T value); 62 62 63 forall( otypeT)63 forall(T) 64 64 void set_none(maybe(T) * this); 65 65 -
libcfa/src/containers/pair.cfa
r342af53 r8e4aa05 13 13 #include <containers/pair.hfa> 14 14 15 forall( otype R, otypeS15 forall(R, S 16 16 | { int ?==?(R, R); int ?<?(R, R); int ?<?(S, S); }) 17 17 int ?<?(pair(R, S) p, pair(R, S) q) { … … 19 19 } 20 20 21 forall( otype R, otypeS21 forall(R, S 22 22 | { int ?==?(R, R); int ?<?(R, R); int ?<=?(S, S); }) 23 23 int ?<=?(pair(R, S) p, pair(R, S) q) { … … 25 25 } 26 26 27 forall( otype R, otypeS | { int ?==?(R, R); int ?==?(S, S); })27 forall(R, S | { int ?==?(R, R); int ?==?(S, S); }) 28 28 int ?==?(pair(R, S) p, pair(R, S) q) { 29 29 return p.first == q.first && p.second == q.second; 30 30 } 31 31 32 forall( otype R, otypeS | { int ?!=?(R, R); int ?!=?(S, S); })32 forall(R, S | { int ?!=?(R, R); int ?!=?(S, S); }) 33 33 int ?!=?(pair(R, S) p, pair(R, S) q) { 34 34 return p.first != q.first || p.second != q.second; 35 35 } 36 36 37 forall( otype R, otypeS37 forall(R, S 38 38 | { int ?==?(R, R); int ?>?(R, R); int ?>?(S, S); }) 39 39 int ?>?(pair(R, S) p, pair(R, S) q) { … … 41 41 } 42 42 43 forall( otype R, otypeS43 forall(R, S 44 44 | { int ?==?(R, R); int ?>?(R, R); int ?>=?(S, S); }) 45 45 int ?>=?(pair(R, S) p, pair(R, S) q) { -
libcfa/src/containers/pair.hfa
r342af53 r8e4aa05 16 16 #pragma once 17 17 18 forall( otype R, otypeS) struct pair {18 forall(R, S) struct pair { 19 19 R first; 20 20 S second; 21 21 }; 22 22 23 forall( otype R, otypeS23 forall(R, S 24 24 | { int ?==?(R, R); int ?<?(R, R); int ?<?(S, S); }) 25 25 int ?<?(pair(R, S) p, pair(R, S) q); 26 26 27 forall( otype R, otypeS27 forall(R, S 28 28 | { int ?==?(R, R); int ?<?(R, R); int ?<=?(S, S); }) 29 29 int ?<=?(pair(R, S) p, pair(R, S) q); 30 30 31 forall( otype R, otypeS | { int ?==?(R, R); int ?==?(S, S); })31 forall(R, S | { int ?==?(R, R); int ?==?(S, S); }) 32 32 int ?==?(pair(R, S) p, pair(R, S) q); 33 33 34 forall( otype R, otypeS | { int ?!=?(R, R); int ?!=?(S, S); })34 forall(R, S | { int ?!=?(R, R); int ?!=?(S, S); }) 35 35 int ?!=?(pair(R, S) p, pair(R, S) q); 36 36 37 forall( otype R, otypeS37 forall(R, S 38 38 | { int ?==?(R, R); int ?>?(R, R); int ?>?(S, S); }) 39 39 int ?>?(pair(R, S) p, pair(R, S) q); 40 40 41 forall( otype R, otypeS41 forall(R, S 42 42 | { int ?==?(R, R); int ?>?(R, R); int ?>=?(S, S); }) 43 43 int ?>=?(pair(R, S) p, pair(R, S) q); -
libcfa/src/containers/result.cfa
r342af53 r8e4aa05 18 18 19 19 20 forall( otype T, otypeE)20 forall(T, E) 21 21 void ?{}(result(T, E) & this) { 22 22 this.has_value = false; … … 24 24 } 25 25 26 forall( otype T, otypeE)26 forall(T, E) 27 27 void ?{}(result(T, E) & this, one_t, T value) { 28 28 this.has_value = true; … … 30 30 } 31 31 32 forall( otype T, otypeE)32 forall(T, E) 33 33 void ?{}(result(T, E) & this, zero_t, E error) { 34 34 this.has_value = false; … … 36 36 } 37 37 38 forall( otype T, otypeE)38 forall(T, E) 39 39 void ?{}(result(T, E) & this, result(T, E) other) { 40 40 this.has_value = other.has_value; … … 46 46 } 47 47 48 forall( otype T, otypeE)48 forall(T, E) 49 49 result(T, E) ?=?(result(T, E) & this, result(T, E) that) { 50 50 if (this.has_value && that.has_value) { … … 63 63 } 64 64 65 forall( otype T, otypeE)65 forall(T, E) 66 66 void ^?{}(result(T, E) & this) { 67 67 if (this.has_value) { … … 72 72 } 73 73 74 forall( otype T, otypeE)74 forall(T, E) 75 75 bool ?!=?(result(T, E) this, zero_t) { 76 76 return this.has_value; 77 77 } 78 78 79 forall( otype T, otypeE)79 forall(T, E) 80 80 result(T, E) result_value(T value) { 81 81 return (result(T, E)){1, value}; 82 82 } 83 83 84 forall( otype T, otypeE)84 forall(T, E) 85 85 result(T, E) result_error(E error) { 86 86 return (result(T, E)){0, error}; 87 87 } 88 88 89 forall( otype T, otypeE)89 forall(T, E) 90 90 bool has_value(result(T, E) * this) { 91 91 return this->has_value; 92 92 } 93 93 94 forall( otype T, otypeE)94 forall(T, E) 95 95 T get(result(T, E) * this) { 96 96 assertf(this->has_value, "attempt to get from result without value"); … … 98 98 } 99 99 100 forall( otype T, otypeE)100 forall(T, E) 101 101 E get_error(result(T, E) * this) { 102 102 assertf(!this->has_value, "attempt to get from result without error"); … … 104 104 } 105 105 106 forall( otype T, otypeE)106 forall(T, E) 107 107 void set(result(T, E) * this, T value) { 108 108 if (this->has_value) { … … 115 115 } 116 116 117 forall( otype T, otypeE)117 forall(T, E) 118 118 void set_error(result(T, E) * this, E error) { 119 119 if (this->has_value) { -
libcfa/src/containers/result.hfa
r342af53 r8e4aa05 19 19 20 20 // DO NOT USE DIRECTLY! 21 forall( otype T, otypeE)21 forall(T, E) 22 22 union inner_result{ 23 23 T value; … … 25 25 }; 26 26 27 forall( otype T, otypeE)27 forall(T, E) 28 28 struct result { 29 29 bool has_value; … … 32 32 33 33 34 forall( otype T, otypeE)34 forall(T, E) 35 35 void ?{}(result(T, E) & this); 36 36 37 forall( otype T, otypeE)37 forall(T, E) 38 38 void ?{}(result(T, E) & this, one_t, T value); 39 39 40 forall( otype T, otypeE)40 forall(T, E) 41 41 void ?{}(result(T, E) & this, zero_t, E error); 42 42 43 forall( otype T, otypeE)43 forall(T, E) 44 44 void ?{}(result(T, E) & this, result(T, E) other); 45 45 46 forall( otype T, otypeE)46 forall(T, E) 47 47 void ^?{}(result(T, E) & this); 48 48 49 forall( otype T, otypeE)49 forall(T, E) 50 50 result(T, E) ?=?(result(T, E) & this, result(T, E) other); 51 51 52 forall( otype T, otypeE)52 forall(T, E) 53 53 bool ?!=?(result(T, E) this, zero_t); 54 54 55 55 /* Wating for bug#11 to be fixed. 56 forall( otype T, otypeE)56 forall(T, E) 57 57 result(T, E) result_value(T value); 58 58 59 forall( otype T, otypeE)59 forall(T, E) 60 60 result(T, E) result_error(E error); 61 61 */ 62 62 63 forall( otype T, otypeE)63 forall(T, E) 64 64 bool has_value(result(T, E) * this); 65 65 66 forall( otype T, otypeE)66 forall(T, E) 67 67 T get(result(T, E) * this); 68 68 69 forall( otype T, otypeE)69 forall(T, E) 70 70 E get_error(result(T, E) * this); 71 71 72 forall( otype T, otypeE)72 forall(T, E) 73 73 void set(result(T, E) * this, T value); 74 74 75 forall( otype T, otypeE)75 forall(T, E) 76 76 void set_error(result(T, E) * this, E error); 77 77 -
libcfa/src/containers/stackLockFree.hfa
r342af53 r8e4aa05 9 9 // Created On : Wed May 13 20:58:58 2020 10 10 // Last Modified By : Peter A. Buhr 11 // Last Modified On : Sun Jun 14 13:25:09 202012 // Update Count : 6 411 // Last Modified On : Wed Jan 20 20:40:03 2021 12 // Update Count : 67 13 13 // 14 14 … … 17 17 #include <stdint.h> 18 18 19 forall( dtype T)19 forall( T & ) 20 20 union Link { 21 21 struct { // 32/64-bit x 2 … … 31 31 }; // Link 32 32 33 forall( otypeT | sized(T) | { Link(T) * ?`next( T * ); } ) {33 forall( T | sized(T) | { Link(T) * ?`next( T * ); } ) { 34 34 struct StackLF { 35 35 Link(T) stack; … … 42 42 43 43 void push( StackLF(T) & this, T & n ) with(this) { 44 *( &n )`next = stack; // atomic assignment unnecessary, or use CAA44 *( &n )`next = stack; // atomic assignment unnecessary, or use CAA 45 45 for () { // busy wait 46 46 if ( __atomic_compare_exchange_n( &stack.atom, &( &n )`next->atom, (Link(T))@{ {&n, ( &n )`next->count + 1} }.atom, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) ) break; // attempt to update top node … … 65 65 } 66 66 if( next == 0p ) return false; 67 link = ( next)`next;67 link = ( next )`next; 68 68 } 69 69 } -
libcfa/src/containers/vector.cfa
r342af53 r8e4aa05 18 18 #include <stdlib.hfa> 19 19 20 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))20 forall(T, allocator_t | allocator_c(T, allocator_t)) 21 21 void copy_internal(vector(T, allocator_t)* this, vector(T, allocator_t)* other); 22 22 23 23 //------------------------------------------------------------------------------ 24 24 //Initialization 25 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))25 forall(T, allocator_t | allocator_c(T, allocator_t)) 26 26 void ?{}(vector(T, allocator_t)& this) 27 27 { … … 30 30 } 31 31 32 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))32 forall(T, allocator_t | allocator_c(T, allocator_t)) 33 33 void ?{}(vector(T, allocator_t)& this, vector(T, allocator_t) rhs) 34 34 { … … 37 37 } 38 38 39 // forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))39 // forall(T, allocator_t | allocator_c(T, allocator_t)) 40 40 // vector(T, allocator_t) ?=?(vector(T, allocator_t)* this, vector(T, allocator_t) rhs) 41 41 // { … … 45 45 // } 46 46 47 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))47 forall(T, allocator_t | allocator_c(T, allocator_t)) 48 48 void ^?{}(vector(T, allocator_t)& this) 49 49 { … … 54 54 //------------------------------------------------------------------------------ 55 55 //Modifiers 56 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))56 forall(T, allocator_t | allocator_c(T, allocator_t)) 57 57 void push_back(vector(T, allocator_t)* this, T value) 58 58 { … … 62 62 } 63 63 64 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))64 forall(T, allocator_t | allocator_c(T, allocator_t)) 65 65 void pop_back(vector(T, allocator_t)* this) 66 66 { … … 69 69 } 70 70 71 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))71 forall(T, allocator_t | allocator_c(T, allocator_t)) 72 72 void clear(vector(T, allocator_t)* this) 73 73 { … … 82 82 //Internal Helpers 83 83 84 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))84 forall(T, allocator_t | allocator_c(T, allocator_t)) 85 85 void copy_internal(vector(T, allocator_t)* this, vector(T, allocator_t)* other) 86 86 { … … 93 93 //------------------------------------------------------------------------------ 94 94 //Allocator 95 forall( otypeT)95 forall(T) 96 96 void ?{}(heap_allocator(T)& this) 97 97 { … … 100 100 } 101 101 102 forall( otypeT)102 forall(T) 103 103 void ?{}(heap_allocator(T)& this, heap_allocator(T) rhs) 104 104 { … … 107 107 } 108 108 109 forall( otypeT)109 forall(T) 110 110 heap_allocator(T) ?=?(heap_allocator(T)& this, heap_allocator(T) rhs) 111 111 { … … 115 115 } 116 116 117 forall( otypeT)117 forall(T) 118 118 void ^?{}(heap_allocator(T)& this) 119 119 { … … 121 121 } 122 122 123 forall( otypeT)123 forall(T) 124 124 inline void realloc_storage(heap_allocator(T)* this, size_t size) 125 125 { -
libcfa/src/containers/vector.hfa
r342af53 r8e4aa05 20 20 //------------------------------------------------------------------------------ 21 21 //Allocator 22 forall( otypeT)22 forall(T) 23 23 struct heap_allocator 24 24 { … … 27 27 }; 28 28 29 forall( otypeT)29 forall(T) 30 30 void ?{}(heap_allocator(T)& this); 31 31 32 forall( otypeT)32 forall(T) 33 33 void ?{}(heap_allocator(T)& this, heap_allocator(T) rhs); 34 34 35 forall( otypeT)35 forall(T) 36 36 heap_allocator(T) ?=?(heap_allocator(T)& this, heap_allocator(T) rhs); 37 37 38 forall( otypeT)38 forall(T) 39 39 void ^?{}(heap_allocator(T)& this); 40 40 41 forall( otypeT)41 forall(T) 42 42 void realloc_storage(heap_allocator(T)* this, size_t size); 43 43 44 forall( otypeT)44 forall(T) 45 45 static inline T* data(heap_allocator(T)* this) 46 46 { … … 50 50 //------------------------------------------------------------------------------ 51 51 //Declaration 52 trait allocator_c( otype T, otypeallocator_t)52 trait allocator_c(T, allocator_t) 53 53 { 54 54 void realloc_storage(allocator_t*, size_t); … … 56 56 }; 57 57 58 forall( otype T, otypeallocator_t = heap_allocator(T) | allocator_c(T, allocator_t))58 forall(T, allocator_t = heap_allocator(T) | allocator_c(T, allocator_t)) 59 59 struct vector; 60 60 61 61 //------------------------------------------------------------------------------ 62 62 //Initialization 63 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))63 forall(T, allocator_t | allocator_c(T, allocator_t)) 64 64 void ?{}(vector(T, allocator_t)& this); 65 65 66 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))66 forall(T, allocator_t | allocator_c(T, allocator_t)) 67 67 void ?{}(vector(T, allocator_t)& this, vector(T, allocator_t) rhs); 68 68 69 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))69 forall(T, allocator_t | allocator_c(T, allocator_t)) 70 70 vector(T, allocator_t) ?=?(vector(T, allocator_t)& this, vector(T, allocator_t) rhs); 71 71 72 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))72 forall(T, allocator_t | allocator_c(T, allocator_t)) 73 73 void ^?{}(vector(T, allocator_t)& this); 74 74 75 forall( otype T, otypeallocator_t = heap_allocator(T) | allocator_c(T, allocator_t))75 forall(T, allocator_t = heap_allocator(T) | allocator_c(T, allocator_t)) 76 76 struct vector 77 77 { … … 82 82 //------------------------------------------------------------------------------ 83 83 //Capacity 84 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))84 forall(T, allocator_t | allocator_c(T, allocator_t)) 85 85 static inline bool empty(vector(T, allocator_t)* this) 86 86 { … … 88 88 } 89 89 90 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))90 forall(T, allocator_t | allocator_c(T, allocator_t)) 91 91 static inline size_t size(vector(T, allocator_t)* this) 92 92 { … … 94 94 } 95 95 96 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))96 forall(T, allocator_t | allocator_c(T, allocator_t)) 97 97 static inline void reserve(vector(T, allocator_t)* this, size_t size) 98 98 { … … 102 102 //------------------------------------------------------------------------------ 103 103 //Element access 104 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))104 forall(T, allocator_t | allocator_c(T, allocator_t)) 105 105 static inline T at(vector(T, allocator_t)* this, size_t index) 106 106 { … … 108 108 } 109 109 110 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))110 forall(T, allocator_t | allocator_c(T, allocator_t)) 111 111 static inline T ?[?](vector(T, allocator_t)* this, size_t index) 112 112 { … … 114 114 } 115 115 116 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))116 forall(T, allocator_t | allocator_c(T, allocator_t)) 117 117 static inline T front(vector(T, allocator_t)* this) 118 118 { … … 120 120 } 121 121 122 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))122 forall(T, allocator_t | allocator_c(T, allocator_t)) 123 123 static inline T back(vector(T, allocator_t)* this) 124 124 { … … 128 128 //------------------------------------------------------------------------------ 129 129 //Modifiers 130 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))130 forall(T, allocator_t | allocator_c(T, allocator_t)) 131 131 void push_back(vector(T, allocator_t)* this, T value); 132 132 133 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))133 forall(T, allocator_t | allocator_c(T, allocator_t)) 134 134 void pop_back(vector(T, allocator_t)* this); 135 135 136 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))136 forall(T, allocator_t | allocator_c(T, allocator_t)) 137 137 void clear(vector(T, allocator_t)* this); 138 138 139 139 //------------------------------------------------------------------------------ 140 140 //Iterators 141 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))141 forall(T, allocator_t | allocator_c(T, allocator_t)) 142 142 static inline T* begin(vector(T, allocator_t)* this) 143 143 { … … 145 145 } 146 146 147 // forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))147 // forall(T, allocator_t | allocator_c(T, allocator_t)) 148 148 // static inline const T* cbegin(const vector(T, allocator_t)* this) 149 149 // { … … 151 151 // } 152 152 153 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))153 forall(T, allocator_t | allocator_c(T, allocator_t)) 154 154 static inline T* end(vector(T, allocator_t)* this) 155 155 { … … 157 157 } 158 158 159 // forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))159 // forall(T, allocator_t | allocator_c(T, allocator_t)) 160 160 // static inline const T* cend(const vector(T, allocator_t)* this) 161 161 // { -
libcfa/src/exception.h
r342af53 r8e4aa05 101 101 // implemented in the .c file either so they all have to be inline. 102 102 103 trait is_exception( dtype exceptT, dtype virtualT) {103 trait is_exception(exceptT &, virtualT &) { 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. … … 109 109 }; 110 110 111 trait is_termination_exception( dtype exceptT, dtype virtualT| is_exception(exceptT, virtualT)) {111 trait is_termination_exception(exceptT &, virtualT & | is_exception(exceptT, virtualT)) { 112 112 void defaultTerminationHandler(exceptT &); 113 113 }; 114 114 115 trait is_resumption_exception( dtype exceptT, dtype virtualT| is_exception(exceptT, virtualT)) {115 trait is_resumption_exception(exceptT &, virtualT & | is_exception(exceptT, virtualT)) { 116 116 void defaultResumptionHandler(exceptT &); 117 117 }; 118 118 119 forall( dtype exceptT, dtype virtualT| is_termination_exception(exceptT, virtualT))119 forall(exceptT &, virtualT & | is_termination_exception(exceptT, virtualT)) 120 120 static inline void $throw(exceptT & except) { 121 121 __cfaehm_throw_terminate( … … 125 125 } 126 126 127 forall( dtype exceptT, dtype virtualT| is_resumption_exception(exceptT, virtualT))127 forall(exceptT &, virtualT & | is_resumption_exception(exceptT, virtualT)) 128 128 static inline void $throwResume(exceptT & except) { 129 129 __cfaehm_throw_resume( … … 133 133 } 134 134 135 forall( dtype exceptT, dtype virtualT| is_exception(exceptT, virtualT))135 forall(exceptT &, virtualT & | is_exception(exceptT, virtualT)) 136 136 static inline void cancel_stack(exceptT & except) __attribute__((noreturn)) { 137 137 __cfaehm_cancel_stack( (exception_t *)&except ); 138 138 } 139 139 140 forall( dtype exceptT, dtype virtualT| is_exception(exceptT, virtualT))140 forall(exceptT &, virtualT & | is_exception(exceptT, virtualT)) 141 141 static inline void defaultTerminationHandler(exceptT & except) { 142 142 return cancel_stack( except ); 143 143 } 144 144 145 forall( dtype exceptT, dtype virtualT| is_exception(exceptT, virtualT))145 forall(exceptT &, virtualT & | is_exception(exceptT, virtualT)) 146 146 static inline void defaultResumptionHandler(exceptT & except) { 147 147 throw except; -
libcfa/src/executor.cfa
r342af53 r8e4aa05 7 7 #include <containers/list.hfa> 8 8 9 forall( dtype T| $dlistable(T, T) ) {9 forall( T & | $dlistable(T, T) ) { 10 10 monitor Buffer { // unbounded buffer 11 11 dlist( T, T ) queue; // unbounded list of work requests -
libcfa/src/fstream.cfa
r342af53 r8e4aa05 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Jun 19 16:24:54 202013 // Update Count : 38412 // Last Modified On : Mon Mar 1 21:12:15 2021 13 // Update Count : 424 14 14 // 15 15 … … 25 25 #include <errno.h> // errno 26 26 27 28 27 // *********************************** ofstream *********************************** 29 28 … … 38 37 os.$prt = false; 39 38 os.$sawNL = false; 39 os.$acquired = false; 40 40 $sepSetCur( os, sepGet( os ) ); 41 41 sepSet( os, " " ); … … 109 109 if ( &os == &exit ) exit( EXIT_FAILURE ); 110 110 if ( &os == &abort ) abort(); 111 if ( os.$acquired ) { os.$acquired = false; release( os ); } 111 112 } // ends 112 113 … … 171 172 } // fmt 172 173 174 inline void acquire( ofstream & os ) { 175 lock( os.$lock ); 176 if ( ! os.$acquired ) os.$acquired = true; 177 else unlock( os.$lock ); 178 } // acquire 179 180 inline void release( ofstream & os ) { 181 unlock( os.$lock ); 182 } // release 183 184 void ?{}( osacquire & acq, ofstream & os ) { &acq.os = &os; lock( os.$lock ); } 185 void ^?{}( osacquire & acq ) { release( acq.os ); } 186 173 187 static ofstream soutFile = { (FILE *)stdout }; 174 188 ofstream & sout = soutFile, & stdout = soutFile; … … 176 190 ofstream & serr = serrFile, & stderr = serrFile; 177 191 192 static ofstream lsoutFile = { (FILE *)stdout }; 193 ofstream & lsout = lsoutFile; 194 178 195 static ofstream exitFile = { (FILE *)stdout }; 179 196 ofstream & exit = exitFile; … … 189 206 is.$file = file; 190 207 is.$nlOnOff = false; 208 is.$acquired = false; 191 209 } // ?{} 192 210 … … 213 231 return is.$file == 0p || ferror( (FILE *)(is.$file) ); 214 232 } // fail 233 234 void ends( ifstream & is ) { 235 if ( is.$acquired ) { is.$acquired = false; release( is ); } 236 } // ends 215 237 216 238 int eof( ifstream & is ) { … … 279 301 } // fmt 280 302 303 inline void acquire( ifstream & is ) { 304 lock( is.$lock ); 305 if ( ! is.$acquired ) is.$acquired = true; 306 else unlock( is.$lock ); 307 } // acquire 308 309 inline void release( ifstream & is ) { 310 unlock( is.$lock ); 311 } // release 312 313 void ?{}( isacquire & acq, ifstream & is ) { &acq.is = &is; lock( is.$lock ); } 314 void ^?{}( isacquire & acq ) { release( acq.is ); } 315 281 316 static ifstream sinFile = { (FILE *)stdin }; 282 317 ifstream & sin = sinFile, & stdin = sinFile; -
libcfa/src/fstream.hfa
r342af53 r8e4aa05 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Jun 19 16:29:17 202013 // Update Count : 18912 // Last Modified On : Mon Mar 1 22:45:08 2021 13 // Update Count : 217 14 14 // 15 15 16 16 #pragma once 17 17 18 #include "bits/weakso_locks.hfa" // mutex_lock 18 19 #include "iostream.hfa" 19 20 #include <exception.hfa> … … 34 35 char $separator[sepSize]; 35 36 char $tupleSeparator[sepSize]; 37 multiple_acquisition_lock $lock; 38 bool $acquired; 36 39 }; // ofstream 37 40 … … 69 72 ofstream & write( ofstream &, const char data[], size_t size ); 70 73 int fmt( ofstream &, const char format[], ... ) __attribute__(( format(printf, 2, 3) )); 74 void acquire( ofstream & os ); 75 void release( ofstream & os ); 76 77 struct osacquire { 78 ofstream & os; 79 }; 80 void ?{}( osacquire & acq, ofstream & os ); 81 void ^?{}( osacquire & acq ); 71 82 72 83 void ?{}( ofstream & os ); … … 85 96 void * $file; 86 97 bool $nlOnOff; 98 multiple_acquisition_lock $lock; 99 bool $acquired; 87 100 }; // ifstream 88 101 … … 91 104 void nlOff( ifstream & ); 92 105 bool getANL( ifstream & ); 106 void ends( ifstream & ); 93 107 int fail( ifstream & is ); 94 108 int eof( ifstream & is ); … … 99 113 ifstream & ungetc( ifstream & is, char c ); 100 114 int fmt( ifstream &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) )); 115 void acquire( ifstream & is ); 116 void release( ifstream & is ); 117 118 struct isacquire { 119 ifstream & is; 120 }; 121 void ?{}( isacquire & acq, ifstream & is ); 122 void ^?{}( isacquire & acq ); 101 123 102 124 void ?{}( ifstream & is ); -
libcfa/src/gmp.hfa
r342af53 r8e4aa05 255 255 256 256 // I/O 257 forall( dtype istype| istream( istype ) )257 forall( istype & | istream( istype ) ) 258 258 istype & ?|?( istype & is, Int & mp ) { 259 259 gmp_scanf( "%Zd", &mp ); … … 261 261 } // ?|? 262 262 263 forall( dtype ostype| ostream( ostype ) ) {263 forall( ostype & | ostream( ostype ) ) { 264 264 ostype & ?|?( ostype & os, Int mp ) { 265 265 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); -
libcfa/src/interpose.cfa
r342af53 r8e4aa05 125 125 126 126 // Failure handler 127 __cfaabi_sigaction( SIGSEGV, sigHandler_segv, SA_SIGINFO | SA_ONSTACK ); 128 __cfaabi_sigaction( SIGBUS , sigHandler_segv, SA_SIGINFO | SA_ONSTACK ); 129 __cfaabi_sigaction( SIGILL , sigHandler_ill , SA_SIGINFO | SA_ONSTACK ); 130 __cfaabi_sigaction( SIGFPE , sigHandler_fpe , SA_SIGINFO | SA_ONSTACK ); 131 __cfaabi_sigaction( SIGTERM, sigHandler_term, SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ); // one shot handler, return to default 132 __cfaabi_sigaction( SIGINT , sigHandler_term, SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ); 133 __cfaabi_sigaction( SIGABRT, sigHandler_term, SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ); 134 __cfaabi_sigaction( SIGHUP , sigHandler_term, SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ); // terminal hangup 127 // internal errors 128 __cfaabi_sigaction( SIGSEGV, sigHandler_segv, SA_SIGINFO | SA_ONSTACK ); // Invalid memory reference (default: Core) 129 __cfaabi_sigaction( SIGBUS , sigHandler_segv, SA_SIGINFO | SA_ONSTACK ); // Bus error, bad memory access (default: Core) 130 __cfaabi_sigaction( SIGILL , sigHandler_ill , SA_SIGINFO | SA_ONSTACK ); // Illegal Instruction (default: Core) 131 __cfaabi_sigaction( SIGFPE , sigHandler_fpe , SA_SIGINFO | SA_ONSTACK ); // Floating-point exception (default: Core) 132 133 // handlers to outside errors 134 // reset in-case they insist and send it over and over 135 __cfaabi_sigaction( SIGTERM, sigHandler_term, SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ); // Termination signal (default: Term) 136 __cfaabi_sigaction( SIGINT , sigHandler_term, SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ); // Interrupt from keyboard (default: Term) 137 __cfaabi_sigaction( SIGHUP , sigHandler_term, SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ); // Hangup detected on controlling terminal or death of controlling process (default: Term) 138 __cfaabi_sigaction( SIGQUIT, sigHandler_term, SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ); // Quit from keyboard (default: Core) 139 __cfaabi_sigaction( SIGABRT, sigHandler_term, SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ); // Abort signal from abort(3) (default: Core) 135 140 } 136 141 } … … 163 168 } 164 169 165 void * kernel_abort( void ) __attribute__(( __nothrow__, __leaf__, __weak__ )) { return 0p; } 166 void kernel_abort_msg( void * data, char buffer[], int size) __attribute__(( __nothrow__, __leaf__, __weak__ )) {}167 // See concurrency/kernel.cfa for strong definition used in multi-processor mode. 168 int kernel_abort_lastframe( void ) __attribute__(( __nothrow__, __leaf__, __weak__ )) { return 4; }170 // See concurrency/kernel.cfa and concurrency/preemption.cfa for strong definition used in multi-processor mode. 171 void __kernel_abort_lock( void ) __attribute__(( __nothrow__, __leaf__, __weak__ )) {} 172 void __kernel_abort_msg( char buffer[], int size ) __attribute__(( __nothrow__, __leaf__, __weak__ )) {} 173 int __kernel_abort_lastframe( void ) __attribute__(( __nothrow__, __leaf__, __weak__ )) { return 4; } 169 174 170 175 enum { abort_text_size = 1024 }; … … 173 178 static void __cfaabi_backtrace( int start ) { 174 179 enum { Frames = 50, }; // maximum number of stack frames 175 int last = kernel_abort_lastframe(); // skip last N stack frames180 int last = __kernel_abort_lastframe(); // skip last N stack frames 176 181 177 182 void * array[Frames]; … … 220 225 } 221 226 222 static volatile int __abort_stage= 0;227 static volatile bool __abort_first = 0; 223 228 224 229 // Cannot forward va_list. 225 230 void __abort( bool signalAbort, const char fmt[], va_list args ) { 226 int stage = __atomic_add_fetch( &__abort_stage, 1, __ATOMIC_SEQ_CST ); 227 228 // First stage: stop the cforall kernel and print 229 if(stage == 1) { 230 // increment stage 231 stage = __atomic_add_fetch( &__abort_stage, 1, __ATOMIC_SEQ_CST ); 232 233 // must be done here to lock down kernel 234 void * kernel_data = kernel_abort(); 235 int len; 236 237 signal( SIGABRT, SIG_DFL ); // prevent final "real" abort from recursing to handler 238 239 len = snprintf( abort_text, abort_text_size, "Cforall Runtime error (UNIX pid:%ld) ", (long int)getpid() ); // use UNIX pid (versus getPid) 240 __cfaabi_bits_write( STDERR_FILENO, abort_text, len ); 241 242 assert( fmt ); 243 len = vsnprintf( abort_text, abort_text_size, fmt, args ); 244 __cfaabi_bits_write( STDERR_FILENO, abort_text, len ); 245 246 // add optional newline if missing at the end of the format text 247 if ( fmt[strlen( fmt ) - 1] != '\n' ) { 248 __cfaabi_bits_write( STDERR_FILENO, "\n", 1 ); 249 } // if 250 kernel_abort_msg( kernel_data, abort_text, abort_text_size ); 251 } 252 253 // Second stage: print the backtrace 254 if(stage == 2) { 255 // increment stage 256 stage = __atomic_add_fetch( &__abort_stage, 1, __ATOMIC_SEQ_CST ); 257 258 // print stack trace in handler 259 __cfaabi_backtrace( signalAbort ? 4 : 2 ); 260 } 261 262 do { 263 // Finally call abort 231 // Multiple threads can come here from multiple paths 232 // To make sure this is safe any concurrent/subsequent call to abort is redirected to libc-abort 233 bool first = ! __atomic_test_and_set( &__abort_first, __ATOMIC_SEQ_CST); 234 235 // Prevent preemption from kicking-in and messing with the abort 236 __kernel_abort_lock(); 237 238 // first to abort ? 239 if ( !first ) { 240 // We aren't the first to abort just let C handle it 241 signal( SIGABRT, SIG_DFL ); // restore default in case we came here through the function. 264 242 __cabi_libc.abort(); 265 266 // Loop so that we never return 267 } while(true); 243 } 244 245 int len = snprintf( abort_text, abort_text_size, "Cforall Runtime error (UNIX pid:%ld) ", (long int)getpid() ); // use UNIX pid (versus getPid) 246 __cfaabi_bits_write( STDERR_FILENO, abort_text, len ); 247 248 // print the cause of the error 249 assert( fmt ); 250 len = vsnprintf( abort_text, abort_text_size, fmt, args ); 251 __cfaabi_bits_write( STDERR_FILENO, abort_text, len ); 252 253 // add optional newline if missing at the end of the format text 254 if ( fmt[strlen( fmt ) - 1] != '\n' ) { 255 __cfaabi_bits_write( STDERR_FILENO, "\n", 1 ); 256 } // if 257 258 // Give the kernel the chance to add some data in here 259 __kernel_abort_msg( abort_text, abort_text_size ); 260 261 // print stack trace in handler 262 __cfaabi_backtrace( signalAbort ? 4 : 2 ); 263 264 // Finally call abort 265 __cabi_libc.abort(); 266 268 267 } 269 268 -
libcfa/src/iostream.cfa
r342af53 r8e4aa05 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Aug 24 08:31:35 202013 // Update Count : 11 3012 // Last Modified On : Tue Mar 2 14:51:30 2021 13 // Update Count : 1151 14 14 // 15 15 … … 36 36 37 37 38 forall( dtype ostype| ostream( ostype ) ) {38 forall( ostype & | ostream( ostype ) ) { 39 39 ostype & ?|?( ostype & os, bool b ) { 40 40 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); … … 266 266 } // ?|? 267 267 268 ostype & ?|?( ostype & os, const char s tr[] ) {268 ostype & ?|?( ostype & os, const char s[] ) { 269 269 enum { Open = 1, Close, OpenClose }; 270 270 static const unsigned char mask[256] @= { … … 282 282 }; // mask 283 283 284 if ( s tr[0] == '\0' ) { sepOff( os ); return os; } // null string => no separator284 if ( s[0] == '\0' ) { sepOff( os ); return os; } // null string => no separator 285 285 286 286 // first character IS NOT spacing or closing punctuation => add left separator 287 unsigned char ch = s tr[0]; // must make unsigned287 unsigned char ch = s[0]; // must make unsigned 288 288 if ( $sepPrt( os ) && mask[ ch ] != Close && mask[ ch ] != OpenClose ) { 289 289 fmt( os, "%s", $sepGetCur( os ) ); … … 294 294 295 295 // last character IS spacing or opening punctuation => turn off separator for next item 296 size_t len = strlen( s tr);297 ch = s tr[len - 1]; // must make unsigned296 size_t len = strlen( s ); 297 ch = s[len - 1]; // must make unsigned 298 298 if ( $sepPrt( os ) && mask[ ch ] != Open && mask[ ch ] != OpenClose ) { 299 299 sepOn( os ); … … 302 302 } // if 303 303 if ( ch == '\n' ) $setNL( os, true ); // check *AFTER* $sepPrt call above as it resets NL flag 304 return write( os, str, len ); 305 } // ?|? 306 307 void ?|?( ostype & os, const char str[] ) { 308 (ostype &)(os | str); ends( os ); 309 } // ?|? 310 311 // ostype & ?|?( ostype & os, const char16_t * str ) { 304 return write( os, s, len ); 305 } // ?|? 306 void ?|?( ostype & os, const char s[] ) { 307 (ostype &)(os | s); ends( os ); 308 } // ?|? 309 310 // ostype & ?|?( ostype & os, const char16_t * s ) { 312 311 // if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); 313 // fmt( os, "%ls", s tr);312 // fmt( os, "%ls", s ); 314 313 // return os; 315 314 // } // ?|? 316 315 317 316 // #if ! ( __ARM_ARCH_ISA_ARM == 1 && __ARM_32BIT_STATE == 1 ) // char32_t == wchar_t => ambiguous 318 // ostype & ?|?( ostype & os, const char32_t * s tr) {317 // ostype & ?|?( ostype & os, const char32_t * s ) { 319 318 // if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); 320 // fmt( os, "%ls", s tr);319 // fmt( os, "%ls", s ); 321 320 // return os; 322 321 // } // ?|? 323 322 // #endif // ! ( __ARM_ARCH_ISA_ARM == 1 && __ARM_32BIT_STATE == 1 ) 324 323 325 // ostype & ?|?( ostype & os, const wchar_t * s tr) {324 // ostype & ?|?( ostype & os, const wchar_t * s ) { 326 325 // if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); 327 // fmt( os, "%ls", s tr);326 // fmt( os, "%ls", s ); 328 327 // return os; 329 328 // } // ?|? … … 340 339 // manipulators 341 340 ostype & ?|?( ostype & os, ostype & (* manip)( ostype & ) ) { 342 (ostype &)(manip( os )); 343 return os; 341 return manip( os ); 344 342 } // ?|? 345 343 void ?|?( ostype & os, ostype & (* manip)( ostype & ) ) { 346 (ostype &)(manip( os ));344 manip( os ); 347 345 if ( $getPrt( os ) ) ends( os ); // something printed ? 348 346 $setPrt( os, false ); // turn off … … 399 397 return os; 400 398 } // nlOff 399 400 ostype & acquire( ostype & os ) { 401 acquire( os ); // call void returning 402 return os; 403 } // acquire 401 404 } // distribution 402 405 403 406 // tuples 404 forall( dtype ostype, otype T, ttype Params| writeable( T, ostype ) | { ostype & ?|?( ostype &, Params ); } ) {407 forall( ostype &, T, Params... | writeable( T, ostype ) | { ostype & ?|?( ostype &, Params ); } ) { 405 408 ostype & ?|?( ostype & os, T arg, Params rest ) { 406 409 (ostype &)(os | arg); // print first argument … … 421 424 422 425 // writes the range [begin, end) to the given stream 423 forall( dtype ostype, otype elt_type | writeable( elt_type, ostype ), otypeiterator_type | iterator( iterator_type, elt_type ) ) {426 forall( ostype &, elt_type | writeable( elt_type, ostype ), iterator_type | iterator( iterator_type, elt_type ) ) { 424 427 void write( iterator_type begin, iterator_type end, ostype & os ) { 425 428 void print( elt_type i ) { os | i; } … … 442 445 // Default prefix for non-decimal prints is 0b, 0, 0x. 443 446 #define IntegralFMTImpl( T, IFMTNP, IFMTP ) \ 444 forall( dtype ostype| ostream( ostype ) ) { \447 forall( ostype & | ostream( ostype ) ) { \ 445 448 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 446 449 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); \ … … 517 520 return os; \ 518 521 } /* ?|? */ \ 519 void ?|?( ostype & os, _Ostream_Manip(T) f ) { (ostype &)(os | f); ends( os ); } \ 522 void ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 523 (ostype &)(os | f); ends( os ); \ 524 } /* ?|? */ \ 520 525 } // distribution 521 526 … … 535 540 // Default prefix for non-decimal prints is 0b, 0, 0x. 536 541 #define IntegralFMTImpl128( T, SIGNED, CODE, IFMTNP, IFMTP ) \ 537 forall( dtype ostype| ostream( ostype ) ) \542 forall( ostype & | ostream( ostype ) ) \ 538 543 static void base10_128( ostype & os, _Ostream_Manip(T) f ) { \ 539 544 if ( f.val > UINT64_MAX ) { \ … … 552 557 } /* if */ \ 553 558 } /* base10_128 */ \ 554 forall( dtype ostype| ostream( ostype ) ) { \559 forall( ostype & | ostream( ostype ) ) { \ 555 560 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 556 561 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); \ … … 654 659 #if defined( __SIZEOF_INT128__ ) 655 660 // Default prefix for non-decimal prints is 0b, 0, 0x. 656 forall( dtype ostype| ostream( ostype ) )661 forall( ostype & | ostream( ostype ) ) 657 662 static inline void base_128( ostype & os, unsigned int128 val, unsigned int128 power, _Ostream_Manip(uint64_t) & f, unsigned int maxdig, unsigned int bits, unsigned int cnt = 0 ) { 658 663 int wd = 1; // f.wd is never 0 because 0 implies left-pad … … 719 724 720 725 #define IntegralFMTImpl128( T ) \ 721 forall( dtype ostype| ostream( ostype ) ) { \726 forall( ostype & | ostream( ostype ) ) { \ 722 727 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 723 728 _Ostream_Manip(uint64_t) fmt; \ … … 767 772 768 773 #define FloatingPointFMTImpl( T, DFMTNP, DFMTP ) \ 769 forall( dtype ostype| ostream( ostype ) ) { \774 forall( ostype & | ostream( ostype ) ) { \ 770 775 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 771 776 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); \ … … 801 806 // *********************************** character *********************************** 802 807 803 forall( dtype ostype| ostream( ostype ) ) {808 forall( ostype & | ostream( ostype ) ) { 804 809 ostype & ?|?( ostype & os, _Ostream_Manip(char) f ) { 805 810 if ( f.base != 'c' ) { // bespoke binary/octal/hex format … … 834 839 // *********************************** C string *********************************** 835 840 836 forall( dtype ostype| ostream( ostype ) ) {841 forall( ostype & | ostream( ostype ) ) { 837 842 ostype & ?|?( ostype & os, _Ostream_Manip(const char *) f ) { 838 843 if ( ! f.val ) return os; // null pointer ? … … 882 887 883 888 884 forall( dtype istype| istream( istype ) ) {889 forall( istype & | istream( istype ) ) { 885 890 istype & ?|?( istype & is, bool & b ) { 886 891 char val[6]; … … 894 899 return is; 895 900 } // ?|? 901 void ?|?( istype & is, bool & b ) { 902 (istype &)(is | b); ends( is ); 903 } // ?|? 896 904 897 905 istype & ?|?( istype & is, char & c ) { … … 905 913 return is; 906 914 } // ?|? 915 void ?|?( istype & is, char & c ) { 916 (istype &)(is | c); ends( is ); 917 } // ?|? 907 918 908 919 istype & ?|?( istype & is, signed char & sc ) { … … 910 921 return is; 911 922 } // ?|? 923 void ?|?( istype & is, signed char & sc ) { 924 (istype &)(is | sc); ends( is ); 925 } // ?|? 912 926 913 927 istype & ?|?( istype & is, unsigned char & usc ) { … … 915 929 return is; 916 930 } // ?|? 931 void ?|?( istype & is, unsigned char & usc ) { 932 (istype &)(is | usc); ends( is ); 933 } // ?|? 917 934 918 935 istype & ?|?( istype & is, short int & si ) { … … 920 937 return is; 921 938 } // ?|? 939 void ?|?( istype & is, short int & si ) { 940 (istype &)(is | si); ends( is ); 941 } // ?|? 922 942 923 943 istype & ?|?( istype & is, unsigned short int & usi ) { … … 925 945 return is; 926 946 } // ?|? 947 void ?|?( istype & is, unsigned short int & usi ) { 948 (istype &)(is | usi); ends( is ); 949 } // ?|? 927 950 928 951 istype & ?|?( istype & is, int & i ) { … … 930 953 return is; 931 954 } // ?|? 955 void ?|?( istype & is, int & i ) { 956 (istype &)(is | i); ends( is ); 957 } // ?|? 932 958 933 959 istype & ?|?( istype & is, unsigned int & ui ) { … … 935 961 return is; 936 962 } // ?|? 963 void ?|?( istype & is, unsigned int & ui ) { 964 (istype &)(is | ui); ends( is ); 965 } // ?|? 937 966 938 967 istype & ?|?( istype & is, long int & li ) { … … 940 969 return is; 941 970 } // ?|? 971 void ?|?( istype & is, long int & li ) { 972 (istype &)(is | li); ends( is ); 973 } // ?|? 942 974 943 975 istype & ?|?( istype & is, unsigned long int & ulli ) { … … 945 977 return is; 946 978 } // ?|? 979 void ?|?( istype & is, unsigned long int & ulli ) { 980 (istype &)(is | ulli); ends( is ); 981 } // ?|? 947 982 948 983 istype & ?|?( istype & is, long long int & lli ) { … … 950 985 return is; 951 986 } // ?|? 987 void ?|?( istype & is, long long int & lli ) { 988 (istype &)(is | lli); ends( is ); 989 } // ?|? 952 990 953 991 istype & ?|?( istype & is, unsigned long long int & ulli ) { … … 955 993 return is; 956 994 } // ?|? 995 void & ?|?( istype & is, unsigned long long int & ulli ) { 996 (istype &)(is | ulli); ends( is ); 997 } // ?|? 957 998 958 999 #if defined( __SIZEOF_INT128__ ) 959 istype & ?|?( istype & is, int128 & i128 ) { 960 return (istype &)(is | (unsigned int128 &)i128); 961 } // ?|? 962 963 istype & ?|?( istype & is, unsigned int128 & ui128 ) { 1000 istype & ?|?( istype & is, int128 & llli ) { 1001 return (istype &)(is | (unsigned int128 &)llli); 1002 } // ?|? 1003 void ?|?( istype & is, int128 & llli ) { 1004 (istype &)(is | llli); ends( is ); 1005 } // ?|? 1006 1007 istype & ?|?( istype & is, unsigned int128 & ullli ) { 964 1008 char s[40]; 965 1009 bool sign = false; … … 968 1012 // If the input is too large, the value returned is undefined. If there is no input, no value is returned 969 1013 if ( fmt( is, "%39[0-9]%*[0-9]", s ) == 1 ) { // take first 39 characters, ignore remaining 970 u i128= 0;1014 ullli = 0; 971 1015 for ( unsigned int i = 0; s[i] != '\0'; i += 1 ) { 972 u i128 = ui128* 10 + s[i] - '0';1016 ullli = ullli * 10 + s[i] - '0'; 973 1017 } // for 974 if ( sign ) u i128 = -ui128;1018 if ( sign ) ullli = -ullli; 975 1019 } else if ( sign ) ungetc( is, '-' ); // return minus when no digits 976 1020 return is; 1021 } // ?|? 1022 void ?|?( istype & is, unsigned int128 & ullli ) { 1023 (istype &)(is | ullli); ends( is ); 977 1024 } // ?|? 978 1025 #endif // __SIZEOF_INT128__ … … 982 1029 return is; 983 1030 } // ?|? 1031 void ?|?( istype & is, float & f ) { 1032 (istype &)(is | f); ends( is ); 1033 } // ?|? 984 1034 985 1035 istype & ?|?( istype & is, double & d ) { … … 987 1037 return is; 988 1038 } // ?|? 1039 void ?|?( istype & is, double & d ) { 1040 (istype &)(is | d); ends( is ); 1041 } // ?|? 989 1042 990 1043 istype & ?|?( istype & is, long double & ld ) { … … 992 1045 return is; 993 1046 } // ?|? 994 1047 void ?|?( istype & is, long double & ld ) { 1048 (istype &)(is | ld); ends( is ); 1049 } // ?|? 995 1050 996 1051 istype & ?|?( istype & is, float _Complex & fc ) { … … 1000 1055 return is; 1001 1056 } // ?|? 1057 void ?|?( istype & is, float _Complex & fc ) { 1058 (istype &)(is | fc); ends( is ); 1059 } // ?|? 1002 1060 1003 1061 istype & ?|?( istype & is, double _Complex & dc ) { … … 1007 1065 return is; 1008 1066 } // ?|? 1067 void ?|?( istype & is, double _Complex & dc ) { 1068 (istype &)(is | dc); ends( is ); 1069 } // ?|? 1009 1070 1010 1071 istype & ?|?( istype & is, long double _Complex & ldc ) { … … 1014 1075 return is; 1015 1076 } // ?|? 1077 void ?|?( istype & is, long double _Complex & ldc ) { 1078 (istype &)(is | ldc); ends( is ); 1079 } // ?|? 1016 1080 1017 1081 // istype & ?|?( istype & is, const char fmt[] ) { … … 1020 1084 // } // ?|? 1021 1085 1022 istype & ?|?( istype & is, char * s) {1086 istype & ?|?( istype & is, char s[] ) { 1023 1087 fmt( is, "%s", s ); 1024 1088 return is; 1089 } // ?|? 1090 void ?|?( istype & is, char s[] ) { 1091 (istype &)(is | s); ends( is ); 1025 1092 } // ?|? 1026 1093 … … 1029 1096 return manip( is ); 1030 1097 } // ?|? 1098 void ?|?( istype & is, istype & (* manip)( istype & ) ) { 1099 manip( is ); ends( is ); 1100 } // ?|? 1031 1101 1032 1102 istype & nl( istype & is ) { … … 1044 1114 return is; 1045 1115 } // nlOff 1116 1117 istype & acquire( istype & is ) { 1118 acquire( is ); // call void returning 1119 return is; 1120 } // acquire 1046 1121 } // distribution 1047 1122 1048 1123 // *********************************** manipulators *********************************** 1049 1124 1050 forall( dtype istype | istream( istype ) ) 1051 istype & ?|?( istype & is, _Istream_Cstr f ) { 1052 // skip xxx 1053 if ( ! f.s ) { 1054 // printf( "skip %s %d\n", f.scanset, f.wd ); 1055 if ( f.wd == -1 ) fmt( is, f.scanset, "" ); // no input arguments 1056 else for ( f.wd ) fmt( is, "%*c" ); 1057 return is; 1058 } // if 1059 size_t len = 0; 1060 if ( f.scanset ) len = strlen( f.scanset ); 1061 char fmtstr[len + 16]; 1062 int start = 1; 1063 fmtstr[0] = '%'; 1064 if ( f.flags.ignore ) { fmtstr[1] = '*'; start += 1; } 1065 if ( f.wd != -1 ) { start += sprintf( &fmtstr[start], "%d", f.wd ); } 1066 // cstr %s, %*s, %ws, %*ws 1067 if ( ! f.scanset ) { 1068 fmtstr[start] = 's'; fmtstr[start + 1] = '\0'; 1069 // printf( "cstr %s\n", fmtstr ); 1125 forall( istype & | istream( istype ) ) { 1126 istype & ?|?( istype & is, _Istream_Cstr f ) { 1127 // skip xxx 1128 if ( ! f.s ) { 1129 // printf( "skip %s %d\n", f.scanset, f.wd ); 1130 if ( f.wd == -1 ) fmt( is, f.scanset, "" ); // no input arguments 1131 else for ( f.wd ) fmt( is, "%*c" ); 1132 return is; 1133 } // if 1134 size_t len = 0; 1135 if ( f.scanset ) len = strlen( f.scanset ); 1136 char fmtstr[len + 16]; 1137 int start = 1; 1138 fmtstr[0] = '%'; 1139 if ( f.flags.ignore ) { fmtstr[1] = '*'; start += 1; } 1140 if ( f.wd != -1 ) { start += sprintf( &fmtstr[start], "%d", f.wd ); } 1141 // cstr %s, %*s, %ws, %*ws 1142 if ( ! f.scanset ) { 1143 fmtstr[start] = 's'; fmtstr[start + 1] = '\0'; 1144 // printf( "cstr %s\n", fmtstr ); 1145 fmt( is, fmtstr, f.s ); 1146 return is; 1147 } // if 1148 // incl %[xxx], %*[xxx], %w[xxx], %*w[xxx] 1149 // excl %[^xxx], %*[^xxx], %w[^xxx], %*w[^xxx] 1150 fmtstr[start] = '['; start += 1; 1151 if ( f.flags.inex ) { fmtstr[start] = '^'; start += 1; } 1152 strcpy( &fmtstr[start], f.scanset ); // copy includes '\0' 1153 len += start; 1154 fmtstr[len] = ']'; fmtstr[len + 1] = '\0'; 1155 // printf( "incl/excl %s\n", fmtstr ); 1070 1156 fmt( is, fmtstr, f.s ); 1071 1157 return is; 1072 } // if 1073 // incl %[xxx], %*[xxx], %w[xxx], %*w[xxx] 1074 // excl %[^xxx], %*[^xxx], %w[^xxx], %*w[^xxx] 1075 fmtstr[start] = '['; start += 1; 1076 if ( f.flags.inex ) { fmtstr[start] = '^'; start += 1; } 1077 strcpy( &fmtstr[start], f.scanset ); // copy includes '\0' 1078 len += start; 1079 fmtstr[len] = ']'; fmtstr[len + 1] = '\0'; 1080 // printf( "incl/excl %s\n", fmtstr ); 1081 fmt( is, fmtstr, f.s ); 1082 return is; 1083 } // ?|? 1084 1085 forall( dtype istype | istream( istype ) ) 1086 istype & ?|?( istype & is, _Istream_Char f ) { 1087 fmt( is, "%*c" ); // argument variable unused 1088 return is; 1089 } // ?|? 1158 } // ?|? 1159 void ?|?( istype & is, _Istream_Cstr f ) { 1160 (istype &)(is | f); ends( is ); 1161 } // ?|? 1162 1163 istype & ?|?( istype & is, _Istream_Char f ) { 1164 fmt( is, "%*c" ); // argument variable unused 1165 return is; 1166 } // ?|? 1167 void ?|?( istype & is, _Istream_Char f ) { 1168 (istype &)(is | f); ends( is ); 1169 } // ?|? 1170 } // distribution 1090 1171 1091 1172 #define InputFMTImpl( T, CODE ) \ 1092 forall( dtype istype | istream( istype ) ) \ 1093 istype & ?|?( istype & is, _Istream_Manip(T) f ) { \ 1094 enum { size = 16 }; \ 1095 char fmtstr[size]; \ 1096 if ( f.wd == -1 ) { \ 1097 snprintf( fmtstr, size, "%%%s%s", f.ignore ? "*" : "", CODE ); \ 1098 } else { \ 1099 snprintf( fmtstr, size, "%%%s%d%s", f.ignore ? "*" : "", f.wd, CODE ); \ 1100 } /* if */ \ 1101 /* printf( "%d %s %p\n", f.wd, fmtstr, &f.val ); */ \ 1102 fmt( is, fmtstr, &f.val ); \ 1103 return is; \ 1104 } // ?|? 1173 forall( istype & | istream( istype ) ) { \ 1174 istype & ?|?( istype & is, _Istream_Manip(T) f ) { \ 1175 enum { size = 16 }; \ 1176 char fmtstr[size]; \ 1177 if ( f.wd == -1 ) { \ 1178 snprintf( fmtstr, size, "%%%s%s", f.ignore ? "*" : "", CODE ); \ 1179 } else { \ 1180 snprintf( fmtstr, size, "%%%s%d%s", f.ignore ? "*" : "", f.wd, CODE ); \ 1181 } /* if */ \ 1182 /* printf( "%d %s %p\n", f.wd, fmtstr, &f.val ); */ \ 1183 fmt( is, fmtstr, &f.val ); \ 1184 return is; \ 1185 } /* ?|? */ \ 1186 void ?|?( istype & is, _Istream_Manip(T) f ) { \ 1187 (istype &)(is | f); ends( is ); \ 1188 } /* ?|? */ \ 1189 } // distribution 1105 1190 1106 1191 InputFMTImpl( signed char, "hhi" ) … … 1119 1204 InputFMTImpl( long double, "Lf" ) 1120 1205 1121 forall( dtype istype | istream( istype ) ) 1122 istype & ?|?( istype & is, _Istream_Manip(float _Complex) fc ) { 1123 float re, im; 1124 _Istream_Manip(float) fmtuc @= { re, fc.wd, fc.ignore }; 1125 is | fmtuc; 1126 &fmtuc.val = &im; 1127 is | fmtuc; 1128 if ( ! fc.ignore ) fc.val = re + im * _Complex_I; // re/im are uninitialized for ignore 1129 return is; 1130 } // ?|? 1131 1132 forall( dtype istype | istream( istype ) ) 1133 istype & ?|?( istype & is, _Istream_Manip(double _Complex) dc ) { 1134 double re, im; 1135 _Istream_Manip(double) fmtuc @= { re, dc.wd, dc.ignore }; 1136 is | fmtuc; 1137 &fmtuc.val = &im; 1138 is | fmtuc; 1139 if ( ! dc.ignore ) dc.val = re + im * _Complex_I; // re/im are uninitialized for ignore 1140 return is; 1141 } // ?|? 1142 1143 forall( dtype istype | istream( istype ) ) 1144 istype & ?|?( istype & is, _Istream_Manip(long double _Complex) ldc ) { 1145 long double re, im; 1146 _Istream_Manip(long double) fmtuc @= { re, ldc.wd, ldc.ignore }; 1147 is | fmtuc; 1148 &fmtuc.val = &im; 1149 is | fmtuc; 1150 if ( ! ldc.ignore ) ldc.val = re + im * _Complex_I; // re/im are uninitialized for ignore 1151 return is; 1152 } // ?|? 1206 forall( istype & | istream( istype ) ) { 1207 istype & ?|?( istype & is, _Istream_Manip(float _Complex) fc ) { 1208 float re, im; 1209 _Istream_Manip(float) fmtuc @= { re, fc.wd, fc.ignore }; 1210 is | fmtuc; 1211 &fmtuc.val = &im; 1212 is | fmtuc; 1213 if ( ! fc.ignore ) fc.val = re + im * _Complex_I; // re/im are uninitialized for ignore 1214 return is; 1215 } // ?|? 1216 void ?|?( istype & is, _Istream_Manip(float _Complex) fc ) { 1217 (istype &)(is | fc); ends( is ); 1218 } // ?|? 1219 1220 istype & ?|?( istype & is, _Istream_Manip(double _Complex) dc ) { 1221 double re, im; 1222 _Istream_Manip(double) fmtuc @= { re, dc.wd, dc.ignore }; 1223 is | fmtuc; 1224 &fmtuc.val = &im; 1225 is | fmtuc; 1226 if ( ! dc.ignore ) dc.val = re + im * _Complex_I; // re/im are uninitialized for ignore 1227 return is; 1228 } // ?|? 1229 void ?|?( istype & is, _Istream_Manip(double _Complex) dc ) { 1230 (istype &)(is | dc); ends( is ); 1231 } // ?|? 1232 1233 istype & ?|?( istype & is, _Istream_Manip(long double _Complex) ldc ) { 1234 long double re, im; 1235 _Istream_Manip(long double) fmtuc @= { re, ldc.wd, ldc.ignore }; 1236 is | fmtuc; 1237 &fmtuc.val = &im; 1238 is | fmtuc; 1239 if ( ! ldc.ignore ) ldc.val = re + im * _Complex_I; // re/im are uninitialized for ignore 1240 return is; 1241 } // ?|? 1242 void ?|?( istype & is, _Istream_Manip(long double _Complex) ldc ) { 1243 (istype &)(is | ldc); ends( is ); 1244 } // ?|? 1245 } // distribution 1153 1246 1154 1247 // Local Variables: // -
libcfa/src/iostream.hfa
r342af53 r8e4aa05 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Aug 11 22:16:14 202013 // Update Count : 3 5012 // Last Modified On : Tue Mar 2 14:05:08 2021 13 // Update Count : 369 14 14 // 15 15 … … 22 22 23 23 24 trait ostream( dtype ostype) {24 trait ostream( ostype & ) { 25 25 // private 26 26 bool $sepPrt( ostype & ); // get separator state (on/off) … … 54 54 ostype & write( ostype &, const char [], size_t ); 55 55 int fmt( ostype &, const char format[], ... ) __attribute__(( format(printf, 2, 3) )); 56 void acquire( ostype & ); 56 57 }; // ostream 57 58 58 // trait writeable( otypeT ) {59 // forall( dtype ostype| ostream( ostype ) ) ostype & ?|?( ostype &, T );59 // trait writeable( T ) { 60 // forall( ostype & | ostream( ostype ) ) ostype & ?|?( ostype &, T ); 60 61 // }; // writeable 61 62 62 trait writeable( otype T, dtype ostype| ostream( ostype ) ) {63 trait writeable( T, ostype & | ostream( ostype ) ) { 63 64 ostype & ?|?( ostype &, T ); 64 65 }; // writeable … … 66 67 // implement writable for intrinsic types 67 68 68 forall( dtype ostype| ostream( ostype ) ) {69 forall( ostype & | ostream( ostype ) ) { 69 70 ostype & ?|?( ostype &, bool ); 70 71 void ?|?( ostype &, bool ); … … 137 138 ostype & nlOn( ostype & ); 138 139 ostype & nlOff( ostype & ); 140 ostype & acquire( ostype & ); 139 141 } // distribution 140 142 141 143 // tuples 142 forall( dtype ostype, otype T, ttype Params| writeable( T, ostype ) | { ostype & ?|?( ostype &, Params ); } ) {144 forall( ostype &, T, Params... | writeable( T, ostype ) | { ostype & ?|?( ostype &, Params ); } ) { 143 145 ostype & ?|?( ostype & os, T arg, Params rest ); 144 146 void ?|?( ostype & os, T arg, Params rest ); … … 146 148 147 149 // writes the range [begin, end) to the given stream 148 forall( dtype ostype, otype elt_type | writeable( elt_type, ostype ), otypeiterator_type | iterator( iterator_type, elt_type ) ) {150 forall( ostype &, elt_type | writeable( elt_type, ostype ), iterator_type | iterator( iterator_type, elt_type ) ) { 149 151 void write( iterator_type begin, iterator_type end, ostype & os ); 150 152 void write_reverse( iterator_type begin, iterator_type end, ostype & os ); … … 153 155 // *********************************** manipulators *********************************** 154 156 155 forall( otypeT )157 forall( T ) 156 158 struct _Ostream_Manip { 157 159 T val; // polymorphic base-type … … 193 195 _Ostream_Manip(T) & sign( _Ostream_Manip(T) & fmt ) { fmt.flags.sign = true; return fmt; } \ 194 196 } /* distribution */ \ 195 forall( dtype ostype| ostream( ostype ) ) { \197 forall( ostype & | ostream( ostype ) ) { \ 196 198 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ); \ 197 199 void ?|?( ostype & os, _Ostream_Manip(T) f ); \ … … 234 236 _Ostream_Manip(T) & nodp( _Ostream_Manip(T) & fmt ) { fmt.flags.nobsdp = true; return fmt; } \ 235 237 } /* distribution */ \ 236 forall( dtype ostype| ostream( ostype ) ) { \238 forall( ostype & | ostream( ostype ) ) { \ 237 239 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ); \ 238 240 void ?|?( ostype & os, _Ostream_Manip(T) f ); \ … … 254 256 _Ostream_Manip(char) & nobase( _Ostream_Manip(char) & fmt ) { fmt.flags.nobsdp = true; return fmt; } 255 257 } // distribution 256 forall( dtype ostype| ostream( ostype ) ) {258 forall( ostype & | ostream( ostype ) ) { 257 259 ostype & ?|?( ostype & os, _Ostream_Manip(char) f ); 258 260 void ?|?( ostype & os, _Ostream_Manip(char) f ); … … 272 274 _Ostream_Manip(const char *) & nobase( _Ostream_Manip(const char *) & fmt ) { fmt.flags.nobsdp = true; return fmt; } 273 275 } // distribution 274 forall( dtype ostype| ostream( ostype ) ) {276 forall( ostype & | ostream( ostype ) ) { 275 277 ostype & ?|?( ostype & os, _Ostream_Manip(const char *) f ); 276 278 void ?|?( ostype & os, _Ostream_Manip(const char *) f ); … … 281 283 282 284 283 trait istream( dtype istype) {285 trait istream( istype & ) { 284 286 void nlOn( istype & ); // read newline 285 287 void nlOff( istype & ); // scan newline 286 288 bool getANL( istype & ); // get scan newline (on/off) 289 290 void ends( istype & os ); // end of output statement 287 291 int fail( istype & ); 288 292 int eof( istype & ); … … 292 296 istype & ungetc( istype &, char ); 293 297 int fmt( istype &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) )); 298 void acquire( istype & ); 294 299 }; // istream 295 300 296 trait readable( otypeT ) {297 forall( dtype istype| istream( istype ) ) istype & ?|?( istype &, T );301 trait readable( T ) { 302 forall( istype & | istream( istype ) ) istype & ?|?( istype &, T ); 298 303 }; // readable 299 304 300 forall( dtype istype| istream( istype ) ) {305 forall( istype & | istream( istype ) ) { 301 306 istype & ?|?( istype &, bool & ); 307 void ?|?( istype &, bool & ); 302 308 303 309 istype & ?|?( istype &, char & ); 310 void ?|?( istype &, char & ); 304 311 istype & ?|?( istype &, signed char & ); 312 void ?|?( istype &, signed char & ); 305 313 istype & ?|?( istype &, unsigned char & ); 314 void ?|?( istype &, unsigned char & ); 306 315 307 316 istype & ?|?( istype &, short int & ); 317 void ?|?( istype &, short int & ); 308 318 istype & ?|?( istype &, unsigned short int & ); 319 void ?|?( istype &, unsigned short int & ); 309 320 istype & ?|?( istype &, int & ); 321 void ?|?( istype &, int & ); 310 322 istype & ?|?( istype &, unsigned int & ); 323 void ?|?( istype &, unsigned int & ); 311 324 istype & ?|?( istype &, long int & ); 325 void ?|?( istype &, long int & ); 312 326 istype & ?|?( istype &, unsigned long int & ); 327 void ?|?( istype &, unsigned long int & ); 313 328 istype & ?|?( istype &, long long int & ); 329 void ?|?( istype &, long long int & ); 314 330 istype & ?|?( istype &, unsigned long long int & ); 331 void ?|?( istype &, unsigned long long int & ); 315 332 #if defined( __SIZEOF_INT128__ ) 316 333 istype & ?|?( istype &, int128 & ); 334 void ?|?( istype &, int128 & ); 317 335 istype & ?|?( istype &, unsigned int128 & ); 336 void ?|?( istype &, unsigned int128 & ); 318 337 #endif // __SIZEOF_INT128__ 319 338 320 339 istype & ?|?( istype &, float & ); 340 void ?|?( istype &, float & ); 321 341 istype & ?|?( istype &, double & ); 342 void ?|?( istype &, double & ); 322 343 istype & ?|?( istype &, long double & ); 344 void ?|?( istype &, long double & ); 323 345 324 346 istype & ?|?( istype &, float _Complex & ); 347 void ?|?( istype &, float _Complex & ); 325 348 istype & ?|?( istype &, double _Complex & ); 349 void ?|?( istype &, double _Complex & ); 326 350 istype & ?|?( istype &, long double _Complex & ); 351 void ?|?( istype &, long double _Complex & ); 327 352 328 353 // istype & ?|?( istype &, const char [] ); 329 istype & ?|?( istype &, char * ); 354 istype & ?|?( istype &, char [] ); 355 void ?|?( istype &, char [] ); 330 356 331 357 // manipulators 332 358 istype & ?|?( istype &, istype & (*)( istype & ) ); 359 void ?|?( istype &, istype & (*)( istype & ) ); 333 360 istype & nl( istype & is ); 334 361 istype & nlOn( istype & ); 335 362 istype & nlOff( istype & ); 363 istype & acquire( istype & ); 336 364 } // distribution 337 365 … … 363 391 _Istream_Cstr & wdi( unsigned int w, _Istream_Cstr & fmt ) { fmt.wd = w; return fmt; } 364 392 } // distribution 365 forall( dtype istype | istream( istype ) ) istype & ?|?( istype & is, _Istream_Cstr f ); 393 forall( istype & | istream( istype ) ) { 394 istype & ?|?( istype & is, _Istream_Cstr f ); 395 void ?|?( istype & is, _Istream_Cstr f ); 396 } 366 397 367 398 struct _Istream_Char { … … 373 404 _Istream_Char & ignore( _Istream_Char & fmt ) { fmt.ignore = true; return fmt; } 374 405 } // distribution 375 forall( dtype istype | istream( istype ) ) istype & ?|?( istype & is, _Istream_Char f ); 376 377 forall( dtype T | sized( T ) ) 406 forall( istype & | istream( istype ) ) { 407 istype & ?|?( istype & is, _Istream_Char f ); 408 void ?|?( istype & is, _Istream_Char f ); 409 } 410 411 forall( T & | sized( T ) ) 378 412 struct _Istream_Manip { 379 413 T & val; // polymorphic base-type … … 389 423 _Istream_Manip(T) & wdi( unsigned int w, _Istream_Manip(T) & fmt ) { fmt.wd = w; return fmt; } \ 390 424 } /* distribution */ \ 391 forall( dtype istype| istream( istype ) ) { \425 forall( istype & | istream( istype ) ) { \ 392 426 istype & ?|?( istype & is, _Istream_Manip(T) f ); \ 427 void ?|?( istype & is, _Istream_Manip(T) f ); \ 393 428 } // ?|? 394 429 … … 418 453 #include <time_t.hfa> // Duration (constructors) / Time (constructors) 419 454 420 forall( dtype ostype| ostream( ostype ) ) {455 forall( ostype & | ostream( ostype ) ) { 421 456 ostype & ?|?( ostype & os, Duration dur ); 422 457 void ?|?( ostype & os, Duration dur ); -
libcfa/src/iterator.cfa
r342af53 r8e4aa05 16 16 #include "iterator.hfa" 17 17 18 forall( otype iterator_type, otypeelt_type | iterator( iterator_type, elt_type ) )18 forall( iterator_type, elt_type | iterator( iterator_type, elt_type ) ) 19 19 void for_each( iterator_type begin, iterator_type end, void (* func)( elt_type ) ) { 20 20 for ( iterator_type i = begin; i != end; ++i ) { … … 23 23 } // for_each 24 24 25 forall( otype iterator_type, otypeelt_type | iterator( iterator_type, elt_type ) )25 forall( iterator_type, elt_type | iterator( iterator_type, elt_type ) ) 26 26 void for_each_reverse( iterator_type begin, iterator_type end, void (* func)( elt_type ) ) { 27 27 for ( iterator_type i = end; i != begin; ) { -
libcfa/src/iterator.hfa
r342af53 r8e4aa05 17 17 18 18 // An iterator can be used to traverse a data structure. 19 trait iterator( otype iterator_type, otypeelt_type ) {19 trait iterator( iterator_type, elt_type ) { 20 20 // point to the next element 21 21 // iterator_type ?++( iterator_type & ); … … 31 31 }; 32 32 33 trait iterator_for( otype iterator_type, otype collection_type, otypeelt_type | iterator( iterator_type, elt_type ) ) {33 trait iterator_for( iterator_type, collection_type, elt_type | iterator( iterator_type, elt_type ) ) { 34 34 // [ iterator_type begin, iterator_type end ] get_iterators( collection_type ); 35 35 iterator_type begin( collection_type ); … … 37 37 }; 38 38 39 forall( otype iterator_type, otypeelt_type | iterator( iterator_type, elt_type ) )39 forall( iterator_type, elt_type | iterator( iterator_type, elt_type ) ) 40 40 void for_each( iterator_type begin, iterator_type end, void (* func)( elt_type ) ); 41 41 42 forall( otype iterator_type, otypeelt_type | iterator( iterator_type, elt_type ) )42 forall( iterator_type, elt_type | iterator( iterator_type, elt_type ) ) 43 43 void for_each_reverse( iterator_type begin, iterator_type end, void (* func)( elt_type ) ); 44 44 -
libcfa/src/math.hfa
r342af53 r8e4aa05 286 286 unsigned long long int floor( unsigned long long int n, unsigned long long int align ) { return n / align * align; } 287 287 288 // forall( otypeT | { T ?/?( T, T ); T ?*?( T, T ); } )288 // forall( T | { T ?/?( T, T ); T ?*?( T, T ); } ) 289 289 // T floor( T n, T align ) { return n / align * align; } 290 290 … … 300 300 unsigned long long int ceiling_div( unsigned long long int n, unsigned long long int align ) { return (n + (align - 1)) / align; } 301 301 302 // forall( otypeT | { T ?+?( T, T ); T ?-?( T, T ); T ?%?( T, T ); } )302 // forall( T | { T ?+?( T, T ); T ?-?( T, T ); T ?%?( T, T ); } ) 303 303 // T ceiling_div( T n, T align ) { verify( is_pow2( align ) );return (n + (align - 1)) / align; } 304 304 … … 315 315 unsigned long long int ceiling( unsigned long long int n, unsigned long long int align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); } 316 316 317 // forall( otypeT | { void ?{}( T &, one_t ); T ?+?( T, T ); T ?-?( T, T ); T ?/?( T, T ); } )317 // forall( T | { void ?{}( T &, one_t ); T ?+?( T, T ); T ?-?( T, T ); T ?/?( T, T ); } ) 318 318 // T ceiling( T n, T align ) { return return floor( n + (n % align != 0 ? align - 1 : 0), align ); *} 319 319 … … 414 414 415 415 static inline { 416 forall( otypeT | { void ?{}( T &, one_t ); T ?+?( T, T ); T ?-?( T, T );T ?*?( T, T ); } )416 forall( T | { void ?{}( T &, one_t ); T ?+?( T, T ); T ?-?( T, T );T ?*?( T, T ); } ) 417 417 T lerp( T x, T y, T a ) { return x * ((T){1} - a) + y * a; } 418 418 419 forall( otypeT | { void ?{}( T &, zero_t ); void ?{}( T &, one_t ); int ?<?( T, T ); } )419 forall( T | { void ?{}( T &, zero_t ); void ?{}( T &, one_t ); int ?<?( T, T ); } ) 420 420 T step( T edge, T x ) { return x < edge ? (T){0} : (T){1}; } 421 421 422 forall( otypeT | { void ?{}( T &, int ); T clamp( T, T, T ); T ?-?( T, T ); T ?*?( T, T ); T ?/?( T, T ); } )422 forall( T | { void ?{}( T &, int ); T clamp( T, T, T ); T ?-?( T, T ); T ?*?( T, T ); T ?/?( T, T ); } ) 423 423 T smoothstep( T edge0, T edge1, T x ) { T t = clamp( (x - edge0) / (edge1 - edge0), (T){0}, (T){1} ); return t * t * ((T){3} - (T){2} * t); } 424 424 } // distribution -
libcfa/src/memory.cfa
r342af53 r8e4aa05 10 10 // Created On : Tue Jun 2 16:48:00 2020 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Tue Jun 3 12:30:00 202013 // Update Count : 012 // Last Modified On : Mon Feb 1 16:10:00 2021 13 // Update Count : 1 14 14 // 15 15 … … 18 18 19 19 // Internal data object. 20 forall( dtype T | sized(T), ttype Args| { void ?{}(T &, Args); })20 forall(T & | sized(T), Args... | { void ?{}(T &, Args); }) 21 21 void ?{}(counter_data(T) & this, Args args) { 22 22 (this.counter){1}; … … 24 24 } 25 25 26 forall( dtype T| sized(T) | { void ^?{}(T &); })26 forall(T & | sized(T) | { void ^?{}(T &); }) 27 27 void ^?{}(counter_data(T) & this) { 28 28 assert(0 == this.counter); … … 31 31 32 32 // This is one of many pointers keeping this alive. 33 forall( dtype T| sized(T))33 forall(T & | sized(T)) 34 34 void ?{}(counter_ptr(T) & this) { 35 35 this.data = 0p; 36 36 } 37 37 38 forall( dtype T| sized(T))38 forall(T & | sized(T)) 39 39 void ?{}(counter_ptr(T) & this, zero_t) { 40 40 this.data = 0p; 41 41 } 42 42 43 forall( dtype T| sized(T) | { void ^?{}(T &); })43 forall(T & | sized(T) | { void ^?{}(T &); }) 44 44 static void internal_decrement(counter_ptr(T) & this) { 45 45 if (this.data && 0 == --this.data->counter) { … … 48 48 } 49 49 50 forall( dtype T| sized(T))50 forall(T & | sized(T)) 51 51 static void internal_copy(counter_ptr(T) & this, counter_ptr(T) & that) { 52 52 this.data = that.data; … … 56 56 } 57 57 58 forall( dtype T | sized(T) | { void ^?{}(T &); })58 forall(T & | sized(T)) 59 59 void ?{}(counter_ptr(T) & this, counter_ptr(T) that) { 60 60 // `that` is a copy but it should have neither a constructor 61 61 // nor destructor run on it so it shouldn't need adjustment. 62 internal_decrement(this);63 62 internal_copy(this, that); 64 63 } 65 64 66 forall( dtype T | sized(T), ttype Args| { void ?{}(T&, Args); })65 forall(T & | sized(T), Args... | { void ?{}(T&, Args); }) 67 66 void ?{}(counter_ptr(T) & this, Args args) { 68 this.data = (counter_data(T)*)new(args); 67 this.data = malloc(); 68 this.data->counter = 1; 69 (this.data->object){args}; 69 70 } 70 71 71 forall( dtype T| sized(T) | { void ^?{}(T &); })72 forall(T & | sized(T) | { void ^?{}(T &); }) 72 73 void ^?{}(counter_ptr(T) & this) { 73 74 internal_decrement(this); 74 75 } 75 76 76 forall( dtype T| sized(T))77 forall(T & | sized(T)) 77 78 T & *?(counter_ptr(T) & this) { 78 79 return *((this.data) ? &this.data->object : 0p); 79 80 } 80 81 81 forall( dtype T| sized(T) | { void ^?{}(T &); })82 forall(T & | sized(T) | { void ^?{}(T &); }) 82 83 void ?=?(counter_ptr(T) & this, counter_ptr(T) that) { 83 84 if (this.data != that.data) { … … 87 88 } 88 89 89 forall( dtype T| sized(T) | { void ^?{}(T &); })90 forall(T & | sized(T) | { void ^?{}(T &); }) 90 91 void ?=?(counter_ptr(T) & this, zero_t) { 91 92 internal_decrement(this); … … 93 94 } 94 95 95 forall( dtype T| sized(T))96 forall(T & | sized(T)) 96 97 int ?==?(counter_ptr(T) const & this, counter_ptr(T) const & that) { 97 98 return this.data == that.data; 98 99 } 99 100 100 forall( dtype T| sized(T))101 forall(T & | sized(T)) 101 102 int ?!=?(counter_ptr(T) const & this, counter_ptr(T) const & that) { 102 103 return !?==?(this, that); 103 104 } 104 105 105 forall( dtype T| sized(T))106 forall(T & | sized(T)) 106 107 int ?==?(counter_ptr(T) const & this, zero_t) { 107 108 return this.data == 0; 108 109 } 109 110 110 forall( dtype T| sized(T))111 forall(T & | sized(T)) 111 112 int ?!=?(counter_ptr(T) const & this, zero_t) { 112 113 return !?==?(this, (zero_t)0); … … 114 115 115 116 // This is the only pointer that keeps this alive. 116 forall( dtype T)117 forall(T &) 117 118 void ?{}(unique_ptr(T) & this) { 118 119 this.data = 0p; 119 120 } 120 121 121 forall( dtype T)122 forall(T &) 122 123 void ?{}(unique_ptr(T) & this, zero_t) { 123 124 this.data = 0p; 124 125 } 125 126 126 forall( dtype T | sized(T), ttype Args| { void ?{}(T &, Args); })127 forall(T & | sized(T), Args... | { void ?{}(T &, Args); }) 127 128 void ?{}(unique_ptr(T) & this, Args args) { 128 this.data = (T *)new(args); 129 this.data = malloc(); 130 (*this.data){args}; 129 131 } 130 132 131 forall( dtype T| { void ^?{}(T &); })133 forall(T & | { void ^?{}(T &); }) 132 134 void ^?{}(unique_ptr(T) & this) { 133 135 delete(this.data); 134 136 } 135 137 136 forall( dtype T)138 forall(T &) 137 139 T & *?(unique_ptr(T) & this) { 138 140 return *this.data; 139 141 } 140 142 141 forall( dtype T| { void ^?{}(T &); })143 forall(T & | { void ^?{}(T &); }) 142 144 void ?=?(unique_ptr(T) & this, zero_t) { 143 145 delete(this.data); … … 145 147 } 146 148 147 forall( dtype T| { void ^?{}(T &); })149 forall(T & | { void ^?{}(T &); }) 148 150 void move(unique_ptr(T) & this, unique_ptr(T) & that) { 149 151 delete(this.data); … … 152 154 } 153 155 154 forall( dtype T)156 forall(T &) 155 157 int ?==?(unique_ptr(T) const & this, unique_ptr(T) const & that) { 156 158 return this.data == that.data; 157 159 } 158 160 159 forall( dtype T)161 forall(T &) 160 162 int ?!=?(unique_ptr(T) const & this, unique_ptr(T) const & that) { 161 163 return !?==?(this, that); 162 164 } 163 165 164 forall( dtype T)166 forall(T &) 165 167 int ?==?(unique_ptr(T) const & this, zero_t) { 166 168 return this.data == 0; 167 169 } 168 170 169 forall( dtype T)171 forall(T &) 170 172 int ?!=?(unique_ptr(T) const & this, zero_t) { 171 173 return !?==?(this, (zero_t)0); -
libcfa/src/memory.hfa
r342af53 r8e4aa05 10 10 // Created On : Tue Jun 2 16:48:00 2020 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Tue Jun 3 12:29:00 202013 // Update Count : 012 // Last Modified On : Fri Jan 29 15:52:00 2021 13 // Update Count : 1 14 14 // 15 15 … … 17 17 18 18 // Internal data object. 19 forall( dtype T | sized(T)) {20 21 22 23 19 forall(T & | sized(T)) 20 struct counter_data { 21 unsigned int counter; 22 T object; 23 }; 24 24 25 forall(ttype Args| { void ?{}(T &, Args); })26 25 forall(T & | sized(T), Args... | { void ?{}(T &, Args); }) 26 void ?{}(counter_data(T) & this, Args args); 27 27 28 forall( | { void ^?{}(T &); }) 29 void ^?{}(counter_data(T) & this); 30 } 28 forall(T & | sized(T) | { void ^?{}(T &); }) 29 void ^?{}(counter_data(T) & this); 31 30 32 31 // This is one of many pointers keeping this alive. 33 forall( dtype T | sized(T)) {34 35 36 32 forall(T & | sized(T)) 33 struct counter_ptr { 34 counter_data(T) * data; 35 }; 37 36 38 void ?{}(counter_ptr(T) & this); 39 void ?{}(counter_ptr(T) & this, zero_t); 40 forall( | { void ^?{}(T &); }) 41 void ?{}(counter_ptr(T) & this, counter_ptr(T) that); 42 forall(ttype Args | { void ?{}(T&, Args); }) 43 void ?{}(counter_ptr(T) & this, Args args); 37 forall(T & | sized(T)) 38 void ?{}(counter_ptr(T) & this); 39 forall(T & | sized(T)) 40 void ?{}(counter_ptr(T) & this, zero_t); 41 forall(T & | sized(T)) 42 void ?{}(counter_ptr(T) & this, counter_ptr(T) that); 43 forall(T & | sized(T), Args... | { void ?{}(T&, Args); }) 44 void ?{}(counter_ptr(T) & this, Args args); 44 45 45 forall(| { void ^?{}(T &); })46 46 forall(T & | sized(T) | { void ^?{}(T &); }) 47 void ^?{}(counter_ptr(T) & this); 47 48 48 T & *?(counter_ptr(T) & this); 49 forall(T & | sized(T)) 50 T & *?(counter_ptr(T) & this); 49 51 50 forall(| { void ^?{}(T &); })51 52 forall(| { void ^?{}(T &); })53 52 forall(T & | sized(T) | { void ^?{}(T &); }) 53 void ?=?(counter_ptr(T) & this, counter_ptr(T) that); 54 forall(T & | sized(T) | { void ^?{}(T &); }) 55 void ?=?(counter_ptr(T) & this, zero_t); 54 56 55 int ?==?(counter_ptr(T) const & this, counter_ptr(T) const & that); 56 int ?!=?(counter_ptr(T) const & this, counter_ptr(T) const & that); 57 int ?==?(counter_ptr(T) const & this, zero_t); 58 int ?!=?(counter_ptr(T) const & this, zero_t); 59 } 57 forall(T & | sized(T)) 58 int ?==?(counter_ptr(T) const & this, counter_ptr(T) const & that); 59 forall(T & | sized(T)) 60 int ?!=?(counter_ptr(T) const & this, counter_ptr(T) const & that); 61 forall(T & | sized(T)) 62 int ?==?(counter_ptr(T) const & this, zero_t); 63 forall(T & | sized(T)) 64 int ?!=?(counter_ptr(T) const & this, zero_t); 60 65 61 66 // This is the only pointer that keeps this alive. 62 forall( dtype T) {63 64 65 67 forall(T &) 68 struct unique_ptr { 69 T * data; 70 }; 66 71 67 void ?{}(unique_ptr(T) & this); 68 void ?{}(unique_ptr(T) & this, zero_t); 69 void ?{}(unique_ptr(T) & this, unique_ptr(T) that) = void; 70 forall( | sized(T), ttype Args | { void ?{}(T &, Args); }) 71 void ?{}(unique_ptr(T) & this, Args args); 72 forall(T &) 73 void ?{}(unique_ptr(T) & this); 74 forall(T &) 75 void ?{}(unique_ptr(T) & this, zero_t); 76 forall(T &) 77 void ?{}(unique_ptr(T) & this, unique_ptr(T) that) = void; 78 forall(T & | sized(T), Args... | { void ?{}(T &, Args); }) 79 void ?{}(unique_ptr(T) & this, Args args); 72 80 73 forall(| { void ^?{}(T &); })74 81 forall(T & | { void ^?{}(T &); }) 82 void ^?{}(unique_ptr(T) & this); 75 83 76 T & *?(unique_ptr(T) & this); 84 forall(T & ) 85 T & *?(unique_ptr(T) & this); 77 86 78 void ?=?(unique_ptr(T) & this, unique_ptr(T) that) = void; 79 forall( | { void ^?{}(T &); }) 80 void ?=?(unique_ptr(T) & this, zero_t); 87 forall(T &) 88 void ?=?(unique_ptr(T) & this, unique_ptr(T) that) = void; 89 forall(T & | { void ^?{}(T &); }) 90 void ?=?(unique_ptr(T) & this, zero_t); 81 91 82 forall(| { void ^?{}(T &); })83 92 forall(T & | { void ^?{}(T &); }) 93 void move(unique_ptr(T) & this, unique_ptr(T) & that); 84 94 85 int ?==?(unique_ptr(T) const & this, unique_ptr(T) const & that); 86 int ?!=?(unique_ptr(T) const & this, unique_ptr(T) const & that); 87 int ?==?(unique_ptr(T) const & this, zero_t); 88 int ?!=?(unique_ptr(T) const & this, zero_t); 89 } 95 forall(T &) 96 int ?==?(unique_ptr(T) const & this, unique_ptr(T) const & that); 97 forall(T &) 98 int ?!=?(unique_ptr(T) const & this, unique_ptr(T) const & that); 99 forall(T &) 100 int ?==?(unique_ptr(T) const & this, zero_t); 101 forall(T &) 102 int ?!=?(unique_ptr(T) const & this, zero_t); -
libcfa/src/parseargs.cfa
r342af53 r8e4aa05 30 30 31 31 static void usage(char * cmd, cfa_option options[], size_t opt_count, const char * usage, FILE * out) __attribute__ ((noreturn)); 32 32 //----------------------------------------------------------------------------- 33 // checking 34 static void check_args(cfa_option options[], size_t opt_count) { 35 for(i; opt_count) { 36 for(j; opt_count) { 37 if(i == j) continue; 38 39 if( options[i].short_name != '\0' 40 && options[i].short_name == options[j].short_name) 41 abort("Parse Args error: two options have short name '%c' (%zu & %zu)", options[i].short_name, i, j); 42 43 if(0 == strcmp(options[i].long_name, options[j].long_name)) abort("Parse Args error: two options have long name '%s' (%zu & %zu)", options[i].long_name, i, j); 44 } 45 } 46 } 47 48 49 //----------------------------------------------------------------------------- 50 // Parsing args 33 51 void parse_args( cfa_option options[], size_t opt_count, const char * usage, char ** & left ) { 34 52 if( 0p != &cfa_args_argc ) { … … 41 59 } 42 60 43 //-----------------------------------------------------------------------------44 // getopt_long wrapping45 61 void parse_args( 46 62 int argc, … … 51 67 char ** & left 52 68 ) { 69 check_args(options, opt_count); 70 71 int maxv = 'h'; 72 char optstring[opt_count * 3] = { '\0' }; 73 { 74 int idx = 0; 75 for(i; opt_count) { 76 if (options[i].short_name) { 77 maxv = max(options[i].short_name, maxv); 78 optstring[idx] = options[i].short_name; 79 idx++; 80 if( ((intptr_t)options[i].parse) != ((intptr_t)parse_settrue) 81 && ((intptr_t)options[i].parse) != ((intptr_t)parse_setfalse) ) { 82 optstring[idx] = ':'; 83 idx++; 84 } 85 } 86 } 87 optstring[idx+0] = 'h'; 88 optstring[idx+1] = '\0'; 89 } 90 53 91 struct option optarr[opt_count + 2]; 54 92 { … … 56 94 for(i; opt_count) { 57 95 if(options[i].long_name) { 96 options[i].val = (options[i].short_name != '\0') ? ((int)options[i].short_name) : ++maxv; 58 97 optarr[idx].name = options[i].long_name; 59 98 optarr[idx].flag = 0p; 60 optarr[idx].val = options[i]. short_name;99 optarr[idx].val = options[i].val; 61 100 if( ((intptr_t)options[i].parse) == ((intptr_t)parse_settrue) 62 101 || ((intptr_t)options[i].parse) == ((intptr_t)parse_setfalse) ) { … … 70 109 optarr[idx+0].[name, has_arg, flag, val] = ["help", no_argument, 0, 'h']; 71 110 optarr[idx+1].[name, has_arg, flag, val] = [0, no_argument, 0, 0]; 72 }73 74 char optstring[opt_count * 3] = { '\0' };75 {76 int idx = 0;77 for(i; opt_count) {78 optstring[idx] = options[i].short_name;79 idx++;80 if( ((intptr_t)options[i].parse) != ((intptr_t)parse_settrue)81 && ((intptr_t)options[i].parse) != ((intptr_t)parse_setfalse) ) {82 optstring[idx] = ':';83 idx++;84 }85 }86 optstring[idx+0] = 'h';87 optstring[idx+1] = '\0';88 111 } 89 112 … … 103 126 default: 104 127 for(i; opt_count) { 105 if(opt == options[i]. short_name) {128 if(opt == options[i].val) { 106 129 const char * arg = optarg ? optarg : ""; 107 130 if( arg[0] == '=' ) { arg++; } … … 125 148 if(hwidth <= 0) hwidth = max; 126 149 127 fprintf(out, " -%c, --%-*s %.*s\n", sn, width, ln, hwidth, help); 150 char sname[4] = { ' ', ' ', ' ', '\0' }; 151 if(sn != '\0') { 152 sname[0] = '-'; 153 sname[1] = sn; 154 sname[2] = ','; 155 } 156 157 fprintf(out, " %s --%-*s %.*s\n", sname, width, ln, hwidth, help); 128 158 for() { 129 159 help += min(strlen(help), hwidth); -
libcfa/src/parseargs.hfa
r342af53 r8e4aa05 2 2 3 3 struct cfa_option { 4 int val; // reserved 4 5 char short_name; 5 6 const char * long_name; … … 13 14 static inline void ?{}( cfa_option & this ) {} 14 15 15 forall( dtype T| { bool parse(const char *, T & ); })16 forall(T & | { bool parse(const char *, T & ); }) 16 17 static inline void ?{}( cfa_option & this, char short_name, const char * long_name, const char * help, T & variable ) { 18 this.val = 0; 17 19 this.short_name = short_name; 18 20 this.long_name = long_name; … … 22 24 } 23 25 24 forall( dtype T)26 forall(T &) 25 27 static inline void ?{}( cfa_option & this, char short_name, const char * long_name, const char * help, T & variable, bool (*parse)(const char *, T & )) { 28 this.val = 0; 26 29 this.short_name = short_name; 27 30 this.long_name = long_name; -
libcfa/src/rational.cfa
r342af53 r8e4aa05 18 18 #include "stdlib.hfa" 19 19 20 forall( otypeRationalImpl | arithmetic( RationalImpl ) ) {20 forall( RationalImpl | arithmetic( RationalImpl ) ) { 21 21 // helper routines 22 22 … … 159 159 // I/O 160 160 161 forall( dtype istype| istream( istype ) | { istype & ?|?( istype &, RationalImpl & ); } )161 forall( istype & | istream( istype ) | { istype & ?|?( istype &, RationalImpl & ); } ) 162 162 istype & ?|?( istype & is, Rational(RationalImpl) & r ) { 163 163 is | r.numerator | r.denominator; … … 168 168 } // ?|? 169 169 170 forall( dtype ostype| ostream( ostype ) | { ostype & ?|?( ostype &, RationalImpl ); } ) {170 forall( ostype & | ostream( ostype ) | { ostype & ?|?( ostype &, RationalImpl ); } ) { 171 171 ostype & ?|?( ostype & os, Rational(RationalImpl) r ) { 172 172 return os | r.numerator | '/' | r.denominator; … … 179 179 } // distribution 180 180 181 forall( otypeRationalImpl | arithmetic( RationalImpl ) | { RationalImpl ?\?( RationalImpl, unsigned long ); } )181 forall( RationalImpl | arithmetic( RationalImpl ) | { RationalImpl ?\?( RationalImpl, unsigned long ); } ) 182 182 Rational(RationalImpl) ?\?( Rational(RationalImpl) x, long int y ) { 183 183 if ( y < 0 ) { … … 190 190 // conversion 191 191 192 forall( otypeRationalImpl | arithmetic( RationalImpl ) | { double convert( RationalImpl ); } )192 forall( RationalImpl | arithmetic( RationalImpl ) | { double convert( RationalImpl ); } ) 193 193 double widen( Rational(RationalImpl) r ) { 194 194 return convert( r.numerator ) / convert( r.denominator ); 195 195 } // widen 196 196 197 forall( otypeRationalImpl | arithmetic( RationalImpl ) | { double convert( RationalImpl ); RationalImpl convert( double ); } )197 forall( RationalImpl | arithmetic( RationalImpl ) | { double convert( RationalImpl ); RationalImpl convert( double ); } ) 198 198 Rational(RationalImpl) narrow( double f, RationalImpl md ) { 199 199 // http://www.ics.uci.edu/~eppstein/numth/frap.c -
libcfa/src/rational.hfa
r342af53 r8e4aa05 20 20 #include "iostream.hfa" 21 21 22 trait scalar( otypeT ) {22 trait scalar( T ) { 23 23 }; 24 24 25 trait arithmetic( otypeT | scalar( T ) ) {25 trait arithmetic( T | scalar( T ) ) { 26 26 int !?( T ); 27 27 int ?==?( T, T ); … … 46 46 // implementation 47 47 48 forall( otypeRationalImpl | arithmetic( RationalImpl ) ) {48 forall( RationalImpl | arithmetic( RationalImpl ) ) { 49 49 struct Rational { 50 50 RationalImpl numerator, denominator; // invariant: denominator > 0 … … 89 89 90 90 // I/O 91 forall( dtype istype| istream( istype ) | { istype & ?|?( istype &, RationalImpl & ); } )91 forall( istype & | istream( istype ) | { istype & ?|?( istype &, RationalImpl & ); } ) 92 92 istype & ?|?( istype &, Rational(RationalImpl) & ); 93 93 94 forall( dtype ostype| ostream( ostype ) | { ostype & ?|?( ostype &, RationalImpl ); } ) {94 forall( ostype & | ostream( ostype ) | { ostype & ?|?( ostype &, RationalImpl ); } ) { 95 95 ostype & ?|?( ostype &, Rational(RationalImpl) ); 96 96 void ?|?( ostype &, Rational(RationalImpl) ); … … 98 98 } // distribution 99 99 100 forall( otypeRationalImpl | arithmetic( RationalImpl ) |{RationalImpl ?\?( RationalImpl, unsigned long );} )100 forall( RationalImpl | arithmetic( RationalImpl ) |{RationalImpl ?\?( RationalImpl, unsigned long );} ) 101 101 Rational(RationalImpl) ?\?( Rational(RationalImpl) x, long int y ); 102 102 103 103 // conversion 104 forall( otypeRationalImpl | arithmetic( RationalImpl ) | { double convert( RationalImpl ); } )104 forall( RationalImpl | arithmetic( RationalImpl ) | { double convert( RationalImpl ); } ) 105 105 double widen( Rational(RationalImpl) r ); 106 forall( otypeRationalImpl | arithmetic( RationalImpl ) | { double convert( RationalImpl ); RationalImpl convert( double );} )106 forall( RationalImpl | arithmetic( RationalImpl ) | { double convert( RationalImpl ); RationalImpl convert( double );} ) 107 107 Rational(RationalImpl) narrow( double f, RationalImpl md ); 108 108 -
libcfa/src/stdlib.cfa
r342af53 r8e4aa05 28 28 // Cforall allocation/deallocation and constructor/destructor, array types 29 29 30 forall( dtype T | sized(T), ttype TT| { void ?{}( T &, TT ); } )30 forall( T & | sized(T), TT... | { void ?{}( T &, TT ); } ) 31 31 T * anew( size_t dim, TT p ) { 32 32 T * arr = alloc( dim ); … … 37 37 } // anew 38 38 39 forall( dtype T| sized(T) | { void ^?{}( T & ); } )39 forall( T & | sized(T) | { void ^?{}( T & ); } ) 40 40 void adelete( T arr[] ) { 41 41 if ( arr ) { // ignore null … … 48 48 } // adelete 49 49 50 forall( dtype T | sized(T) | { void ^?{}( T & ); }, ttype TT| { void adelete( TT ); } )50 forall( T & | sized(T) | { void ^?{}( T & ); }, TT... | { void adelete( TT ); } ) 51 51 void adelete( T arr[], TT rest ) { 52 52 if ( arr ) { // ignore null … … 97 97 //--------------------------------------- 98 98 99 forall( otypeE | { int ?<?( E, E ); } ) {99 forall( E | { int ?<?( E, E ); } ) { 100 100 E * bsearch( E key, const E * vals, size_t dim ) { 101 101 int cmp( const void * t1, const void * t2 ) { … … 156 156 157 157 158 forall( otype K, otypeE | { int ?<?( K, K ); K getKey( const E & ); } ) {158 forall( K, E | { int ?<?( K, K ); K getKey( const E & ); } ) { 159 159 E * bsearch( K key, const E * vals, size_t dim ) { 160 160 int cmp( const void * t1, const void * t2 ) { -
libcfa/src/stdlib.hfa
r342af53 r8e4aa05 10 10 // Created On : Thu Jan 28 17:12:35 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Dec 12 13:52:34 202013 // Update Count : 5 3612 // Last Modified On : Thu Jan 21 22:02:13 2021 13 // Update Count : 574 14 14 // 15 15 … … 48 48 else return (T *)alignment( _Alignof(T), dim, sizeof(T) ) 49 49 50 static inline forall( dtype T| sized(T) ) {50 static inline forall( T & | sized(T) ) { 51 51 // CFA safe equivalents, i.e., implicit size specification 52 52 … … 108 108 109 109 1. Replace the current forall-block that contains defintions of S_fill and S_realloc with following: 110 forall( dtype T| sized(T) ) {110 forall( T & | sized(T) ) { 111 111 union U_fill { char c; T * a; T t; }; 112 112 struct S_fill { char tag; U_fill(T) fill; }; … … 151 151 typedef struct S_resize { inline void *; } T_resize; 152 152 153 forall( dtype T) {153 forall( T & ) { 154 154 struct S_fill { char tag; char c; size_t size; T * at; char t[50]; }; 155 155 struct S_realloc { inline T *; }; … … 159 159 static inline T_resize ?`resize ( void * a ) { return (T_resize){a}; } 160 160 161 static inline forall( dtype T| sized(T) ) {161 static inline forall( T & | sized(T) ) { 162 162 S_fill(T) ?`fill ( T t ) { 163 163 S_fill(T) ret = { 't' }; 164 164 size_t size = sizeof(T); 165 if(size > sizeof(ret.t)) { printf("ERROR: const object of size greater than 50 bytes given for dynamic memory fill\n"); exit(1); } 165 if ( size > sizeof(ret.t) ) { 166 abort( "ERROR: const object of size greater than 50 bytes given for dynamic memory fill\n" ); 167 } // if 166 168 memcpy( &ret.t, &t, size ); 167 169 return ret; … … 173 175 S_realloc(T) ?`realloc ( T * a ) { return (S_realloc(T)){a}; } 174 176 175 T * $alloc_internal( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill ) {177 T * $alloc_internal( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill ) { 176 178 T * ptr = NULL; 177 179 size_t size = sizeof(T); … … 181 183 ptr = (T*) (void *) resize( (void *)Resize, Align, Dim * size ); 182 184 } else if ( Realloc ) { 183 if ( Fill.tag != '0') copy_end = min(malloc_size( Realloc ), Dim * size);184 ptr = (T *) (void *) realloc( (void *)Realloc, Align, Dim * size );185 if ( Fill.tag != '0' ) copy_end = min(malloc_size( Realloc ), Dim * size ); 186 ptr = (T *) (void *) realloc( (void *)Realloc, Align, Dim * size ); 185 187 } else { 186 ptr = (T *) (void *) memalign( Align, Dim * size );187 } 188 189 if (Fill.tag == 'c') {188 ptr = (T *) (void *) memalign( Align, Dim * size ); 189 } 190 191 if ( Fill.tag == 'c' ) { 190 192 memset( (char *)ptr + copy_end, (int)Fill.c, Dim * size - copy_end ); 191 } else if (Fill.tag == 't') {193 } else if ( Fill.tag == 't' ) { 192 194 for ( int i = copy_end; i < Dim * size; i += size ) { 195 #pragma GCC diagnostic push 196 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 197 assert( size <= sizeof(Fill.t) ); 193 198 memcpy( (char *)ptr + i, &Fill.t, size ); 199 #pragma GCC diagnostic pop 194 200 } 195 } else if (Fill.tag == 'a') {201 } else if ( Fill.tag == 'a' ) { 196 202 memcpy( (char *)ptr + copy_end, Fill.at, min(Dim * size - copy_end, Fill.size) ); 197 } else if(Fill.tag == 'T') { 198 for ( int i = copy_end; i < Dim * size; i += size ) { 199 memcpy( (char *)ptr + i, Fill.at, size ); 200 } 203 } else if ( Fill.tag == 'T' ) { 204 memcpy( (char *)ptr + copy_end, Fill.at, Dim * size ); 201 205 } 202 206 … … 204 208 } // $alloc_internal 205 209 206 forall( ttype TT| { T * $alloc_internal( void *, T *, size_t, size_t, S_fill(T), TT ); } ) {210 forall( TT... | { T * $alloc_internal( void *, T *, size_t, size_t, S_fill(T), TT ); } ) { 207 211 208 212 T * $alloc_internal( void * , T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill, T_resize Resize, TT rest) { … … 233 237 } // distribution T 234 238 235 static inline forall( dtype T| sized(T) ) {239 static inline forall( T & | sized(T) ) { 236 240 // CFA safe initialization/copy, i.e., implicit size specification, non-array types 237 241 T * memset( T * dest, char fill ) { … … 254 258 255 259 // CFA deallocation for multiple objects 256 static inline forall( dtype T) // FIX ME, problems with 0p in list260 static inline forall( T & ) // FIX ME, problems with 0p in list 257 261 void free( T * ptr ) { 258 262 free( (void *)ptr ); // C free 259 263 } // free 260 static inline forall( dtype T, ttype TT| { void free( TT ); } )264 static inline forall( T &, TT... | { void free( TT ); } ) 261 265 void free( T * ptr, TT rest ) { 262 266 free( ptr ); … … 265 269 266 270 // CFA allocation/deallocation and constructor/destructor, non-array types 267 static inline forall( dtype T | sized(T), ttype TT| { void ?{}( T &, TT ); } )271 static inline forall( T & | sized(T), TT... | { void ?{}( T &, TT ); } ) 268 272 T * new( TT p ) { 269 return &(*(T *)malloc()){ p }; 273 return &(*(T *)malloc()){ p }; // run constructor 270 274 } // new 271 275 272 static inline forall( dtype T| { void ^?{}( T & ); } )276 static inline forall( T & | { void ^?{}( T & ); } ) 273 277 void delete( T * ptr ) { 274 278 // special case for 0-sized object => always call destructor … … 278 282 free( ptr ); // always call free 279 283 } // delete 280 static inline forall( dtype T, ttype TT| { void ^?{}( T & ); void delete( TT ); } )284 static inline forall( T &, TT... | { void ^?{}( T & ); void delete( TT ); } ) 281 285 void delete( T * ptr, TT rest ) { 282 286 delete( ptr ); … … 285 289 286 290 // CFA allocation/deallocation and constructor/destructor, array types 287 forall( dtype T | sized(T), ttype TT| { void ?{}( T &, TT ); } ) T * anew( size_t dim, TT p );288 forall( dtype T| sized(T) | { void ^?{}( T & ); } ) void adelete( T arr[] );289 forall( dtype T | sized(T) | { void ^?{}( T & ); }, ttype TT| { void adelete( TT ); } ) void adelete( T arr[], TT rest );291 forall( T & | sized(T), TT... | { void ?{}( T &, TT ); } ) T * anew( size_t dim, TT p ); 292 forall( T & | sized(T) | { void ^?{}( T & ); } ) void adelete( T arr[] ); 293 forall( T & | sized(T) | { void ^?{}( T & ); }, TT... | { void adelete( TT ); } ) void adelete( T arr[], TT rest ); 290 294 291 295 //--------------------------------------- … … 327 331 //--------------------------------------- 328 332 329 forall( otypeE | { int ?<?( E, E ); } ) {333 forall( E | { int ?<?( E, E ); } ) { 330 334 E * bsearch( E key, const E * vals, size_t dim ); 331 335 size_t bsearch( E key, const E * vals, size_t dim ); … … 336 340 } // distribution 337 341 338 forall( otype K, otypeE | { int ?<?( K, K ); K getKey( const E & ); } ) {342 forall( K, E | { int ?<?( K, K ); K getKey( const E & ); } ) { 339 343 E * bsearch( K key, const E * vals, size_t dim ); 340 344 size_t bsearch( K key, const E * vals, size_t dim ); … … 345 349 } // distribution 346 350 347 forall( otypeE | { int ?<?( E, E ); } ) {351 forall( E | { int ?<?( E, E ); } ) { 348 352 void qsort( E * vals, size_t dim ); 349 353 } // distribution -
libcfa/src/time.cfa
r342af53 r8e4aa05 31 31 32 32 33 forall( dtype ostype| ostream( ostype ) ) {33 forall( ostype & | ostream( ostype ) ) { 34 34 ostype & ?|?( ostype & os, Duration dur ) with( dur ) { 35 35 (ostype &)(os | tn / TIMEGRAN); // print seconds … … 136 136 } // strftime 137 137 138 forall( dtype ostype| ostream( ostype ) ) {138 forall( ostype & | ostream( ostype ) ) { 139 139 ostype & ?|?( ostype & os, Time time ) with( time ) { 140 140 char buf[32]; // at least 26 -
libcfa/src/vec/vec.hfa
r342af53 r8e4aa05 18 18 #include <math.hfa> 19 19 20 trait fromint( otypeT) {20 trait fromint(T) { 21 21 void ?{}(T&, int); 22 22 }; 23 trait zeroinit( otypeT) {23 trait zeroinit(T) { 24 24 void ?{}(T&, zero_t); 25 25 }; 26 trait zero_assign( otypeT) {26 trait zero_assign(T) { 27 27 T ?=?(T&, zero_t); 28 28 }; 29 trait subtract( otypeT) {29 trait subtract(T) { 30 30 T ?-?(T, T); 31 31 }; 32 trait negate( otypeT) {32 trait negate(T) { 33 33 T -?(T); 34 34 }; 35 trait add( otypeT) {35 trait add(T) { 36 36 T ?+?(T, T); 37 37 }; 38 trait multiply( otypeT) {38 trait multiply(T) { 39 39 T ?*?(T, T); 40 40 }; 41 trait divide( otypeT) {41 trait divide(T) { 42 42 T ?/?(T, T); 43 43 }; 44 trait lessthan( otypeT) {44 trait lessthan(T) { 45 45 int ?<?(T, T); 46 46 }; 47 trait equality( otypeT) {47 trait equality(T) { 48 48 int ?==?(T, T); 49 49 }; 50 trait sqrt( otypeT) {50 trait sqrt(T) { 51 51 T sqrt(T); 52 52 }; … … 68 68 } 69 69 70 trait dottable( otype V, otypeT) {70 trait dottable(V, T) { 71 71 T dot(V, V); 72 72 }; … … 74 74 static inline { 75 75 76 forall( otype T | sqrt(T), otypeV | dottable(V, T))76 forall(T | sqrt(T), V | dottable(V, T)) 77 77 T length(V v) { 78 78 return sqrt(dot(v, v)); 79 79 } 80 80 81 forall( otype T, otypeV | dottable(V, T))81 forall(T, V | dottable(V, T)) 82 82 T length_squared(V v) { 83 83 return dot(v, v); 84 84 } 85 85 86 forall( otype T, otypeV | { T length(V); } | subtract(V))86 forall(T, V | { T length(V); } | subtract(V)) 87 87 T distance(V v1, V v2) { 88 88 return length(v1 - v2); 89 89 } 90 90 91 forall( otype T, otypeV | { T length(V); V ?/?(V, T); })91 forall(T, V | { T length(V); V ?/?(V, T); }) 92 92 V normalize(V v) { 93 93 return v / length(v); … … 95 95 96 96 // Project vector u onto vector v 97 forall( otype T, otypeV | dottable(V, T) | { V normalize(V); V ?*?(V, T); })97 forall(T, V | dottable(V, T) | { V normalize(V); V ?*?(V, T); }) 98 98 V project(V u, V v) { 99 99 V v_norm = normalize(v); … … 102 102 103 103 // Reflect incident vector v with respect to surface with normal n 104 forall( otype T | fromint(T), otypeV | { V project(V, V); V ?*?(T, V); V ?-?(V,V); })104 forall(T | fromint(T), V | { V project(V, V); V ?*?(T, V); V ?-?(V,V); }) 105 105 V reflect(V v, V n) { 106 106 return v - (T){2} * project(v, n); … … 111 111 // entering material (i.e., from air to water, eta = 1/1.33) 112 112 // v and n must already be normalized 113 forall( otypeT | fromint(T) | subtract(T) | multiply(T) | add(T) | lessthan(T) | sqrt(T),114 otypeV | dottable(V, T) | { V ?*?(T, V); V ?-?(V,V); void ?{}(V&, zero_t); })113 forall(T | fromint(T) | subtract(T) | multiply(T) | add(T) | lessthan(T) | sqrt(T), 114 V | dottable(V, T) | { V ?*?(T, V); V ?-?(V,V); void ?{}(V&, zero_t); }) 115 115 V refract(V v, V n, T eta) { 116 116 T dotValue = dot(n, v); … … 128 128 // i is the incident vector 129 129 // ng is the geometric normal of the surface 130 forall( otype T | lessthan(T) | zeroinit(T), otypeV | dottable(V, T) | negate(V))130 forall(T | lessthan(T) | zeroinit(T), V | dottable(V, T) | negate(V)) 131 131 V faceforward(V n, V i, V ng) { 132 132 return dot(ng, i) < (T){0} ? n : -n; -
libcfa/src/vec/vec2.hfa
r342af53 r8e4aa05 19 19 #include "vec.hfa" 20 20 21 forall ( otypeT) {21 forall (T) { 22 22 struct vec2 { 23 23 T x, y; … … 25 25 } 26 26 27 forall ( otypeT) {27 forall (T) { 28 28 static inline { 29 29 … … 279 279 } 280 280 281 forall( dtype ostype, otypeT | writeable(T, ostype)) {281 forall(ostype &, T | writeable(T, ostype)) { 282 282 ostype & ?|?(ostype & os, vec2(T) v) with (v) { 283 283 return os | '<' | x | ',' | y | '>'; -
libcfa/src/vec/vec3.hfa
r342af53 r8e4aa05 19 19 #include "vec.hfa" 20 20 21 forall ( otypeT) {21 forall (T) { 22 22 struct vec3 { 23 23 T x, y, z; … … 25 25 } 26 26 27 forall ( otypeT) {27 forall (T) { 28 28 static inline { 29 29 … … 288 288 } 289 289 290 forall( dtype ostype, otypeT | writeable(T, ostype)) {290 forall(ostype &, T | writeable(T, ostype)) { 291 291 ostype & ?|?(ostype & os, vec3(T) v) with (v) { 292 292 return os | '<' | x | ',' | y | ',' | z | '>'; -
libcfa/src/vec/vec4.hfa
r342af53 r8e4aa05 19 19 #include "vec.hfa" 20 20 21 forall ( otypeT) {21 forall (T) { 22 22 struct vec4 { 23 23 T x, y, z, w; … … 25 25 } 26 26 27 forall ( otypeT) {27 forall (T) { 28 28 static inline { 29 29 … … 283 283 } 284 284 285 forall( dtype ostype, otypeT | writeable(T, ostype)) {285 forall(ostype &, T | writeable(T, ostype)) { 286 286 ostype & ?|?(ostype & os, vec4(T) v) with (v) { 287 287 return os | '<' | x | ',' | y | ',' | z | ',' | w | '>'; -
src/Parser/lex.ll
r342af53 r8e4aa05 10 10 * Created On : Sat Sep 22 08:58:10 2001 11 11 * Last Modified By : Peter A. Buhr 12 * Last Modified On : Tue Oct 6 18:15:41 202013 * Update Count : 7 4312 * Last Modified On : Wed Feb 17 08:38:13 2021 13 * Update Count : 752 14 14 */ 15 15 … … 221 221 break { KEYWORD_RETURN(BREAK); } 222 222 case { KEYWORD_RETURN(CASE); } 223 catch { KEYWORD_RETURN(CATCH); } // CFA224 catchResume { KEYWORD_RETURN(CATCHRESUME); } // CFA223 catch { QKEYWORD_RETURN(CATCH); } // CFA 224 catchResume { QKEYWORD_RETURN(CATCHRESUME); } // CFA 225 225 char { KEYWORD_RETURN(CHAR); } 226 226 choose { KEYWORD_RETURN(CHOOSE); } // CFA … … 247 247 fallthrough { KEYWORD_RETURN(FALLTHROUGH); } // CFA 248 248 fallthru { KEYWORD_RETURN(FALLTHRU); } // CFA 249 finally { KEYWORD_RETURN(FINALLY); } // CFA 249 finally { QKEYWORD_RETURN(FINALLY); } // CFA 250 fixup { QKEYWORD_RETURN(FIXUP); } // CFA 250 251 float { KEYWORD_RETURN(FLOAT); } 251 252 __float80 { KEYWORD_RETURN(uuFLOAT80); } // GCC … … 287 288 or { QKEYWORD_RETURN(WOR); } // CFA 288 289 otype { KEYWORD_RETURN(OTYPE); } // CFA 290 recover { QKEYWORD_RETURN(RECOVER); } // CFA 289 291 register { KEYWORD_RETURN(REGISTER); } 292 report { KEYWORD_RETURN(THROWRESUME); } // CFA 290 293 restrict { KEYWORD_RETURN(RESTRICT); } // C99 291 294 __restrict { KEYWORD_RETURN(RESTRICT); } // GCC … … 324 327 __volatile { KEYWORD_RETURN(VOLATILE); } // GCC 325 328 __volatile__ { KEYWORD_RETURN(VOLATILE); } // GCC 326 waitfor { KEYWORD_RETURN(WAITFOR); } 327 when { KEYWORD_RETURN(WHEN); } 329 waitfor { KEYWORD_RETURN(WAITFOR); } // CFA 330 when { KEYWORD_RETURN(WHEN); } // CFA 328 331 while { KEYWORD_RETURN(WHILE); } 329 332 with { KEYWORD_RETURN(WITH); } // CFA -
src/Parser/parser.yy
r342af53 r8e4aa05 10 10 // Created On : Sat Sep 1 20:22:55 2001 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Jan 11 21:32:10202113 // Update Count : 4 63312 // Last Modified On : Wed Feb 17 09:03:07 2021 13 // Update Count : 4722 14 14 // 15 15 … … 41 41 42 42 %{ 43 #define YYDEBUG_LEXER_TEXT (yylval)// lexer loads this up each time43 #define YYDEBUG_LEXER_TEXT( yylval ) // lexer loads this up each time 44 44 #define YYDEBUG 1 // get the pretty debugging code to compile 45 45 #define YYERROR_VERBOSE // more information in syntax errors … … 63 63 extern TypedefTable typedefTable; 64 64 65 stack< LinkageSpec::Spec> linkageStack;65 stack<LinkageSpec::Spec> linkageStack; 66 66 67 67 bool appendStr( string & to, string & from ) { … … 187 187 ConstantExpr * constant = dynamic_cast<ConstantExpr *>(type->expr.get()); 188 188 if ( constant && (constant->get_constant()->get_value() == "0" || constant->get_constant()->get_value() == "1") ) { 189 type = new ExpressionNode( new CastExpr( maybeMoveBuild< Expression>(type), new BasicType( Type::Qualifiers(), BasicType::SignedInt ) ) );189 type = new ExpressionNode( new CastExpr( maybeMoveBuild<Expression>(type), new BasicType( Type::Qualifiers(), BasicType::SignedInt ) ) ); 190 190 } // if 191 191 return new ForCtrl( … … 282 282 %token ATTRIBUTE EXTENSION // GCC 283 283 %token IF ELSE SWITCH CASE DEFAULT DO WHILE FOR BREAK CONTINUE GOTO RETURN 284 %token CHOOSE DISABLE ENABLE FALLTHRU FALLTHROUGH TRY CATCH CATCHRESUME FINALLYTHROW THROWRESUME AT WITH WHEN WAITFOR // CFA284 %token CHOOSE DISABLE ENABLE FALLTHRU FALLTHROUGH TRY THROW THROWRESUME AT WITH WHEN WAITFOR // CFA 285 285 %token ASM // C99, extension ISO/IEC 9899:1999 Section J.5.10(1) 286 286 %token ALIGNAS ALIGNOF GENERIC STATICASSERT // C11 287 287 288 288 // names and constants: lexer differentiates between identifier and typedef names 289 %token<tok> IDENTIFIER QUOTED_IDENTIFIER TYPEDEFnameTYPEGENname290 %token<tok> TIMEOUT WOR291 %token<tok> INTEGERconstant CHARACTERconstantSTRINGliteral289 %token<tok> IDENTIFIER QUOTED_IDENTIFIER TYPEDEFname TYPEGENname 290 %token<tok> TIMEOUT WOR CATCH RECOVER CATCHRESUME FIXUP FINALLY // CFA 291 %token<tok> INTEGERconstant CHARACTERconstant STRINGliteral 292 292 %token<tok> DIRECTIVE 293 293 // Floating point constant is broken into three kinds of tokens because of the ambiguity with tuple indexing and … … 440 440 441 441 %type<decl> type_qualifier type_qualifier_name forall type_qualifier_list_opt type_qualifier_list 442 %type<decl> type_specifier type_specifier_nobody 442 %type<decl> type_specifier type_specifier_nobody enum_specifier_nobody 443 443 444 444 %type<decl> variable_declarator variable_ptr variable_array variable_function 445 445 %type<decl> variable_abstract_declarator variable_abstract_ptr variable_abstract_array variable_abstract_function 446 446 447 %type<decl> attribute_list_opt attribute_list attribute_ name_list attributeattribute_name447 %type<decl> attribute_list_opt attribute_list attribute_opt attribute attribute_name_list attribute_name 448 448 449 449 // initializers … … 462 462 // Order of these lines matters (low-to-high precedence). THEN is left associative over WOR/TIMEOUT/ELSE, WOR is left 463 463 // associative over TIMEOUT/ELSE, and TIMEOUT is left associative over ELSE. 464 %precedence THEN // rule precedence for IF/WAITFOR statement 465 %precedence WOR // token precedence for start of WOR in WAITFOR statement 466 %precedence TIMEOUT // token precedence for start of TIMEOUT in WAITFOR statement 467 %precedence ELSE // token precedence for start of else clause in IF/WAITFOR statement 464 %precedence THEN // rule precedence for IF/WAITFOR statement 465 %precedence WOR // token precedence for start of WOR in WAITFOR statement 466 %precedence TIMEOUT // token precedence for start of TIMEOUT in WAITFOR statement 467 %precedence CATCH // token precedence for start of TIMEOUT in WAITFOR statement 468 %precedence RECOVER // token precedence for start of TIMEOUT in WAITFOR statement 469 %precedence CATCHRESUME // token precedence for start of TIMEOUT in WAITFOR statement 470 %precedence FIXUP // token precedence for start of TIMEOUT in WAITFOR statement 471 %precedence FINALLY // token precedence for start of TIMEOUT in WAITFOR statement 472 %precedence ELSE // token precedence for start of else clause in IF/WAITFOR statement 473 468 474 469 475 // Handle shift/reduce conflict for generic type by shifting the '(' token. For example, this string is ambiguous: … … 544 550 TIMEOUT 545 551 | WOR 552 | CATCH 553 | RECOVER 554 | CATCHRESUME 555 | FIXUP 556 | FINALLY 546 557 ; 547 558 … … 578 589 { $$ = $2; } 579 590 | '(' compound_statement ')' // GCC, lambda expression 580 { $$ = new ExpressionNode( new StmtExpr( dynamic_cast< CompoundStmt * >(maybeMoveBuild< Statement>($2) ) ) ); }591 { $$ = new ExpressionNode( new StmtExpr( dynamic_cast<CompoundStmt *>(maybeMoveBuild<Statement>($2) ) ) ); } 581 592 | type_name '.' identifier // CFA, nested type 582 593 { SemanticError( yylloc, "Qualified name is currently unimplemented." ); $$ = nullptr; } … … 610 621 { 611 622 // create a GenericExpr wrapper with one association pair 612 $$ = new GenericExpr( nullptr, { { maybeMoveBuildType($1), maybeMoveBuild<Expression>( $3) } } );623 $$ = new GenericExpr( nullptr, { { maybeMoveBuildType($1), maybeMoveBuild<Expression>( $3 ) } } ); 613 624 } 614 625 | DEFAULT ':' assignment_expression 615 { $$ = new GenericExpr( nullptr, { { maybeMoveBuild<Expression>( $3) } } ); }626 { $$ = new GenericExpr( nullptr, { { maybeMoveBuild<Expression>( $3 ) } } ); } 616 627 ; 617 628 618 629 postfix_expression: 619 630 primary_expression 631 | postfix_expression '[' assignment_expression ',' comma_expression ']' 632 // { $$ = new ExpressionNode( build_binary_val( OperKinds::Index, $1, new ExpressionNode( build_binary_val( OperKinds::Index, $3, $5 ) ) ) ); } 633 { SemanticError( yylloc, "New array subscript is currently unimplemented." ); $$ = nullptr; } 620 634 | postfix_expression '[' assignment_expression ']' 621 635 // CFA, comma_expression disallowed in this context because it results in a common user error: subscripting a … … 743 757 switch ( $1 ) { 744 758 case OperKinds::AddressOf: 745 $$ = new ExpressionNode( new AddressExpr( maybeMoveBuild< Expression>( $2 ) ) );759 $$ = new ExpressionNode( new AddressExpr( maybeMoveBuild<Expression>( $2 ) ) ); 746 760 break; 747 761 case OperKinds::PointTo: … … 749 763 break; 750 764 case OperKinds::And: 751 $$ = new ExpressionNode( new AddressExpr( new AddressExpr( maybeMoveBuild< Expression>( $2 ) ) ) );765 $$ = new ExpressionNode( new AddressExpr( new AddressExpr( maybeMoveBuild<Expression>( $2 ) ) ) ); 752 766 break; 753 767 default: … … 762 776 { $$ = new ExpressionNode( build_unary_ptr( OperKinds::Decr, $2 ) ); } 763 777 | SIZEOF unary_expression 764 { $$ = new ExpressionNode( new SizeofExpr( maybeMoveBuild< Expression>( $2 ) ) ); }778 { $$ = new ExpressionNode( new SizeofExpr( maybeMoveBuild<Expression>( $2 ) ) ); } 765 779 | SIZEOF '(' type_no_function ')' 766 780 { $$ = new ExpressionNode( new SizeofExpr( maybeMoveBuildType( $3 ) ) ); } 767 781 | ALIGNOF unary_expression // GCC, variable alignment 768 { $$ = new ExpressionNode( new AlignofExpr( maybeMoveBuild< Expression>( $2 ) ) ); }782 { $$ = new ExpressionNode( new AlignofExpr( maybeMoveBuild<Expression>( $2 ) ) ); } 769 783 | ALIGNOF '(' type_no_function ')' // GCC, type alignment 770 784 { $$ = new ExpressionNode( new AlignofExpr( maybeMoveBuildType( $3 ) ) ); } … … 794 808 { $$ = new ExpressionNode( build_keyword_cast( $2, $5 ) ); } 795 809 | '(' VIRTUAL ')' cast_expression // CFA 796 { $$ = new ExpressionNode( new VirtualCastExpr( maybeMoveBuild< Expression>( $4 ), maybeMoveBuildType( nullptr ) ) ); }810 { $$ = new ExpressionNode( new VirtualCastExpr( maybeMoveBuild<Expression>( $4 ), maybeMoveBuildType( nullptr ) ) ); } 797 811 | '(' VIRTUAL type_no_function ')' cast_expression // CFA 798 { $$ = new ExpressionNode( new VirtualCastExpr( maybeMoveBuild< Expression>( $5 ), maybeMoveBuildType( $3 ) ) ); }812 { $$ = new ExpressionNode( new VirtualCastExpr( maybeMoveBuild<Expression>( $5 ), maybeMoveBuildType( $3 ) ) ); } 799 813 | '(' RETURN type_no_function ')' cast_expression // CFA 800 814 { SemanticError( yylloc, "Return cast is currently unimplemented." ); $$ = nullptr; } … … 977 991 assignment_expression 978 992 | comma_expression ',' assignment_expression 979 { $$ = new ExpressionNode( new CommaExpr( maybeMoveBuild< Expression >( $1 ), maybeMoveBuild< Expression>( $3 ) ) ); }993 { $$ = new ExpressionNode( new CommaExpr( maybeMoveBuild<Expression>( $1 ), maybeMoveBuild<Expression>( $3 ) ) ); } 980 994 ; 981 995 … … 1102 1116 constant_expression { $$ = $1; } 1103 1117 | constant_expression ELLIPSIS constant_expression // GCC, subrange 1104 { $$ = new ExpressionNode( new RangeExpr( maybeMoveBuild< Expression >( $1 ), maybeMoveBuild< Expression>( $3 ) ) ); }1118 { $$ = new ExpressionNode( new RangeExpr( maybeMoveBuild<Expression>( $1 ), maybeMoveBuild<Expression>( $3 ) ) ); } 1105 1119 | subrange // CFA, subrange 1106 1120 ; … … 1247 1261 { $$ = new StatementNode( build_computedgoto( $3 ) ); } 1248 1262 // A semantic check is required to ensure fallthru appears only in the body of a choose statement. 1249 1263 | fall_through_name ';' // CFA 1250 1264 { $$ = new StatementNode( build_branch( BranchStmt::FallThrough ) ); } 1251 1265 | fall_through_name identifier_or_type_name ';' // CFA 1252 1266 { $$ = new StatementNode( build_branch( $2, BranchStmt::FallThrough ) ); } 1253 1267 | fall_through_name DEFAULT ';' // CFA … … 1363 1377 1364 1378 exception_statement: 1365 TRY compound_statement handler_clause 1379 TRY compound_statement handler_clause %prec THEN 1366 1380 { $$ = new StatementNode( build_try( $2, $3, 0 ) ); } 1367 1381 | TRY compound_statement finally_clause … … 1386 1400 handler_key: 1387 1401 CATCH { $$ = CatchStmt::Terminate; } 1402 | RECOVER { $$ = CatchStmt::Terminate; } 1388 1403 | CATCHRESUME { $$ = CatchStmt::Resume; } 1404 | FIXUP { $$ = CatchStmt::Resume; } 1389 1405 ; 1390 1406 … … 1448 1464 asm_operand: // GCC 1449 1465 string_literal '(' constant_expression ')' 1450 { $$ = new ExpressionNode( new AsmExpr( nullptr, $1, maybeMoveBuild< Expression>( $3 ) ) ); }1466 { $$ = new ExpressionNode( new AsmExpr( nullptr, $1, maybeMoveBuild<Expression>( $3 ) ) ); } 1451 1467 | '[' IDENTIFIER ']' string_literal '(' constant_expression ')' 1452 { $$ = new ExpressionNode( new AsmExpr( $2, $4, maybeMoveBuild< Expression>( $6 ) ) ); }1468 { $$ = new ExpressionNode( new AsmExpr( $2, $4, maybeMoveBuild<Expression>( $6 ) ) ); } 1453 1469 ; 1454 1470 … … 1736 1752 | sue_type_specifier_nobody 1737 1753 | type_type_specifier 1754 ; 1755 1756 enum_specifier_nobody: // type specifier - {...} 1757 // Preclude SUE declarations in restricted scopes (see type_specifier_nobody) 1758 basic_type_specifier 1759 | sue_type_specifier_nobody 1738 1760 ; 1739 1761 … … 2004 2026 ; 2005 2027 2006 fred:2007 // empty2008 { yyy = false; }2009 ;2010 2011 2028 aggregate_type: // struct, union 2012 2029 aggregate_key attribute_list_opt … … 2014 2031 '{' field_declaration_list_opt '}' type_parameters_opt 2015 2032 { $$ = DeclarationNode::newAggregate( $1, nullptr, $7, $5, true )->addQualifiers( $2 ); } 2016 | aggregate_key attribute_list_opt identifier fred2033 | aggregate_key attribute_list_opt identifier 2017 2034 { 2018 2035 typedefTable.makeTypedef( *$3, forall || typedefTable.getEnclForall() ? TYPEGENname : TYPEDEFname ); // create typedef … … 2020 2037 } 2021 2038 '{' field_declaration_list_opt '}' type_parameters_opt 2022 { $$ = DeclarationNode::newAggregate( $1, $3, $ 9, $7, true )->addQualifiers( $2 ); }2023 | aggregate_key attribute_list_opt type_name fred2039 { $$ = DeclarationNode::newAggregate( $1, $3, $8, $6, true )->addQualifiers( $2 ); } 2040 | aggregate_key attribute_list_opt type_name 2024 2041 { 2025 2042 // for type_name can be a qualified type name S.T, in which case only the last name in the chain needs a typedef (other names in the chain should already have one) … … 2028 2045 } 2029 2046 '{' field_declaration_list_opt '}' type_parameters_opt 2030 { $$ = DeclarationNode::newAggregate( $1, $3->type->symbolic.name, $ 9, $7, true )->addQualifiers( $2 ); }2047 { $$ = DeclarationNode::newAggregate( $1, $3->type->symbolic.name, $8, $6, true )->addQualifiers( $2 ); } 2031 2048 | aggregate_type_nobody 2032 2049 ; … … 2040 2057 2041 2058 aggregate_type_nobody: // struct, union - {...} 2042 aggregate_key attribute_list_opt identifier fred2059 aggregate_key attribute_list_opt identifier 2043 2060 { 2044 2061 typedefTable.makeTypedef( *$3, forall || typedefTable.getEnclForall() ? TYPEGENname : TYPEDEFname ); … … 2046 2063 $$ = DeclarationNode::newAggregate( $1, $3, nullptr, nullptr, false )->addQualifiers( $2 ); 2047 2064 } 2048 | aggregate_key attribute_list_opt type_name fred2065 | aggregate_key attribute_list_opt type_name 2049 2066 { 2050 2067 forall = false; // reset … … 2184 2201 ; 2185 2202 2203 // Cannot use attribute_list_opt because of ambiguity with enum_specifier_nobody, which already parses attribute. 2204 // Hence, only a single attribute is allowed after the "ENUM". 2186 2205 enum_type: // enum 2187 ENUM attribute_ list_opt '{' enumerator_list comma_opt '}'2206 ENUM attribute_opt '{' enumerator_list comma_opt '}' 2188 2207 { $$ = DeclarationNode::newEnum( nullptr, $4, true )->addQualifiers( $2 ); } 2189 | ENUM attribute_ list_opt identifier2208 | ENUM attribute_opt identifier 2190 2209 { typedefTable.makeTypedef( *$3 ); } 2191 2210 '{' enumerator_list comma_opt '}' 2192 2211 { $$ = DeclarationNode::newEnum( $3, $6, true )->addQualifiers( $2 ); } 2193 | ENUM attribute_ list_opt type_name2212 | ENUM attribute_opt typedef // enum cannot be generic 2194 2213 '{' enumerator_list comma_opt '}' 2195 { $$ = DeclarationNode::newEnum( $3->type->symbolic.name, $5, true )->addQualifiers( $2 ); } 2214 { $$ = DeclarationNode::newEnum( $3->name, $5, true )->addQualifiers( $2 ); } 2215 | ENUM enum_specifier_nobody '{' enumerator_list comma_opt '}' 2216 // { $$ = DeclarationNode::newEnum( nullptr, $4, true ); } 2217 { SemanticError( yylloc, "Typed enumeration is currently unimplemented." ); $$ = nullptr; } 2218 | ENUM enum_specifier_nobody declarator '{' enumerator_list comma_opt '}' 2219 // { 2220 // typedefTable.makeTypedef( *$3->name ); 2221 // $$ = DeclarationNode::newEnum( nullptr, $5, true ); 2222 // } 2223 { SemanticError( yylloc, "Typed enumeration is currently unimplemented." ); $$ = nullptr; } 2196 2224 | enum_type_nobody 2197 2225 ; 2198 2226 2199 2227 enum_type_nobody: // enum - {...} 2200 ENUM attribute_ list_opt identifier2228 ENUM attribute_opt identifier 2201 2229 { 2202 2230 typedefTable.makeTypedef( *$3 ); 2203 2231 $$ = DeclarationNode::newEnum( $3, 0, false )->addQualifiers( $2 ); 2204 2232 } 2205 | ENUM attribute_ list_opt type_name2233 | ENUM attribute_opt type_name // enum cannot be generic 2206 2234 { 2207 2235 typedefTable.makeTypedef( *$3->type->symbolic.name ); … … 2220 2248 // empty 2221 2249 { $$ = nullptr; } 2222 | '=' constant_expression 2223 { $$ = $2; } 2250 // | '=' constant_expression 2251 // { $$ = $2; } 2252 | '=' initializer 2253 { $$ = $2->get_expression(); } // FIX ME: enum only deals with constant_expression 2224 2254 ; 2225 2255 … … 2403 2433 { $$ = $3; } 2404 2434 | '[' push constant_expression ELLIPSIS constant_expression pop ']' // GCC, multiple array elements 2405 { $$ = new ExpressionNode( new RangeExpr( maybeMoveBuild< Expression >( $3 ), maybeMoveBuild< Expression>( $5 ) ) ); }2435 { $$ = new ExpressionNode( new RangeExpr( maybeMoveBuild<Expression>( $3 ), maybeMoveBuild<Expression>( $5 ) ) ); } 2406 2436 | '.' '[' push field_name_list pop ']' // CFA, tuple field selector 2407 2437 { $$ = $4; } … … 2441 2471 type_parameter: // CFA 2442 2472 type_class identifier_or_type_name 2443 { typedefTable.addToScope( *$2, TYPEDEFname, "9" ); } 2473 { 2474 typedefTable.addToScope( *$2, TYPEDEFname, "9" ); 2475 if ( $1 == TypeDecl::Otype ) { SemanticError( yylloc, "otype keyword is deprecated, use T " ); } 2476 if ( $1 == TypeDecl::Dtype ) { SemanticError( yylloc, "dtype keyword is deprecated, use T &" ); } 2477 if ( $1 == TypeDecl::Ttype ) { SemanticError( yylloc, "ttype keyword is deprecated, use T ..." ); } 2478 } 2444 2479 type_initializer_opt assertion_list_opt 2445 2480 { $$ = DeclarationNode::newTypeParam( $1, $2 )->addTypeInitializer( $4 )->addAssertions( $5 ); } … … 2734 2769 subrange: 2735 2770 constant_expression '~' constant_expression // CFA, integer subrange 2736 { $$ = new ExpressionNode( new RangeExpr( maybeMoveBuild< Expression >( $1 ), maybeMoveBuild< Expression>( $3 ) ) ); }2771 { $$ = new ExpressionNode( new RangeExpr( maybeMoveBuild<Expression>( $1 ), maybeMoveBuild<Expression>( $3 ) ) ); } 2737 2772 ; 2738 2773 … … 2758 2793 | attribute_list attribute 2759 2794 { $$ = $2->addQualifiers( $1 ); } 2795 ; 2796 2797 attribute_opt: 2798 // empty 2799 { $$ = nullptr; } 2800 | attribute 2760 2801 ; 2761 2802 … … 3162 3203 | '[' ']' multi_array_dimension 3163 3204 { $$ = DeclarationNode::newArray( 0, 0, false )->addArray( $3 ); } 3205 | '[' push assignment_expression pop ',' comma_expression ']' 3206 { $$ = DeclarationNode::newArray( $3, 0, false )->addArray( DeclarationNode::newArray( $6, 0, false ) ); } 3207 // { SemanticError( yylloc, "New array dimension is currently unimplemented." ); $$ = nullptr; } 3164 3208 | multi_array_dimension 3165 3209 ; -
src/ResolvExpr/PolyCost.cc
r342af53 r8e4aa05 35 35 PassVisitor<PolyCost> coster( env, indexer ); 36 36 type->accept( coster ); 37 return coster.pass.result;37 return (coster.pass.result > 0) ? 1 : 0; 38 38 } 39 39 … … 87 87 ast::Pass<PolyCost_new> costing( symtab, env ); 88 88 type->accept( costing ); 89 return costing.core.result;89 return (costing.core.result > 0) ? 1 : 0; 90 90 } 91 91 -
src/ResolvExpr/SpecCost.cc
r342af53 r8e4aa05 43 43 // mark specialization of base type 44 44 void postvisit(ReferenceType*) { if ( count >= 0 ) ++count; } 45 46 void postvisit(StructInstType*) { if ( count >= 0 ) ++count; } 47 void postvisit(UnionInstType*) { if ( count >= 0 ) ++count; } 45 48 46 49 private: … … 82 85 void previsit(StructInstType* sty) { 83 86 count = minover( sty->parameters ); 84 visit_children = false;85 87 } 86 88 … … 88 90 void previsit(UnionInstType* uty) { 89 91 count = minover( uty->parameters ); 90 visit_children = false;91 92 } 92 93 … … 174 175 void postvisit( const ast::ArrayType * ) { if ( count >= 0 ) ++count; } 175 176 void postvisit( const ast::ReferenceType * ) { if ( count >= 0 ) ++count; } 177 178 void postvisit( const ast::StructInstType * ) { if ( count >= 0 ) ++count; } 179 void postvisit( const ast::UnionInstType * ) { if ( count >= 0 ) ++count; } 176 180 177 181 // Use the minimal specialization value over returns and params. … … 189 193 void previsit( const ast::StructInstType * sty ) { 190 194 count = minimumPresent( sty->params, expr_result ); 191 visit_children = false;192 195 } 193 196 … … 195 198 void previsit( const ast::UnionInstType * uty ) { 196 199 count = minimumPresent( uty->params, expr_result ); 197 visit_children = false;198 200 } 199 201 -
src/main.cc
r342af53 r8e4aa05 10 10 // Created On : Fri May 15 23:12:02 2015 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Dec 7 15:29:00 202013 // Update Count : 6 3912 // Last Modified On : Fri Feb 19 14:59:00 2021 13 // Update Count : 643 14 14 // 15 15 … … 104 104 static void parse( FILE * input, LinkageSpec::Spec linkage, bool shouldExit = false ); 105 105 static void dump( list< Declaration * > & translationUnit, ostream & out = cout ); 106 static void dump( ast::TranslationUnit && transUnit, ostream & out = cout ); 106 107 107 108 static void backtrace( int start ) { // skip first N stack frames … … 349 350 PASS( "Resolve", ResolvExpr::resolve( transUnit ) ); 350 351 if ( exprp ) { 351 translationUnit = convert( move( transUnit ) ); 352 dump( translationUnit ); 352 dump( move( transUnit ) ); 353 353 return EXIT_SUCCESS; 354 354 } // if … … 492 492 493 493 static const char * description[] = { 494 "diagnostic color: never, always, or auto.",// -c494 "diagnostic color: never, always, auto", // -c 495 495 "wait for gdb to attach", // -g 496 "print help message",// -h496 "print translator help message", // -h 497 497 "generate libcfa.c", // -l 498 498 "generate line marks", // -L … … 500 500 "do not generate line marks", // -N 501 501 "do not read prelude", // -n 502 " generate prototypes for prelude functions",// -p502 "do not generate prelude prototypes => prelude not printed", // -p 503 503 "only print deterministic output", // -d 504 504 "Use the old-ast", // -O … … 506 506 "print", // -P 507 507 "<directory> prelude directory for debug/nodebug", // no flag 508 "<option-list> enable profiling information: \n counters,heap,time,all,none", // -S508 "<option-list> enable profiling information: counters, heap, time, all, none", // -S 509 509 "building cfa standard lib", // -t 510 510 "", // -w … … 732 732 } // dump 733 733 734 static void dump( ast::TranslationUnit && transUnit, ostream & out ) { 735 std::list< Declaration * > translationUnit = convert( move( transUnit ) ); 736 dump( translationUnit, out ); 737 } 738 734 739 // Local Variables: // 735 740 // tab-width: 4 // -
tests/.expect/attributes.nast.x64.txt
r342af53 r8e4aa05 6 6 7 7 } 8 struct __a ttribute__ ((unused)) __anonymous0 {8 struct __anonymous0 { 9 9 }; 10 10 static inline void _X12_constructorFv_S12__anonymous0_autogen___1(struct __anonymous0 *_X4_dstS12__anonymous0_1); … … 26 26 return _X4_retS12__anonymous0_1; 27 27 } 28 __attribute__ ((unused)) struct __anonymous0 _X5DummyS12__anonymous0_1; 28 29 struct __attribute__ ((unused)) Agn1; 29 30 struct __attribute__ ((unused)) Agn2 { -
tests/.expect/attributes.nast.x86.txt
r342af53 r8e4aa05 6 6 7 7 } 8 struct __a ttribute__ ((unused)) __anonymous0 {8 struct __anonymous0 { 9 9 }; 10 10 static inline void _X12_constructorFv_S12__anonymous0_autogen___1(struct __anonymous0 *_X4_dstS12__anonymous0_1); … … 26 26 return _X4_retS12__anonymous0_1; 27 27 } 28 __attribute__ ((unused)) struct __anonymous0 _X5DummyS12__anonymous0_1; 28 29 struct __attribute__ ((unused)) Agn1; 29 30 struct __attribute__ ((unused)) Agn2 { -
tests/.expect/attributes.oast.x64.txt
r342af53 r8e4aa05 6 6 7 7 } 8 struct __a ttribute__ ((unused)) __anonymous0 {8 struct __anonymous0 { 9 9 }; 10 10 static inline void _X12_constructorFv_S12__anonymous0_autogen___1(struct __anonymous0 *_X4_dstS12__anonymous0_1); … … 26 26 return _X4_retS12__anonymous0_1; 27 27 } 28 __attribute__ ((unused)) struct __anonymous0 _X5DummyS12__anonymous0_1; 28 29 struct __attribute__ ((unused)) Agn1; 29 30 struct __attribute__ ((unused)) Agn2 { -
tests/.expect/attributes.oast.x86.txt
r342af53 r8e4aa05 6 6 7 7 } 8 struct __a ttribute__ ((unused)) __anonymous0 {8 struct __anonymous0 { 9 9 }; 10 10 static inline void _X12_constructorFv_S12__anonymous0_autogen___1(struct __anonymous0 *_X4_dstS12__anonymous0_1); … … 26 26 return _X4_retS12__anonymous0_1; 27 27 } 28 __attribute__ ((unused)) struct __anonymous0 _X5DummyS12__anonymous0_1; 28 29 struct __attribute__ ((unused)) Agn1; 29 30 struct __attribute__ ((unused)) Agn2 { -
tests/Makefile.am
r342af53 r8e4aa05 11 11 ## Created On : Sun May 31 09:08:15 2015 12 12 ## Last Modified By : Peter A. Buhr 13 ## Last Modified On : Fri Oct 9 23:13:07 202014 ## Update Count : 8613 ## Last Modified On : Tue Mar 2 21:39:01 2021 14 ## Update Count : 90 15 15 ############################################################################### 16 16 … … 44 44 -Wall \ 45 45 -Wno-unused-function \ 46 -quiet @CFA_FLAGS@ \ 47 -DIN_DIR="${abs_srcdir}/.in/" 46 -quiet @CFA_FLAGS@ 48 47 49 48 AM_CFAFLAGS = -XCFA --deterministic-out … … 76 75 long_tests.hfa \ 77 76 .in/io.data \ 77 io/.in/io.data \ 78 78 avltree/avl.h \ 79 79 avltree/avl-private.h \ … … 142 142 # don't use distcc to do the linking because distcc doesn't do linking 143 143 % : %.cfa $(CFACCBIN) 144 $(CFACOMPILETEST) -c -o $(abspath ${@}).o 144 $(CFACOMPILETEST) -c -o $(abspath ${@}).o -DIN_DIR="$(abspath $(dir ${<}))/.in/" 145 145 $(CFACCLINK) ${@}.o -o $(abspath ${@}) 146 146 rm $(abspath ${@}).o … … 170 170 171 171 SYNTAX_ONLY_CODE = expression typedefRedef variableDeclarator switch numericConstants identFuncDeclarator forall \ 172 init1 limits nested-types stdincludes cast labelledExit array builtins/sync warnings/self-assignment172 init1 limits nested-types stdincludes cast labelledExit array quasiKeyword include/includes builtins/sync warnings/self-assignment 173 173 $(SYNTAX_ONLY_CODE): % : %.cfa $(CFACCBIN) 174 174 $(CFACOMPILE_SYNTAX) -
tests/alloc2.cfa
r342af53 r8e4aa05 16 16 bool passed = (malloc_size(ip) == size) && (malloc_usable_size(ip) >= size) && (malloc_alignment(ip) == align) && ((uintptr_t)ip % align == 0); 17 17 if (!passed) { 18 printf("failed test %3d: %4 lu %4lu but got %4lu ( %3lu ) %4lu\n", tests_total, size, align, malloc_size(ip), malloc_usable_size(ip), malloc_alignment(ip));18 printf("failed test %3d: %4zu %4zu but got %4zu ( %3zu ) %4zu\n", tests_total, size, align, malloc_size(ip), malloc_usable_size(ip), malloc_alignment(ip)); 19 19 tests_failed += 1; 20 20 } -
tests/attributes.cfa
r342af53 r8e4aa05 10 10 // Created On : Mon Feb 6 16:07:02 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Nov 6 17:51:12 201813 // Update Count : 1712 // Last Modified On : Mon Jan 25 21:26:41 2021 13 // Update Count : 20 14 14 // 15 15 … … 22 22 23 23 // aggregate_name 24 struct __attribute__(( unused )) {} ;24 struct __attribute__(( unused )) {} Dummy; 25 25 struct __attribute__(( unused )) Agn1; 26 26 struct __attribute__(( unused )) Agn2 {}; -
tests/avltree/avl-private.cfa
r342af53 r8e4aa05 11 11 // an AVL tree's height is easy to compute 12 12 // just follow path with the larger balance 13 forall( otype K | Comparable(K), otypeV)13 forall(K | Comparable(K), V) 14 14 int height(tree(K, V) * t){ 15 15 int helper(tree(K, V) * t, int ht){ … … 27 27 } 28 28 29 forall( otype K | Comparable(K), otypeV)29 forall(K | Comparable(K), V) 30 30 int calcBalance(tree(K, V) * t){ 31 31 int l = height(t->left); … … 36 36 37 37 // re-establish the link between parent and child 38 forall( otype K | Comparable(K), otypeV)38 forall(K | Comparable(K), V) 39 39 void relinkToParent(tree(K, V) * t){ 40 40 tree(K, V) * parent = t->parent; // FIX ME!! … … 49 49 50 50 // rotate left from t 51 forall( otype K | Comparable(K), otypeV)51 forall(K | Comparable(K), V) 52 52 tree(K, V) * rotateLeft(tree(K, V) * t){ 53 53 tree(K, V) * newRoot = t->right; … … 68 68 69 69 // rotate right from t 70 forall( otype K | Comparable(K), otypeV)70 forall(K | Comparable(K), V) 71 71 tree(K, V) * rotateRight(tree(K, V) * t){ 72 72 tree(K, V) * newRoot = t->left; … … 87 87 88 88 // balances a node that has balance factor -2 or 2 89 forall( otype K | Comparable(K), otypeV)89 forall(K | Comparable(K), V) 90 90 tree(K, V) * fix(tree(K, V) * t){ 91 91 // ensure that t's balance factor is one of … … 113 113 114 114 // attempt to fix the tree, if necessary 115 forall( otype K | Comparable(K), otypeV)115 forall(K | Comparable(K), V) 116 116 tree(K, V) * tryFix(tree(K, V) * t){ 117 117 int b = calcBalance(t); … … 126 126 127 127 // sets parent field of c to be p 128 forall( otype K | Comparable(K), otypeV)128 forall(K | Comparable(K), V) 129 129 void setParent(tree(K, V) * c, tree(K, V) * p){ 130 130 if (! empty(c)){ -
tests/avltree/avl-private.h
r342af53 r8e4aa05 5 5 6 6 // attempt to fix the tree, if necessary 7 forall( otype K | Comparable(K), otypeV)7 forall(K | Comparable(K), V) 8 8 tree(K, V) * tryFix(tree(K, V) * t); 9 9 10 10 // sets parent field of c to be p 11 forall( otype K | Comparable(K), otypeV)11 forall(K | Comparable(K), V) 12 12 void setParent(tree(K, V) * c, tree(K, V) * p); 13 13 14 forall( otype K | Comparable(K), otypeV)14 forall(K | Comparable(K), V) 15 15 int height(tree(K, V) * t); -
tests/avltree/avl.h
r342af53 r8e4aa05 9 9 // #include <lib.h> 10 10 11 trait Comparable( otypeT) {11 trait Comparable(T) { 12 12 int ?<?(T, T); 13 13 }; 14 14 15 forall( otypeT | Comparable(T))15 forall(T | Comparable(T)) 16 16 int ?==?(T t1, T t2); 17 17 18 forall( otypeT | Comparable(T))18 forall(T | Comparable(T)) 19 19 int ?>?(T t1, T t2); 20 20 … … 41 41 42 42 // temporary: need forward decl to get around typedef problem 43 forall( otype K | Comparable(K), otypeV)43 forall(K | Comparable(K), V) 44 44 struct tree; 45 45 46 forall( otype K | Comparable(K), otypeV)46 forall(K | Comparable(K), V) 47 47 struct tree { 48 48 K key; … … 54 54 }; 55 55 56 forall( otype K | Comparable(K), otypeV)56 forall(K | Comparable(K), V) 57 57 void ?{}(tree(K, V) &t, K key, V value); 58 58 59 forall( otype K, otypeV)59 forall(K | Comparable(K), V) 60 60 void ^?{}(tree(K, V) & t); 61 61 62 forall( otype K | Comparable(K), otypeV)62 forall(K | Comparable(K), V) 63 63 tree(K, V) * create(K key, V value); 64 64 65 forall( otype K | Comparable(K), otypeV)65 forall(K | Comparable(K), V) 66 66 V * find(tree(K, V) * t, K key); 67 67 68 forall( otype K | Comparable(K), otypeV)68 forall(K | Comparable(K), V) 69 69 int empty(tree(K, V) * t); 70 70 71 71 // returns the root of the tree 72 forall( otype K | Comparable(K), otypeV)72 forall(K | Comparable(K), V) 73 73 int insert(tree(K, V) ** t, K key, V value); 74 74 75 forall( otype K | Comparable(K), otypeV)75 forall(K | Comparable(K), V) 76 76 int remove(tree(K, V) ** t, K key); 77 77 78 forall( otype K | Comparable(K), otypeV)78 forall(K | Comparable(K), V) 79 79 void copy(tree(K, V) * src, tree(K, V) ** ret); 80 80 81 forall( otype K | Comparable(K), otypeV)81 forall(K | Comparable(K), V) 82 82 void for_each(tree(K, V) * t, void (*func)(V)); 83 83 -
tests/avltree/avl0.cfa
r342af53 r8e4aa05 1 1 #include "avl.h" 2 2 3 forall( otypeT | Comparable(T))3 forall(T | Comparable(T)) 4 4 int ?==?(T t1, T t2) { 5 5 return !(t1 < t2) && !(t2 < t1); 6 6 } 7 7 8 forall( otypeT | Comparable(T))8 forall(T | Comparable(T)) 9 9 int ?>?(T t1, T t2) { 10 10 return t2 < t1; -
tests/avltree/avl1.cfa
r342af53 r8e4aa05 3 3 #include <stdlib.hfa> 4 4 5 forall( otype K | Comparable(K), otypeV)5 forall(K | Comparable(K), V) 6 6 void ?{}(tree(K, V) &t, K key, V value){ 7 7 (t.key) { key }; … … 13 13 } 14 14 15 forall( otype K, otypeV)15 forall(K| Comparable(K), V) 16 16 void ^?{}(tree(K, V) & t){ 17 17 delete(t.left); … … 21 21 } 22 22 23 forall( otype K | Comparable(K), otypeV)23 forall(K | Comparable(K), V) 24 24 tree(K, V) * create(K key, V value) { 25 25 // infinite loop trying to resolve ... t = malloc(); -
tests/avltree/avl2.cfa
r342af53 r8e4aa05 2 2 #include "avl-private.h" 3 3 4 forall( otype K | Comparable(K), otypeV)4 forall(K | Comparable(K), V) 5 5 V * find(tree(K, V) * t, K key){ 6 6 if (empty(t)){ … … 18 18 } 19 19 20 forall( otype K | Comparable(K), otypeV)20 forall(K | Comparable(K), V) 21 21 int empty(tree(K, V) * t){ 22 22 return t == NULL; … … 24 24 25 25 // returns the root of the tree 26 forall( otype K | Comparable(K), otypeV)26 forall(K | Comparable(K), V) 27 27 int insert(tree(K, V) ** t, K key, V value) { 28 28 // handles a non-empty tree -
tests/avltree/avl3.cfa
r342af53 r8e4aa05 4 4 5 5 // swaps the data within two tree nodes 6 forall( otype K | Comparable(K), otypeV)6 forall(K | Comparable(K), V) 7 7 void node_swap(tree(K, V) * t, tree(K, V) * t2){ 8 8 swap( t->key, t2->key); … … 11 11 12 12 // go left as deep as possible from within the right subtree 13 forall( otype K | Comparable(K), otypeV)13 forall(K | Comparable(K), V) 14 14 tree(K, V) * find_successor(tree(K, V) * t){ 15 15 tree(K, V) * find_successor_helper(tree(K, V) * t){ … … 25 25 26 26 // cleanup - don't want to deep delete, so set children to NULL first. 27 forall( otype K | Comparable(K), otypeV)27 forall(K | Comparable(K), V) 28 28 void deleteSingleNode(tree(K, V) * t) { 29 29 t->left = NULL; … … 33 33 34 34 // does the actual remove operation once we've found the node in question 35 forall( otype K | Comparable(K), otypeV)35 forall(K | Comparable(K), V) 36 36 tree(K, V) * remove_node(tree(K, V) * t){ 37 37 // is the node a leaf? … … 85 85 86 86 // finds the node that needs to be removed 87 forall( otype K | Comparable(K), otypeV)87 forall(K | Comparable(K), V) 88 88 tree(K, V) * remove_helper(tree(K, V) * t, K key, int * worked){ 89 89 if (empty(t)){ … … 106 106 } 107 107 108 forall( otype K | Comparable(K), otypeV)108 forall(K | Comparable(K), V) 109 109 int remove(tree(K, V) ** t, K key){ 110 110 int worked = 0; -
tests/avltree/avl4.cfa
r342af53 r8e4aa05 4 4 // Perform a shallow copy of src, return the 5 5 // new tree in ret 6 forall( otype K | Comparable(K), otypeV)6 forall(K | Comparable(K), V) 7 7 int copy(tree(K, V) * src, tree(K, V) ** ret){ 8 8 tree(K, V) * helper(tree(K, V) * t, int * worked){ … … 35 35 36 36 // Apply func to every value element in t, using an in order traversal 37 forall( otype K | Comparable(K), otypeV)37 forall(K | Comparable(K), V) 38 38 void for_each(tree(K, V) * t, int (*func)(V)) { 39 39 if (t == NULL) { -
tests/bugs/10.cfa
r342af53 r8e4aa05 2 2 // https://cforall.uwaterloo.ca/trac/ticket/10 3 3 4 forall( otypeT)4 forall(T) 5 5 struct result { 6 6 union { -
tests/bugs/104.cfa
r342af53 r8e4aa05 4 4 [ float, float ] modf_( float x ); 5 5 6 forall( otypeT | { [T, T] modf_(T); })6 forall(T | { [T, T] modf_(T); }) 7 7 void modf(T); 8 8 -
tests/bugs/194.cfa
r342af53 r8e4aa05 2 2 // https://cforall.uwaterloo.ca/trac/ticket/194 3 3 4 forall( dtype T| sized(T) ) T * foo( void ) {4 forall( T & | sized(T) ) T * foo( void ) { 5 5 printf( "foo1\n" ); 6 6 return (T *)0; 7 7 } 8 forall( dtype T| sized(T) ) T & foo( void ) {8 forall( T & | sized(T) ) T & foo( void ) { 9 9 printf( "foo2\n" ); 10 10 return (T &)*(T *)0; -
tests/bugs/196.cfa
r342af53 r8e4aa05 2 2 // https://cforall.uwaterloo.ca/trac/ticket/196 3 3 4 forall( dtype T)4 forall(T &) 5 5 struct link; 6 6 7 forall( dtype T)7 forall(T &) 8 8 struct link { 9 9 link(T) * next; … … 12 12 // ----- 13 13 14 forall( dtype T)14 forall(T &) 15 15 struct foo; 16 16 17 forall( dtype U)17 forall(U &) 18 18 struct bar { 19 19 foo(U) * data; 20 20 }; 21 21 22 forall( dtype T)22 forall(T &) 23 23 struct foo {}; 24 24 -
tests/bugs/203-2.cfa
r342af53 r8e4aa05 1 1 // Trac ticket: https://cforall.uwaterloo.ca/trac/ticket/203 2 2 3 forall( dtype A)3 forall(A &) 4 4 struct empty { 5 5 // Nothing. 6 6 }; 7 7 8 forall( dtype C)8 forall(C &) 9 9 struct wrap_e { 10 10 empty(C) field; -
tests/bugs/203-7.cfa
r342af53 r8e4aa05 1 1 // Trac ticket: https://cforall.uwaterloo.ca/trac/ticket/203 2 2 3 forall( dtype A)3 forall(A &) 4 4 struct empty { 5 5 // Nothing. 6 6 }; 7 7 8 forall( dtype C)8 forall(C &) 9 9 struct wrap_e { 10 10 empty(C) field; -
tests/bugs/203-9.cfa
r342af53 r8e4aa05 1 1 // Trac ticket: https://cforall.uwaterloo.ca/trac/ticket/203 2 2 3 forall( dtype A)3 forall(A &) 4 4 struct empty { 5 5 // Nothing. 6 6 }; 7 7 8 forall( dtype C)8 forall(C &) 9 9 struct wrap_e { 10 10 empty(C) field; -
tests/bugs/7.cfa
r342af53 r8e4aa05 8 8 9 9 // (Bug 1 unresolved as of this test.) 10 forall( otypeT)10 forall(T) 11 11 struct stack_node; 12 12 13 forall( otypeT)13 forall(T) 14 14 struct stack_node { 15 15 stack_node(T) * next; … … 17 17 }; 18 18 19 forall( otypeT)19 forall(T) 20 20 struct stack { 21 21 stack_node(T) * head; 22 22 }; 23 23 24 trait stack_errors( otypeT) {24 trait stack_errors(T) { 25 25 T emptyStackHandler (stack(T) * this); 26 26 }; 27 27 28 forall( otypeT | stack_errors(T))28 forall(T | stack_errors(T)) 29 29 T pop (stack(T) * this) { 30 30 return (T){}; -
tests/castError.cfa
r342af53 r8e4aa05 14 14 // 15 15 16 forall( otypeT) struct S { T p; };16 forall(T) struct S { T p; }; 17 17 int f; 18 18 S(int) sint; -
tests/concurrent/examples/boundedBufferEXT.cfa
r342af53 r8e4aa05 24 24 enum { BufferSize = 50 }; 25 25 26 forall( otypeT ) {26 forall( T ) { 27 27 monitor Buffer { 28 28 int front, back, count; -
tests/concurrent/examples/boundedBufferINT.cfa
r342af53 r8e4aa05 24 24 enum { BufferSize = 50 }; 25 25 26 forall( otypeT ) {26 forall( T ) { 27 27 monitor Buffer { 28 28 condition full, empty; -
tests/concurrent/examples/quickSort.generic.cfa
r342af53 r8e4aa05 21 21 #include <string.h> // strcmp 22 22 23 forall( otypeT | { int ?<?( T, T ); } ) {23 forall( T | { int ?<?( T, T ); } ) { 24 24 thread Quicksort { 25 25 T * values; // communication variables -
tests/concurrent/multi-monitor.cfa
r342af53 r8e4aa05 38 38 } 39 39 40 forall( dtype T| sized(T) | { void ^?{}(T & mutex); })40 forall(T & | sized(T) | { void ^?{}(T & mutex); }) 41 41 void delete_mutex(T * x) { 42 42 ^(*x){}; -
tests/concurrent/thread.cfa
r342af53 r8e4aa05 1 1 #include <fstream.hfa> 2 2 #include <kernel.hfa> 3 #include <locks.hfa> 3 4 #include <stdlib.hfa> 4 5 #include <thread.hfa> -
tests/errors/completeType.cfa
r342af53 r8e4aa05 1 1 void foo(int *) {} 2 2 void bar(void *) {} 3 forall( otypeT) void baz(T *);4 forall( dtype T) void qux(T *);5 forall( dtype T| sized(T)) void quux(T *);3 forall(T) void baz(T *); 4 forall(T &) void qux(T *); 5 forall(T & | sized(T)) void quux(T *); 6 6 7 7 struct A; // incomplete … … 39 39 40 40 41 forall( otypeT)41 forall(T) 42 42 void baz(T * x) { 43 43 // okay … … 49 49 } 50 50 51 forall( dtype T)51 forall(T &) 52 52 void qux(T * y) { 53 53 // okay … … 61 61 } 62 62 63 forall( dtype T| sized(T))63 forall(T & | sized(T)) 64 64 void quux(T * z) { 65 65 // okay -
tests/exceptions/defaults.cfa
r342af53 r8e4aa05 55 55 56 56 void unhandled_test(void) { 57 forall( dtype T, dtype V| is_exception(T, V))57 forall(T &, V & | is_exception(T, V)) 58 58 void defaultTerminationHandler(T &) { 59 59 throw (unhandled_exception){}; -
tests/exceptions/polymorphic.cfa
r342af53 r8e4aa05 3 3 #include <exception.hfa> 4 4 5 FORALL_TRIVIAL_EXCEPTION(proxy, ( otypeT), (T));6 FORALL_TRIVIAL_INSTANCE(proxy, ( otypeU), (U))5 FORALL_TRIVIAL_EXCEPTION(proxy, (T), (T)); 6 FORALL_TRIVIAL_INSTANCE(proxy, (U), (U)) 7 7 8 8 const char * msg(proxy(int) * this) { return "proxy(int)"; } … … 33 33 } 34 34 35 FORALL_DATA_EXCEPTION(cell, ( otypeT), (T))(35 FORALL_DATA_EXCEPTION(cell, (T), (T))( 36 36 T data; 37 37 ); 38 38 39 FORALL_DATA_INSTANCE(cell, ( otypeT), (T))39 FORALL_DATA_INSTANCE(cell, (T), (T)) 40 40 41 41 const char * msg(cell(int) * this) { return "cell(int)"; } -
tests/exceptions/virtual-poly.cfa
r342af53 r8e4aa05 16 16 }; 17 17 18 forall( otypeT)18 forall(T) 19 19 struct mono_child_vtable { 20 20 mono_base_vtable const * const parent; 21 21 }; 22 22 23 forall( otypeT)23 forall(T) 24 24 struct mono_child { 25 25 mono_child_vtable(T) const * virtual_table; … … 37 37 } 38 38 39 forall( otypeU)39 forall(U) 40 40 struct poly_base_vtable { 41 41 poly_base_vtable(U) const * const parent; 42 42 }; 43 43 44 forall( otypeU)44 forall(U) 45 45 struct poly_base { 46 46 poly_base_vtable(U) const * virtual_table; 47 47 }; 48 48 49 forall( otypeV)49 forall(V) 50 50 struct poly_child_vtable { 51 51 poly_base_vtable(V) const * const parent; 52 52 }; 53 53 54 forall( otypeV)54 forall(V) 55 55 struct poly_child { 56 56 poly_child_vtable(V) const * virtual_table; -
tests/forall.cfa
r342af53 r8e4aa05 15 15 16 16 void g1() { 17 forall( otypeT ) T f( T ) {};17 forall( T ) T f( T ) {}; 18 18 void f( int ) {}; 19 19 void h( void (*p)(void) ) {}; … … 32 32 33 33 void g2() { 34 forall( otypeT ) void f( T, T ) {}35 forall( otype T, otypeU ) void f( T, U ) {}34 forall( T ) void f( T, T ) {} 35 forall( T, U ) void f( T, U ) {} 36 36 37 37 int x; … … 45 45 } 46 46 47 typedef forall ( otypeT ) int (* f)( int );48 49 forall( otypeT )47 typedef forall ( T ) int (* f)( int ); 48 49 forall( T ) 50 50 void swap( T left, T right ) { 51 51 T temp = left; … … 54 54 } 55 55 56 trait sumable( otypeT ) {56 trait sumable( T ) { 57 57 void ?{}( T &, zero_t ); // 0 literal constructor 58 58 T ?+?( T, T ); // assortment of additions … … 62 62 }; // sumable 63 63 64 forall( otypeT | sumable( T ) ) // use trait64 forall( T | sumable( T ) ) // use trait 65 65 T sum( size_t size, T a[] ) { 66 66 T total = 0; // initialize by 0 constructor … … 70 70 } // sum 71 71 72 forall( otypeT | { T ?+?( T, T ); T ?++( T & ); [T] ?+=?( T &,T ); } )72 forall( T | { T ?+?( T, T ); T ?++( T & ); [T] ?+=?( T &,T ); } ) 73 73 T twice( T t ) { 74 74 return t + t; 75 75 } 76 76 77 forall( otypeT | { int ?<?(T, T); } )77 forall( T | { int ?<?(T, T); } ) 78 78 T min( T t1, T t2 ) { 79 79 return t1 < t2 ? t1 : t2; … … 91 91 92 92 // Multiple forall 93 forall( otype T ) forall( otypeS ) struct { int i; };94 forall( otype T ) struct { int i; } forall( otypeS );95 struct { int i; } forall( otype T ) forall( otypeS );96 forall( otype W ) struct { int i; } forall( otype T ) forall( otypeS );93 forall( T ) forall( S ) struct { int i; }; 94 forall( T ) struct { int i; } forall( S ); 95 struct { int i; } forall( T ) forall( S ); 96 forall( W ) struct { int i; } forall( T ) forall( S ); 97 97 98 98 // Distribution 99 99 struct P { int i; }; 100 forall( otypeT ) struct Q { T i; };101 forall( otypeT ) struct { int i; };100 forall( T ) struct Q { T i; }; 101 forall( T ) struct { int i; }; 102 102 struct KK { int i; }; 103 103 inline static { 104 104 void RT1() {} 105 105 } 106 forall( otypeT ) {106 forall( T ) { 107 107 T RT2( T ) { 108 108 typedef int TD1; 109 109 struct S1 { T t; }; 110 110 } 111 forall( otypeX ) {111 forall( X ) { 112 112 typedef int TD2; 113 113 struct S2 {}; … … 117 117 } 118 118 extern "C" { 119 forall( otypeW ) {119 forall( W ) { 120 120 W RT3( W ) {} 121 121 struct S3 {}; … … 123 123 } 124 124 void RT4() { 125 forall( otypeW ) struct S4 {};125 forall( W ) struct S4 {}; 126 126 typedef int TD3; 127 127 } … … 147 147 148 148 static inline { 149 forall( otypeT ) {149 forall( T ) { 150 150 int RT6( T p ); 151 151 } 152 forall( otype T, otypeU ) {152 forall( T, U ) { 153 153 int RT7( T, U ); 154 154 } 155 155 } 156 static forall( otypeT ) {156 static forall( T ) { 157 157 int RT8( T ); 158 158 } 159 forall( otypeT ) inline static {159 forall( T ) inline static { 160 160 int RT9( T ) { T t; return 3; } 161 161 } 162 162 163 forall( otypeT | { T ?+?( T, T ); } ) {164 forall( otypeS | { T ?+?( T, S ); } ) {165 forall( otypeW ) T bar( T t, S s ) { return t + s; }166 forall( otypeW | { W ?+?( T, W ); } ) W baz( T t, S s, W w ) { return t + s + w; }163 forall( T | { T ?+?( T, T ); } ) { 164 forall( S | { T ?+?( T, S ); } ) { 165 forall( W ) T bar( T t, S s ) { return t + s; } 166 forall( W | { W ?+?( T, W ); } ) W baz( T t, S s, W w ) { return t + s + w; } 167 167 struct W { T t; } (int,int) ww; 168 168 struct P pp; … … 170 170 } 171 171 172 forall( otype T | { T ?+?( T, T ); } ) forall( otypeS | { T ?+?( T, S ); } )172 forall( T | { T ?+?( T, T ); } ) forall( S | { T ?+?( T, S ); } ) 173 173 struct XW { T t; }; 174 174 XW(int,int) xww; 175 175 176 forall( otypeT ) struct S { T t; } (int) x, y, z;177 forall( otypeT ) struct { T t; } (int) a, b, c;178 179 forall( otype T ) static forall( otypeS ) {180 forall( otypeX ) struct U {176 forall( T ) struct S { T t; } (int) x, y, z; 177 forall( T ) struct { T t; } (int) a, b, c; 178 179 forall( T ) static forall( S ) { 180 forall( X ) struct U { 181 181 T x; 182 182 }; 183 183 } 184 184 185 forall( otypeT ) {185 forall( T ) { 186 186 extern "C" { 187 187 struct SS { T t; }; -
tests/function-operator.cfa
r342af53 r8e4aa05 22 22 23 23 // STL-like Algorithms 24 trait Assignable( dtype T, dtype U) { T ?=?(T &, U); };25 trait Copyable( dtype T) { void ?{}(T &, T); };26 trait Destructable( dtype T) { void ^?{}(T &); };24 trait Assignable(T &, U &) { T ?=?(T &, U); }; 25 trait Copyable(T &) { void ?{}(T &, T); }; 26 trait Destructable(T &) { void ^?{}(T &); }; 27 27 28 trait Iterator( dtype iter | sized(iter) | Copyable(iter) | Destructable(iter), otypeT) {28 trait Iterator(iter & | sized(iter) | Copyable(iter) | Destructable(iter), T) { 29 29 T & *?(iter); 30 30 iter ++?(iter &); … … 32 32 }; 33 33 34 forall( otype Tin, dtype Input | Iterator(Input, Tin), otype Tout, dtype Output| Iterator(Output, Tout) | Assignable(Tout, Tin))34 forall(Tin, Input & | Iterator(Input, Tin), Tout, Output & | Iterator(Output, Tout) | Assignable(Tout, Tin)) 35 35 Output copy(Input first, Input last, Output result) { 36 36 while (first != last) { … … 42 42 43 43 // test ?()(T *, ...) -- ?() with function call-by-pointer 44 forall( otype Tin, dtype Input | Iterator(Input, Tin), otype Tout, dtype Output | Iterator(Output, Tout), otype FuncRet, dtype Func| { FuncRet ?()(Func *, Tin); } | Assignable(Tout, FuncRet))44 forall(Tin, Input & | Iterator(Input, Tin), Tout, Output & | Iterator(Output, Tout), FuncRet, Func & | { FuncRet ?()(Func *, Tin); } | Assignable(Tout, FuncRet)) 45 45 Output transform (Input first, Input last, Output result, Func * op) { 46 46 while (first != last) { … … 52 52 53 53 // test ?()(T, ...) -- ?() with function call-by-value 54 forall( dtype Iter, otype T | Iterator(Iter, T), otypePred | { int ?()(Pred, T); })54 forall(Iter &, T | Iterator(Iter, T), Pred | { int ?()(Pred, T); }) 55 55 Iter find_if (Iter first, Iter last, Pred pred) { 56 56 while (first != last) { … … 62 62 63 63 // test ?()(T, ...) -- ?() with function call-by-reference 64 forall( otype Generator, otype GenRet | { GenRet ?()(Generator &); }, dtype Iter, otypeT | Iterator(Iter, T) | Assignable(T, GenRet))64 forall(Generator, GenRet | { GenRet ?()(Generator &); }, Iter &, T | Iterator(Iter, T) | Assignable(T, GenRet)) 65 65 void generate(Iter first, Iter last, Generator & gen) { 66 66 int i = 0; … … 108 108 } 109 109 110 forall( otypeT | { int ?==?(T, T); })110 forall(T | { int ?==?(T, T); }) 111 111 struct Equals { 112 112 T val; 113 113 }; 114 114 115 forall( otypeT | { int ?==?(T, T); })115 forall(T | { int ?==?(T, T); }) 116 116 int ?()(Equals(T) eq, T x) { 117 117 return eq.val == x; 118 118 } 119 119 120 forall( otypeT | { T ?*?(T, T); })120 forall(T | { T ?*?(T, T); }) 121 121 struct Multiply { 122 122 T val; 123 123 }; 124 124 125 forall( otypeT | { T ?*?(T, T); })125 forall(T | { T ?*?(T, T); }) 126 126 T ?()(Multiply(T) * mult, T x) { 127 127 return mult->val * x; … … 130 130 // TODO: generalize to ttype return; doesn't work yet 131 131 // like std::function 132 forall( otype Return, ttype Args)132 forall(Return, Args...) 133 133 struct function { 134 134 Return (*f)(Args); -
tests/genericUnion.cfa
r342af53 r8e4aa05 16 16 #include <limits.hfa> 17 17 18 forall( otypeT)18 forall(T) 19 19 union ByteView { 20 20 T val; … … 22 22 }; 23 23 24 forall( otypeT)24 forall(T) 25 25 void print(ByteView(T) x) { 26 26 for (int i = 0; i < sizeof(int); i++) { // want to change to sizeof(T) … … 29 29 } 30 30 31 forall( otypeT)31 forall(T) 32 32 void f(ByteView(T) x, T val) { 33 33 print(x); -
tests/global-monomorph.cfa
r342af53 r8e4aa05 1 1 // Create monomorphic instances of polymorphic types at global scope. 2 2 3 forall( dtype T)3 forall(T &) 4 4 void poly0(T &) {} 5 5 6 forall( dtype T| sized(T))6 forall(T & | sized(T)) 7 7 void poly1(T &) {} 8 8 9 forall( otypeT)9 forall(T) 10 10 void poly2(T &) {} 11 11 -
tests/identity.cfa
r342af53 r8e4aa05 16 16 #include <fstream.hfa> 17 17 18 forall( otypeT )18 forall( T ) 19 19 T identity( T t ) { 20 20 return t; -
tests/init1.cfa
r342af53 r8e4aa05 120 120 } 121 121 122 forall ( dtype T, dtype S)122 forall (T &, S &) 123 123 T & anycvt( S & s ) { 124 124 return s; // mismatched referenced type 125 125 } 126 126 127 forall ( dtype T, dtype S)127 forall (T &, S &) 128 128 T * anycvt( S * s ) { 129 129 return s; // mismatched referenced type -
tests/io/.expect/io1.oast.txt
r342af53 r8e4aa05 1 9 6 28 0 7 1 22 0 1 2 33 01234 01235 6 1 opening delimiters 7 2 x (1 x [2 x {3 x =4 x $5 x £6 x ¥7 x ¡8 x ¿9 x «10 8 3 9 4 closing delimiters 10 1, x 2. x 3; x 4! x 5? x 6% x 7 ¢ x 8» x 9) x 10] x 11} x5 1, x 2. x 3; x 4! x 5? x 6% x 7¢ x 8» x 9) x 10] x 11} x 11 6 12 7 opening/closing delimiters … … 24 19 x ( 1 ) x 2 , x 3 :x: 4 25 20 21 spacing 22 0 1 2 3 23 0123 24 0123 25 26 expressions 27 9 6 28 0 7 1 2 -
tests/io/io1.cfa
r342af53 r8e4aa05 10 10 // Created On : Wed Mar 2 16:56:02 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Mar 4 21:42:47 201913 // Update Count : 11 512 // Last Modified On : Sun Feb 21 10:07:07 2021 13 // Update Count : 119 14 14 // 15 15 … … 17 17 18 18 int main() { 19 int x = 3, y = 5, z = 7; 20 sout | x * 3 | y + 1 | z << 2 | x == y | (x | y) | (x || y) | (x > z ? 1 : 2); 21 sout | 0 | 1 | 2 | 3; 22 sout | '0' | '1' | '2' | '3'; 23 sout | 0 | "" | 1 | "" | 2 | "" | 3; 24 sout | nl; 19 sout | nlOff; // auto nl off 25 20 26 sout | nlOff;27 21 sout | "opening delimiters" | nl; 28 22 sout | "x (" | 1; … … 61 55 sout | nl | nl; 62 56 63 sout | nlOn; 57 sout | nlOn; // auto nl on 58 64 59 sout | "override opening/closing delimiters"; 65 60 sout | "x ( " | 1 | " ) x" | 2 | " , x" | 3 | " :x: " | 4; 66 61 sout | nl; 62 63 sout | "spacing"; 64 sout | 0 | 1 | 2 | 3; 65 sout | '0' | '1' | '2' | '3'; 66 sout | 0 | "" | 1 | "" | 2 | "" | 3; 67 sout | nl; 68 69 sout | "expressions"; 70 int x = 3, y = 5, z = 7; 71 sout | x * 3 | y + 1 | z << 2 | x == y | (x | y) | (x || y) | (x > z ? 1 : 2); 67 72 } 68 73 -
tests/nested-types.cfa
r342af53 r8e4aa05 16 16 typedef int N; 17 17 struct A { 18 forall( otypeT)18 forall(T) 19 19 struct N { 20 20 T x; -
tests/poly-d-cycle.cfa
r342af53 r8e4aa05 1 1 // Check that a cycle of polymorphic dtype structures can be instancated. 2 2 3 forall( dtype T)3 forall(T &) 4 4 struct func_table; 5 5 6 forall( dtype U)6 forall(U &) 7 7 struct object { 8 8 func_table(U) * virtual_table; 9 9 }; 10 10 11 forall( dtype T)11 forall(T &) 12 12 struct func_table { 13 13 void (*object_func)(object(T) *); -
tests/poly-o-cycle.cfa
r342af53 r8e4aa05 1 1 // Check that a cycle of polymorphic otype structures can be instancated. 2 2 3 forall( otypeT)3 forall(T) 4 4 struct func_table; 5 5 6 forall( otypeU)6 forall(U) 7 7 struct object { 8 8 func_table(U) * virtual_table; 9 9 }; 10 10 11 forall( otypeT)11 forall(T) 12 12 struct func_table { 13 13 void (*object_func)(object(T) *); -
tests/polymorphism.cfa
r342af53 r8e4aa05 18 18 #include <fstream.hfa> 19 19 20 forall( otypeT)20 forall(T) 21 21 T f(T x, T y) { 22 22 x = y; … … 24 24 } 25 25 26 forall( otypeT) T ident(T x) {26 forall(T) T ident(T x) { 27 27 return x; 28 28 } 29 29 30 forall( otype T, otypeU )30 forall( T, U ) 31 31 size_t struct_size( T i, U j ) { 32 32 struct S { T i; U j; }; … … 34 34 } 35 35 36 forall( otype T, otypeU )36 forall( T, U ) 37 37 size_t union_size( T i, U j ) { 38 38 union B { T i; U j; }; … … 41 41 42 42 // perform some simple operations on aggregates of T and U 43 forall( otype T | { void print(T); int ?==?(T, T); }, otypeU | { void print(U); U ?=?(U&, zero_t); } )43 forall( T | { void print(T); int ?==?(T, T); }, U | { void print(U); U ?=?(U&, zero_t); } ) 44 44 U foo(T i, U j) { 45 45 struct S { T i; U j; }; -
tests/raii/ctor-autogen.cfa
r342af53 r8e4aa05 33 33 34 34 // dtype-static generic type is otype 35 forall( dtype T)35 forall(T &) 36 36 struct DtypeStaticStruct { 37 37 T * data; … … 39 39 }; 40 40 41 forall( dtype T)41 forall(T &) 42 42 union DtypeStaticUnion { 43 43 T * data; … … 46 46 47 47 // dynamic generic type is otype 48 forall( otypeT)48 forall(T) 49 49 struct DynamicStruct { 50 50 T x; 51 51 }; 52 52 53 forall( otypeT)53 forall(T) 54 54 union DynamicUnion { 55 55 T x; … … 80 80 81 81 82 forall( otypeT)82 forall(T) 83 83 T identity(T x) { return x; } 84 84 -
tests/simpleGenericTriple.cfa
r342af53 r8e4aa05 14 14 // 15 15 16 forall( otypeT)16 forall(T) 17 17 struct T3 { 18 18 T f0, f1, f2; 19 19 }; 20 20 21 forall( otypeT | { T ?+?(T, T); })21 forall(T | { T ?+?(T, T); }) 22 22 T3(T) ?+?(T3(T) x, T3(T) y) { 23 23 T3(T) z = { x.f0+y.f0, x.f1+y.f1, x.f2+y.f2 }; -
tests/smart-pointers.cfa
r342af53 r8e4aa05 2 2 3 3 #include <memory.hfa> 4 #include < stdlib.hfa>4 #include <assert.h> 5 5 6 6 void counter_test(void) { … … 53 53 } 54 54 55 void declare_test(void) { 56 counter_ptr(int) ptr_i0 = 3; 57 counter_ptr(char) ptr_c0 = 'a'; 58 counter_ptr(float) ptr_f0 = 3.5f; 59 counter_ptr(double) ptr_d0 = 3.5; 60 61 unique_ptr(int) ptr_i1 = 3; 62 unique_ptr(char) ptr_c1 = 'a'; 63 unique_ptr(float) ptr_f1 = 3.5f; 64 unique_ptr(double) ptr_d1 = 3.5; 65 } 66 55 67 int main(int argc, char * argv[]) { 56 68 counter_test(); 57 69 unique_test(); 58 70 pointer_equality(); 71 72 printf("done\n"); 59 73 } -
tests/sum.cfa
r342af53 r8e4aa05 18 18 #include <stdlib.hfa> 19 19 20 trait sumable( otypeT ) {20 trait sumable( T ) { 21 21 void ?{}( T &, zero_t ); // 0 literal constructor 22 22 T ?+?( T, T ); // assortment of additions … … 26 26 }; // sumable 27 27 28 forall( otypeT | sumable( T ) ) // use trait28 forall( T | sumable( T ) ) // use trait 29 29 T sum( size_t size, T a[] ) { 30 30 T total = 0; // initialize by 0 constructor … … 107 107 | sum( size, (S *)a ) | ", check" | (S)s; 108 108 109 forall( otypeImpl | sumable( Impl ) )109 forall( Impl | sumable( Impl ) ) 110 110 struct GS { 111 111 Impl * x, * y; … … 194 194 sum( size, (S *)a ).[i, j], s.[i, j] ); 195 195 196 forall( otypeImpl | sumable( Impl ) )196 forall( Impl | sumable( Impl ) ) 197 197 struct GS { 198 198 Impl * x, * y; -
tests/tuple/tuplePolymorphism.cfa
r342af53 r8e4aa05 29 29 // ensure that f is a viable candidate for g, even though its parameter structure does not exactly match 30 30 [A] f([A, B] x, B y) { printf("%g %c %g %lld %c %lld %lld %c %lld\n", x.0.[x,y,z], x.1.[x,y,z], y.[x,y,z]); return x.0; } 31 forall( otype T, otypeU | { T f(T, U, U); })31 forall(T, U | { T f(T, U, U); }) 32 32 void g(T x, U y) { f(x, y, y); } 33 33 34 34 // add two triples 35 forall( otypeT | { T ?+?(T, T); })35 forall(T | { T ?+?(T, T); }) 36 36 [T, T, T] ?+?([T, T, T] x, [T, T, T] y) { 37 37 return [x.0+y.0, x.1+y.1, x.2+y.2]; … … 64 64 } 65 65 66 forall( otypeT)66 forall(T) 67 67 [T, T] foo([T, T] y) { 68 68 [T, T] x; -
tests/tuple/tupleVariadic.cfa
r342af53 r8e4aa05 19 19 printf("called func(void)\n"); 20 20 } 21 forall( otype T, ttype Params| { void process(T); void func(Params); })21 forall(T, Params... | { void process(T); void func(Params); }) 22 22 void func(T arg1, Params p) { 23 23 process(arg1); … … 92 92 } 93 93 94 forall( otypeT)94 forall(T) 95 95 T * copy(T x) { 96 96 // test calling new inside a polymorphic function … … 98 98 } 99 99 100 forall( ttype T| { void foo(T); }) void bar(T x) {}100 forall(T... | { void foo(T); }) void bar(T x) {} 101 101 void foo(int) {} 102 102 -
tests/zombies/ArrayN.c
r342af53 r8e4aa05 6 6 // } 7 7 8 forall( otypeindex_t)8 forall(index_t) 9 9 index_t offset_to_index(unsigned offset, index_t size) { 10 10 return [offset / size.0, offset % size.1]; -
tests/zombies/Members.c
r342af53 r8e4aa05 2 2 int ?=?( int*, int ); 3 3 float ?=?( float*, float ); 4 forall( dtype DT) DT * ?=?( DT**, DT* );5 forall( otypeT) lvalue T *?( T* );4 forall( DT & ) DT * ?=?( DT**, DT* ); 5 forall(T) lvalue T *?( T* ); 6 6 char *__builtin_memcpy(); 7 7 -
tests/zombies/Rank2.c
r342af53 r8e4aa05 1 1 int ?=?( int &, int ); 2 forall( dtype DT) DT * ?=?( DT *&, DT * );2 forall(DT &) DT * ?=?( DT *&, DT * ); 3 3 4 4 void a() { 5 forall( otypeT ) void f( T );6 void g( forall( otypeU ) void p( U ) );5 forall( T ) void f( T ); 6 void g( forall( U ) void p( U ) ); 7 7 g( f ); 8 8 } … … 10 10 void g() { 11 11 void h( int *null ); 12 forall( otypeT ) T id( T );12 forall( T ) T id( T ); 13 13 // forall( dtype T ) T *0; 14 14 // int 0; -
tests/zombies/abstype.c
r342af53 r8e4aa05 21 21 } 22 22 23 forall( otypeT ) T *?( T * );23 forall( T ) T *?( T * ); 24 24 int ?++( int * ); 25 25 int ?=?( int *, int ); 26 forall( dtype DT) DT * ?=?( DT **, DT * );26 forall( DT & ) DT * ?=?( DT **, DT * ); 27 27 28 28 otype U = int *; -
tests/zombies/context.cfa
r342af53 r8e4aa05 1 1 // trait declaration 2 2 3 trait has_q( otypeT ) {3 trait has_q( T ) { 4 4 T q( T ); 5 5 }; 6 6 7 forall( otypez | has_q( z ) ) void f() {8 trait has_r( otype T, otypeU ) {7 forall( z | has_q( z ) ) void f() { 8 trait has_r( T, U ) { 9 9 T r( T, T (T,U) ); 10 10 }; -
tests/zombies/gc_no_raii/bug-repro/blockers/explicit_cast.c
r342af53 r8e4aa05 9 9 }; 10 10 11 forall( otypeT)11 forall(T) 12 12 struct gcpointer 13 13 { … … 15 15 }; 16 16 17 forall( otypeT)17 forall(T) 18 18 static inline gcpointer(T) gcmalloc() 19 19 { -
tests/zombies/gc_no_raii/bug-repro/blockers/recursive_realloc.c
r342af53 r8e4aa05 3 3 #include <stdlib.hfa> 4 4 5 trait allocator_c( otype T, otypeallocator_t)5 trait allocator_c(T, allocator_t) 6 6 { 7 7 void realloc(allocator_t* const, size_t); 8 8 }; 9 9 10 forall( otypeT)10 forall(T) 11 11 struct heap_allocator 12 12 { … … 15 15 }; 16 16 17 forall( otypeT)17 forall(T) 18 18 inline void realloc(heap_allocator(T) *const this, size_t size) 19 19 { -
tests/zombies/gc_no_raii/bug-repro/deref.c
r342af53 r8e4aa05 1 forall( otypeT)1 forall(T) 2 2 struct wrap 3 3 { … … 5 5 }; 6 6 7 forall( otypeT)7 forall(T) 8 8 T *? (wrap(T) rhs) 9 9 { -
tests/zombies/gc_no_raii/bug-repro/field.c
r342af53 r8e4aa05 8 8 //------------------------------------------------------------------------------ 9 9 //Declaration 10 trait allocator_c( otype T, otypeallocator_t)10 trait allocator_c(T, allocator_t) 11 11 { 12 12 void ctor(allocator_t* const); … … 16 16 }; 17 17 18 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))18 forall(T, allocator_t | allocator_c(T, allocator_t)) 19 19 struct vector 20 20 { -
tests/zombies/gc_no_raii/bug-repro/malloc.c
r342af53 r8e4aa05 1 forall( otypeT)1 forall(T) 2 2 struct wrapper 3 3 { … … 5 5 }; 6 6 7 forall( otypeT)7 forall(T) 8 8 void ctor(wrapper(T)* this) 9 9 { … … 11 11 } 12 12 13 forall( otypeT)13 forall(T) 14 14 wrapper(T) gcmalloc() 15 15 { … … 19 19 } 20 20 21 forall( otypeT)21 forall(T) 22 22 wrapper(T)* ?=? (wrapper(T)* lhs, wrapper(T)* rhs) 23 23 { -
tests/zombies/gc_no_raii/bug-repro/oddtype.c
r342af53 r8e4aa05 1 forall( dtype T)1 forall(T &) 2 2 struct wrap { 3 3 int i; 4 4 }; 5 5 6 forall( otypeT) void ?{}(wrap(T)* this) {}7 forall( otypeT) void ?=?(wrap(T)* this) {}8 forall( otypeT) void ^?{}(wrap(T)* this) {}6 forall(T) void ?{}(wrap(T)* this) {} 7 forall(T) void ?=?(wrap(T)* this) {} 8 forall(T) void ^?{}(wrap(T)* this) {} 9 9 10 10 struct List_t { -
tests/zombies/gc_no_raii/bug-repro/push_back.h
r342af53 r8e4aa05 1 1 //------------------------------------------------------------------------------ 2 2 //Declaration 3 trait allocator_c( otype T, otypeallocator_t) {3 trait allocator_c(T, allocator_t) { 4 4 void ctor(allocator_t* const); 5 5 void dtor(allocator_t* const); … … 8 8 }; 9 9 10 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))10 forall(T, allocator_t | allocator_c(T, allocator_t)) 11 11 struct vector 12 12 { … … 17 17 //------------------------------------------------------------------------------ 18 18 //Initialization 19 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))19 forall(T, allocator_t | allocator_c(T, allocator_t)) 20 20 void vector_ctor(vector(T, allocator_t) *const this); 21 21 22 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))22 forall(T, allocator_t | allocator_c(T, allocator_t)) 23 23 void dtor(vector(T, allocator_t) *const this); 24 24 25 25 //------------------------------------------------------------------------------ 26 26 //Allocator 27 forall( otypeT)27 forall(T) 28 28 struct heap_allocator 29 29 { … … 32 32 }; 33 33 34 forall( otypeT)34 forall(T) 35 35 void ctor(heap_allocator(T) *const this); 36 36 37 forall( otypeT)37 forall(T) 38 38 void dtor(heap_allocator(T) *const this); 39 39 40 forall( otypeT)40 forall(T) 41 41 void realloc(heap_allocator(T) *const this, size_t size); 42 42 43 forall( otypeT)43 forall(T) 44 44 inline T* data(heap_allocator(T) *const this) 45 45 { … … 49 49 //------------------------------------------------------------------------------ 50 50 //Capacity 51 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))51 forall(T, allocator_t | allocator_c(T, allocator_t)) 52 52 inline bool empty(vector(T, allocator_t) *const this) 53 53 { … … 55 55 } 56 56 57 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))57 forall(T, allocator_t | allocator_c(T, allocator_t)) 58 58 inline bool size(vector(T, allocator_t) *const this) 59 59 { … … 61 61 } 62 62 63 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))63 forall(T, allocator_t | allocator_c(T, allocator_t)) 64 64 inline void reserve(vector(T, allocator_t) *const this, size_t size) 65 65 { … … 69 69 //------------------------------------------------------------------------------ 70 70 //Modifiers 71 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))71 forall(T, allocator_t | allocator_c(T, allocator_t)) 72 72 void push_back(vector(T, allocator_t) *const this, T value); -
tests/zombies/gc_no_raii/bug-repro/realloc.c
r342af53 r8e4aa05 1 1 void* realloc(void*, unsigned long int); 2 2 3 forall( otypeT)3 forall(T) 4 4 struct wrap 5 5 { … … 7 7 }; 8 8 9 forall( otypeT)9 forall(T) 10 10 static inline void realloc(wrap(T) *const this, unsigned long int size) 11 11 { -
tests/zombies/gc_no_raii/bug-repro/return.c
r342af53 r8e4aa05 1 forall( otypeT)1 forall(T) 2 2 struct wrapper 3 3 { … … 5 5 }; 6 6 7 forall( otypeT)7 forall(T) 8 8 wrapper(T) create() 9 9 { … … 12 12 } 13 13 14 forall( otypeT)14 forall(T) 15 15 wrapper(T)* ?=?(wrapper(T)* lhs, wrapper(T)* rhs) 16 16 { -
tests/zombies/gc_no_raii/bug-repro/return_template.c
r342af53 r8e4aa05 1 forall( otypeT)1 forall(T) 2 2 struct wrap 3 3 { … … 5 5 }; 6 6 7 forall( otypeT) void ?{}(wrap(T)* this);8 forall( otypeT) void ?{}(wrap(T)* this, wrap(T)* rhs);9 forall( otypeT) void ^?{}(wrap(T)* this);10 forall( otypeT) void ?=?(wrap(T)* this, wrap(T)* rhs);7 forall(T) void ?{}(wrap(T)* this); 8 forall(T) void ?{}(wrap(T)* this, wrap(T)* rhs); 9 forall(T) void ^?{}(wrap(T)* this); 10 forall(T) void ?=?(wrap(T)* this, wrap(T)* rhs); 11 11 12 forall( otypeT)12 forall(T) 13 13 wrap(T) test() 14 14 { -
tests/zombies/gc_no_raii/bug-repro/slow_malloc.c
r342af53 r8e4aa05 1 1 #include <stdlib.hfa> 2 2 3 forall( otypeT)3 forall(T) 4 4 struct heap_allocator 5 5 { -
tests/zombies/gc_no_raii/bug-repro/zero.c
r342af53 r8e4aa05 1 forall( otypeT)1 forall(T) 2 2 struct wrap 3 3 { … … 5 5 }; 6 6 7 forall( otypeT)7 forall(T) 8 8 int ?==? (wrap(T) lhs, wrap(T) rhs) 9 9 { … … 14 14 struct wrap(int) 0; 15 15 /*/ 16 forall( otypeT)16 forall(T) 17 17 struct wrap(T) 0; 18 18 //*/ -
tests/zombies/gc_no_raii/src/gc.h
r342af53 r8e4aa05 13 13 // } 14 14 15 forall( otypeT)15 forall(T) 16 16 static inline void gcmalloc(gcpointer(T)* ptr) 17 17 { -
tests/zombies/gc_no_raii/src/gcpointers.c
r342af53 r8e4aa05 113 113 #endif 114 114 115 forall( otypeT) void ?{}(gcpointer(T)* this) {115 forall(T) void ?{}(gcpointer(T)* this) { 116 116 (&this->internal) {}; 117 117 } 118 118 119 forall( otypeT) void ?{}(gcpointer(T)* this, void* address) {119 forall(T) void ?{}(gcpointer(T)* this, void* address) { 120 120 (&this->internal) { address }; 121 121 } 122 122 123 forall( otypeT) void ?{}(gcpointer(T)* this, gcpointer(T) other) {123 forall(T) void ?{}(gcpointer(T)* this, gcpointer(T) other) { 124 124 (&this->internal) { other.internal }; 125 125 } 126 126 127 forall( otypeT) void ^?{}(gcpointer(T)* this) {127 forall(T) void ^?{}(gcpointer(T)* this) { 128 128 ^?{}(&this->internal); 129 129 } 130 130 131 forall( otypeT) gcpointer(T) ?=?(gcpointer(T)* this, gcpointer(T) rhs) {131 forall(T) gcpointer(T) ?=?(gcpointer(T)* this, gcpointer(T) rhs) { 132 132 this->internal = rhs.internal; 133 133 return *this; … … 136 136 // forall(otype T) T *?(gcpointer(T) this); 137 137 138 forall( otypeT) T* get(gcpointer(T)* this) {138 forall(T) T* get(gcpointer(T)* this) { 139 139 return (T*)this->internal.ptr; 140 140 } 141 141 // 142 142 // //Logical operators 143 forall( otypeT) int ?!=?(gcpointer(T) this, int zero) {143 forall(T) int ?!=?(gcpointer(T) this, int zero) { 144 144 return this.internal.ptr != 0; 145 145 } -
tests/zombies/gc_no_raii/src/gcpointers.h
r342af53 r8e4aa05 4 4 #include <stdint.h> 5 5 6 forall( dtype T)6 forall(T &) 7 7 struct gcpointer; 8 8 … … 29 29 #endif 30 30 31 forall( dtype T)31 forall(T &) 32 32 struct gcpointer 33 33 { … … 36 36 37 37 // 38 forall( otypeT) void ?{}(gcpointer(T)* this);39 forall( otypeT) void ?{}(gcpointer(T)* this, void* address);40 forall( otypeT) void ?{}(gcpointer(T)* this, gcpointer(T) other);41 forall( otypeT) void ^?{}(gcpointer(T)* this);42 forall( otypeT) gcpointer(T) ?=?(gcpointer(T)* this, gcpointer(T) rhs);38 forall(T) void ?{}(gcpointer(T)* this); 39 forall(T) void ?{}(gcpointer(T)* this, void* address); 40 forall(T) void ?{}(gcpointer(T)* this, gcpointer(T) other); 41 forall(T) void ^?{}(gcpointer(T)* this); 42 forall(T) gcpointer(T) ?=?(gcpointer(T)* this, gcpointer(T) rhs); 43 43 44 44 45 45 // forall(otype T) T *?(gcpointer(T) this); 46 forall( otypeT) T* get(gcpointer(T)* this);46 forall(T) T* get(gcpointer(T)* this); 47 47 48 48 //Logical operators 49 forall( otypeT) int ?!=?(gcpointer(T) this, int zero);50 forall( otypeT) int ?!=?(gcpointer(T) this, gcpointer(T) rhs);51 forall( otypeT) int ?==?(gcpointer(T) this, gcpointer(T) rhs);49 forall(T) int ?!=?(gcpointer(T) this, int zero); 50 forall(T) int ?!=?(gcpointer(T) this, gcpointer(T) rhs); 51 forall(T) int ?==?(gcpointer(T) this, gcpointer(T) rhs); -
tests/zombies/gc_no_raii/src/tools.h
r342af53 r8e4aa05 12 12 // } 13 13 14 trait has_equal( otypeT)14 trait has_equal(T) 15 15 { 16 16 signed int ?==?(T a, T b); 17 17 }; 18 18 19 trait InputIterator_t( otype T, otypeInputIterator)19 trait InputIterator_t(T, InputIterator) 20 20 { 21 21 signed int ?==?(InputIterator a, InputIterator b); … … 26 26 }; 27 27 28 forall( otype T | has_equal(T), otypeInputIterator | InputIterator_t(T, InputIterator))28 forall(T | has_equal(T), InputIterator | InputIterator_t(T, InputIterator)) 29 29 static inline InputIterator find( InputIterator first, const InputIterator* const last, T val) 30 30 { -
tests/zombies/hashtable.cfa
r342af53 r8e4aa05 14 14 15 15 16 trait has_hash( otypeK ) {16 trait has_hash( K ) { 17 17 size_t hash(K); 18 18 int ?==?( K, K ); 19 19 }; 20 20 21 trait hkey( otype K, dtype tN| has_hash(K) ) {21 trait hkey( K, tN & | has_hash(K) ) { 22 22 K key(tN &); 23 23 }; 24 24 25 forall( otype K, dtype tN, dtype tE| $dlistable(tN, tE) | hkey(K, tN) ) {25 forall( K, tN &, tE & | $dlistable(tN, tE) | hkey(K, tN) ) { 26 26 27 27 struct hashtable { … … 39 39 } 40 40 41 forall( otype K, dtype tN, dtype tE| $dlistable(tN, tE) | hkey(K, tN) | { void defaultResumptionHandler(ht_fill_limit_crossed &); } ) {41 forall( K, tN &, tE & | $dlistable(tN, tE) | hkey(K, tN) | { void defaultResumptionHandler(ht_fill_limit_crossed &); } ) { 42 42 43 43 void ?{}( hashtable(K, tN, tE) & this, size_t n_buckets, dlist(tN, tE) *buckets ) { … … 57 57 } 58 58 59 forall( otype K, dtype tN, dtype tE| $dlistable(tN, tE) | hkey(K, tN) ) {59 forall( K, tN &, tE & | $dlistable(tN, tE) | hkey(K, tN) ) { 60 60 61 61 float fill_frac( hashtable(K, tN, tE) & this ) with(this) { … … 124 124 125 125 126 trait heaped( dtype T) {126 trait heaped(T &) { 127 127 T * alloc( size_t ); 128 128 void free( void * ); … … 133 133 } 134 134 135 forall( otype K, dtype tN, dtype tE| $dlistable(tN, tE) | hkey(K, tN) | heaped( dlist(tN, tE) ) ) {135 forall( K, tN &, tE & | $dlistable(tN, tE) | hkey(K, tN) | heaped( dlist(tN, tE) ) ) { 136 136 137 137 struct hashtable_dynamic { -
tests/zombies/hashtable2.cfa
r342af53 r8e4aa05 69 69 70 70 71 trait pretendsToMatter( dtype TTT) {71 trait pretendsToMatter( TTT & ) { 72 72 void actsmart(TTT &); 73 73 }; 74 74 75 forall( dtype TTTx)75 forall( TTTx & ) 76 76 void actsmart(TTTx &) {} 77 77 … … 86 86 // 2. shows up in -CFA output as hashtable_rbs(), which is bad C; expecting hashtable_rbs* 87 87 88 forall( otypeTt_unused | pretendsToMatter(Tt_unused) ) {88 forall( Tt_unused | pretendsToMatter(Tt_unused) ) { 89 89 90 90 // hashtable of request by source … … 104 104 } 105 105 106 forall( otypeTt_unused | pretendsToMatter(Tt_unused) | { void defaultResumptionHandler(ht_fill_limit_crossed &); } ) {106 forall( Tt_unused | pretendsToMatter(Tt_unused) | { void defaultResumptionHandler(ht_fill_limit_crossed &); } ) { 107 107 108 108 void ?{}( hashtable_rbs(Tt_unused) & this, size_t n_buckets, dlist(request_in_ht_by_src, request) *buckets, … … 135 135 void defaultResumptionHandler( ht_auto_resize_pending & ex ); 136 136 137 forall( otypeTt_unused | pretendsToMatter(Tt_unused) ) {137 forall( Tt_unused | pretendsToMatter(Tt_unused) ) { 138 138 139 139 float fill_frac( hashtable_rbs(Tt_unused) & this ) with(this) { … … 221 221 222 222 223 trait heaped( dtype T) {223 trait heaped(T &) { 224 224 T * alloc( size_t ); 225 225 void free( void * ); … … 228 228 void __dynamic_defaultResumptionHandler(ht_fill_limit_crossed &); 229 229 230 forall( otypeTt_unused ) {230 forall( Tt_unused ) { 231 231 232 232 struct hashtable_rbs_dynamic { … … 263 263 264 264 265 forall( otypeTt_unused | heaped( dlist(request_in_ht_by_src, request) ) ) {265 forall( Tt_unused | heaped( dlist(request_in_ht_by_src, request) ) ) { 266 266 267 267 void ?{}( hashtable_rbs_dynamic(Tt_unused).resize_policy & this, size_t nbuckets_floor ) { … … 325 325 } 326 326 327 forall( otypeTt_unused ) {327 forall( Tt_unused ) { 328 328 void rehashToLarger_STEP( hashtable_rbs_dynamic(Tt_unused) & this, size_t new_n_buckets ) with (this) { 329 329 rehashToLarger( this, new_n_buckets ); -
tests/zombies/huge.c
r342af53 r8e4aa05 14 14 // 15 15 16 int huge( int n, forall( otypeT ) T (*f)( T ) ) {16 int huge( int n, forall( T ) T (*f)( T ) ) { 17 17 if ( n <= 0 ) 18 18 return f( 0 ); -
tests/zombies/it_out.c
r342af53 r8e4aa05 16 16 typedef unsigned long streamsize_type; 17 17 18 trait ostream( dtype os_type) {18 trait ostream( os_type & ) { 19 19 os_type *write( os_type *, const char *, streamsize_type ); 20 20 int fail( os_type * ); 21 21 }; 22 22 23 trait writeable( otypeT ) {24 forall( dtype os_type| ostream( os_type ) ) os_type * ?<<?( os_type *, T );23 trait writeable( T ) { 24 forall( os_type & | ostream( os_type ) ) os_type * ?<<?( os_type *, T ); 25 25 }; 26 26 27 forall( dtype os_type| ostream( os_type ) ) os_type * ?<<?( os_type *, char );28 forall( dtype os_type| ostream( os_type ) ) os_type * ?<<?( os_type *, int );29 forall( dtype os_type| ostream( os_type ) ) os_type * ?<<?( os_type *, const char * );27 forall( os_type & | ostream( os_type ) ) os_type * ?<<?( os_type *, char ); 28 forall( os_type & | ostream( os_type ) ) os_type * ?<<?( os_type *, int ); 29 forall( os_type & | ostream( os_type ) ) os_type * ?<<?( os_type *, const char * ); 30 30 31 trait istream( dtype is_type) {31 trait istream( is_type & ) { 32 32 is_type *read( is_type *, char *, streamsize_type ); 33 33 is_type *unread( is_type *, char ); … … 36 36 }; 37 37 38 trait readable( otypeT ) {39 forall( dtype is_type| istream( is_type ) ) is_type * ?<<?( is_type *, T );38 trait readable( T ) { 39 forall( is_type & | istream( is_type ) ) is_type * ?<<?( is_type *, T ); 40 40 }; 41 41 42 forall( dtype is_type| istream( is_type ) ) is_type * ?>>?( is_type *, char* );43 forall( dtype is_type| istream( is_type ) ) is_type * ?>>?( is_type *, int* );42 forall( is_type & | istream( is_type ) ) is_type * ?>>?( is_type *, char* ); 43 forall( is_type & | istream( is_type ) ) is_type * ?>>?( is_type *, int* ); 44 44 45 trait iterator( otype iterator_type, otypeelt_type ) {45 trait iterator( iterator_type, elt_type ) { 46 46 iterator_type ?++( iterator_type* ); 47 47 iterator_type ++?( iterator_type* ); … … 52 52 }; 53 53 54 forall( otypeelt_type | writeable( elt_type ),55 otypeiterator_type | iterator( iterator_type, elt_type ),56 dtype os_type| ostream( os_type ) )54 forall( elt_type | writeable( elt_type ), 55 iterator_type | iterator( iterator_type, elt_type ), 56 os_type & | ostream( os_type ) ) 57 57 void write_all( iterator_type begin, iterator_type end, os_type *os ); 58 58 59 forall( otypeelt_type | writeable( elt_type ),60 otypeiterator_type | iterator( iterator_type, elt_type ),61 dtype os_type| ostream( os_type ) )59 forall( elt_type | writeable( elt_type ), 60 iterator_type | iterator( iterator_type, elt_type ), 61 os_type & | ostream( os_type ) ) 62 62 void write_all( elt_type begin, iterator_type end, os_type *os ) { 63 63 os << begin; -
tests/zombies/new.c
r342af53 r8e4aa05 14 14 // 15 15 16 forall( otypeT )16 forall( T ) 17 17 void f( T *t ) { 18 18 t--; -
tests/zombies/occursError.cfa
r342af53 r8e4aa05 1 forall( otypeT ) void f( void (*)( T, T * ) );2 forall( otypeU ) void g( U, U * );3 forall( otypeU ) void h( U *, U );1 forall( T ) void f( void (*)( T, T * ) ); 2 forall( U ) void g( U, U * ); 3 forall( U ) void h( U *, U ); 4 4 5 5 void test() { -
tests/zombies/prolog.c
r342af53 r8e4aa05 25 25 void is_integer( int x ) {} 26 26 27 trait ArithmeticType( otypeT ) {27 trait ArithmeticType( T ) { 28 28 void is_arithmetic( T ); 29 29 }; 30 30 31 trait IntegralType( otypeT | ArithmeticType( T ) ) {31 trait IntegralType( T | ArithmeticType( T ) ) { 32 32 void is_integer( T ); 33 33 }; 34 34 35 forall( otypeT | IntegralType( T ) | { void printResult( T ); } )35 forall( T | IntegralType( T ) | { void printResult( T ); } ) 36 36 void hornclause( T param ) { 37 37 printResult( param ); -
tests/zombies/quad.c
r342af53 r8e4aa05 16 16 #include <fstream.hfa> 17 17 18 forall( otypeT | { T ?*?( T, T ); } )18 forall( T | { T ?*?( T, T ); } ) 19 19 T square( T t ) { 20 20 return t * t; 21 21 } 22 22 23 forall( otypeU | { U square( U ); } )23 forall( U | { U square( U ); } ) 24 24 U quad( U u ) { 25 25 return square( square( u ) ); -
tests/zombies/scope.cfa
r342af53 r8e4aa05 20 20 y p; 21 21 22 trait has_u( otypez ) {22 trait has_u( z ) { 23 23 z u(z); 24 24 }; 25 25 26 forall( otypet | has_u( t ) )26 forall( t | has_u( t ) ) 27 27 y q( t the_t ) { 28 28 t y = u( the_t ); -
tests/zombies/simplePoly.c
r342af53 r8e4aa05 14 14 // 15 15 16 forall( otype T, otypeU | { T f( T, U ); } )16 forall( T, U | { T f( T, U ); } ) 17 17 T q( T t, U u ) { 18 18 return f( t, u ); -
tests/zombies/simpler.c
r342af53 r8e4aa05 14 14 // 15 15 16 forall( otypeT ) T id( T, T );16 forall( T ) T id( T, T ); 17 17 18 18 int main() { -
tests/zombies/specialize.c
r342af53 r8e4aa05 39 39 } 40 40 41 forall( otypeT ) T f( T t )41 forall( T ) T f( T t ) 42 42 { 43 43 printf( "in f; sizeof T is %d\n", sizeof( T ) ); -
tests/zombies/square.c
r342af53 r8e4aa05 16 16 #include <fstream.hfa> 17 17 18 forall( otypeT | { T ?*?( T, T ); } )18 forall( T | { T ?*?( T, T ); } ) 19 19 T square( T t ) { 20 20 return t * t; -
tests/zombies/structMember.cfa
r342af53 r8e4aa05 66 66 S.T; 67 67 .S.T; 68 forall( otype S, otypeT ) struct W {68 forall( S, T ) struct W { 69 69 struct X {}; 70 70 }; -
tests/zombies/subrange.cfa
r342af53 r8e4aa05 1 1 // A small context defining the notion of an ordered otype. (The standard 2 2 // library should probably contain a context for this purpose.) 3 trait ordered( otypeT) {3 trait ordered(T) { 4 4 int ?<?(T, T), ?<=?(T, T); 5 5 }; … … 7 7 // A subrange otype resembling an Ada subotype with a base otype and a range 8 8 // constraint. 9 otype subrange( otypebase_t | ordered(base_t), base_t low = 0, base_t high = 8) = base_t;9 otype subrange(base_t | ordered(base_t), base_t low = 0, base_t high = 8) = base_t; 10 10 11 11 // Note that subrange() can be applied to floating-point and pointer otypes, not … … 28 28 29 29 // Convenient access to subrange bounds, for instance for iteration: 30 forall ( otypeT, T low, T high)30 forall (T, T low, T high) 31 31 T lbound( subrange(T, low, high) v) { 32 32 return low; 33 33 } 34 34 35 forall ( otypeT, T low, T high)35 forall (T, T low, T high) 36 36 T hbound( subrange(T, low, high) v) { 37 37 return high; … … 44 44 // of exception handling here. Inlining allows the compiler to eliminate 45 45 // bounds checks. 46 forall ( otypeT | ordered(T), T low, T high)46 forall (T | ordered(T), T low, T high) 47 47 inline subrange(T, low, high) ?=?(subrange(T, low, high)* target, T source) { 48 48 if (low <= source && source <= high) *((T*)target) = source; … … 54 54 // compares range bounds so that the compiler can optimize checks away when the 55 55 // ranges are known to overlap. 56 forall ( otypeT | ordered(T), T t_low, T t_high, T s_low, T s_high)56 forall (T | ordered(T), T t_low, T t_high, T s_low, T s_high) 57 57 inline subrange(T, t_low, t_high) ?=?(subrange(T, t_low, t_high)* target, 58 58 subrange(T, s_low, s_high) source) { -
tests/zombies/twice.c
r342af53 r8e4aa05 16 16 #include <fstream.hfa> 17 17 18 forall( otypeT | { T ?+?( T, T ); } )18 forall( T | { T ?+?( T, T ); } ) 19 19 T twice( const T t ) { 20 20 return t + t; -
tests/zombies/typeGenerator.cfa
r342af53 r8e4aa05 1 context addable( otypeT ) {1 context addable( T ) { 2 2 T ?+?( T,T ); 3 3 T ?=?( T*, T); 4 4 }; 5 5 6 otype List1( otypeT | addable( T ) ) = struct { T data; List1( T ) *next; } *;6 otype List1( T | addable( T ) ) = struct { T data; List1( T ) *next; } *; 7 7 typedef List1( int ) ListOfIntegers; 8 8 //List1( int ) li; … … 11 11 [int] h( * List1( int ) p ); // new declaration syntax 12 12 13 struct( otypeT ) S2 { T i; }; // actual definition13 struct( T ) S2 { T i; }; // actual definition 14 14 struct( int ) S3 v1, *p; // expansion and instantiation 15 struct( otypeT )( int ) S24 { T i; } v2; // actual definition, expansion and instantiation16 struct( otypeT )( int ) { T i; } v2; // anonymous actual definition, expansion and instantiation15 struct( T )( int ) S24 { T i; } v2; // actual definition, expansion and instantiation 16 struct( T )( int ) { T i; } v2; // anonymous actual definition, expansion and instantiation 17 17 18 struct( otypeT | addable( T ) ) node { T data; struct( T ) node *next; };19 otype List( otypeT ) = struct( T ) node *;18 struct( T | addable( T ) ) node { T data; struct( T ) node *next; }; 19 otype List( T ) = struct( T ) node *; 20 20 List( int ) my_list; 21 21 -
tests/zombies/withStatement.cfa
r342af53 r8e4aa05 54 54 } 55 55 56 forall( otypeT )56 forall( T ) 57 57 struct Box { 58 58 T x; 59 59 }; 60 60 61 forall( otypeT )61 forall( T ) 62 62 void ?{}( Box(T) & this ) with( this ) { // with clause in polymorphic function 63 63 x{}; … … 66 66 void print( int i ) { sout | i; } 67 67 68 forall( otypeT | { void print( T ); })68 forall( T | { void print( T ); }) 69 69 void foo( T t ) { 70 70 Box( T ) b = { t }; -
tests/zombies/wrapper/src/pointer.h
r342af53 r8e4aa05 8 8 // type safe malloc / free 9 9 10 forall( otypeT)10 forall(T) 11 11 T* new() 12 12 { … … 16 16 } 17 17 18 forall( otypeT)18 forall(T) 19 19 void delete(T* p) 20 20 { -
tools/prettyprinter/Makefile.am
r342af53 r8e4aa05 11 11 ## Created On : Wed Jun 28 12:07:10 2017 12 12 ## Last Modified By : Peter A. Buhr 13 ## Last Modified On : Mon Apr 16 09:43:23 201814 ## Update Count : 2 013 ## Last Modified On : Thu Jan 28 08:48:22 2021 14 ## Update Count : 23 15 15 ############################################################################### 16 16 … … 20 20 BUILT_SOURCES = parser.hh 21 21 22 AM_YFLAGS = -d -t -v 22 AM_YFLAGS = -d -t -v -Wno-yacc 23 23 24 24 SRC = lex.ll \ … … 34 34 pretty_CXXFLAGS = -Wno-deprecated -Wall -DYY_NO_INPUT -O2 -g -std=c++14 35 35 36 M AINTAINERCLEANFILES = parser.output36 MOSTLYCLEANFILES = parser.output -
tools/prettyprinter/ParserTypes.h
r342af53 r8e4aa05 13 13 // Created On : Sun Dec 16 15:00:49 2001 14 14 // Last Modified By : Peter A. Buhr 15 // Last Modified On : Sat Jul 22 10:13:09 201716 // Update Count : 17 515 // Last Modified On : Tue Jan 26 23:05:34 2021 16 // Update Count : 176 17 17 // 18 18 19 19 #pragma once 20 20 21 int yylex();21 extern "C" int yylex(); 22 22 23 23 #include <string> -
tools/prettyprinter/parser.yy
r342af53 r8e4aa05 10 10 // Created On : Sat Dec 15 13:44:21 2001 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Apr 15 21:40:30 201813 // Update Count : 105 212 // Last Modified On : Tue Jan 26 22:50:03 2021 13 // Update Count : 1053 14 14 // 15 15 … … 17 17 #define YYDEBUG_LEXER_TEXT( yylval ) // lexer loads this up each time 18 18 #define YYDEBUG 1 // get the pretty debugging code to compile 19 #define YYERROR_VERBOSE // more information in syntax errors 19 20 20 21 #include <iostream>
Note:
See TracChangeset
for help on using the changeset viewer.